├── deps └── JRedis.jar ├── src ├── main │ └── java │ │ ├── com │ │ └── tinkerpop │ │ │ ├── .DS_Store │ │ │ └── blueprints │ │ │ └── pgm │ │ │ ├── .DS_Store │ │ │ └── impls │ │ │ ├── .DS_Store │ │ │ └── blueredis │ │ │ ├── iterators │ │ │ ├── RedisEdgeIterator.java │ │ │ ├── RedisVertexIterator.java │ │ │ ├── RedisVertexIterable.java │ │ │ ├── RedisEdgeIterable.java │ │ │ ├── RedisElementType.java │ │ │ ├── RedisElementIterable.java │ │ │ └── RedisElementIterator.java │ │ │ ├── index │ │ │ ├── RedisIndexKeys.java │ │ │ ├── RedisAutomaticIndex.java │ │ │ ├── package-info.java │ │ │ ├── RedisIndex.java │ │ │ └── RedisIndexManager.java │ │ │ ├── RedisVertex.java │ │ │ ├── RedisEdge.java │ │ │ ├── RedisElement.java │ │ │ ├── RedisGraph.java │ │ │ └── utils │ │ │ └── Base64Coder.java │ │ └── biz │ │ └── source_code │ │ └── base64Coder │ │ └── Base64Coder.java └── assembly │ ├── standalone.xml │ └── distribution.xml ├── CHANGELOG ├── target └── test-classes │ └── com │ └── tinkerpop │ └── blueprints │ └── pgm │ └── parser │ └── graph-example-1.xml ├── README.markdown └── pom.xml /deps/JRedis.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmitriid/blueredis/HEAD/deps/JRedis.jar -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmitriid/blueredis/HEAD/src/main/java/com/tinkerpop/.DS_Store -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmitriid/blueredis/HEAD/src/main/java/com/tinkerpop/blueprints/pgm/.DS_Store -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmitriid/blueredis/HEAD/src/main/java/com/tinkerpop/blueprints/pgm/impls/.DS_Store -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 2010-10-21 Dmitrii 'Mamut' Dimandt 2 | 3 | * BREAKING CHANGE 4 | getVertices()/getEdges() no longer return an ArrayList, but an iterable 5 | as a result in/out edges on a vertex are now stored as zsets which breaks 6 | backward compatibility 7 | 8 | * iterable returned from getVertices()/getEdges() has a count() method to quickly 9 | 10 | 2010-24-09 Dmitrii 'Mamut' Dimandt 11 | 12 | * missing dependency for serializable properties 13 | * fixed RedisGraph.setIndexing not setting the indexing flag 14 | 15 | 16 | 2010-18-09 Dmitrii 'Mamut' Dimandt 17 | 18 | + Version bump to 0.2 19 | + Added RedisIndex. Implements Blueprints' Index interface. 20 | RedisIndex indexes both edges and vertices. 21 | + Added RedisGraph.setIndexing to turn indexing on/off or specify own indexing 22 | service 23 | + Added CHANGELOG 24 | 25 | * Changed various methods throughout RedisElement to call index/remove from 26 | index 27 | * Updated README 28 | -------------------------------------------------------------------------------- /src/assembly/standalone.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | standalone 19 | 20 | jar 21 | 22 | false 23 | 24 | 25 | 26 | target/classes 27 | / 28 | 29 | 30 | 31 | 32 | 33 | / 34 | true 35 | runtime 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/iterators/RedisEdgeIterator.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.iterators; 18 | 19 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisElement; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 21 | 22 | public class RedisEdgeIterator extends RedisElementIterator { 23 | public RedisEdgeIterator(final RedisEdgeIterable iterable){ 24 | super(RedisElementType.TYPE.REDIS_ELEMENT_EDGE, iterable); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/iterators/RedisVertexIterator.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.iterators; 18 | 19 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisElement; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 21 | 22 | public class RedisVertexIterator extends RedisElementIterator { 23 | public RedisVertexIterator(final RedisElementType.TYPE type, final RedisVertexIterable iterable) { 24 | super(RedisElementType.TYPE.REDIS_ELEMENT_VERTEX, iterable); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/index/RedisIndexKeys.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.index; 18 | 19 | public class RedisIndexKeys { 20 | public static final String AUTO = "index:auto:"; 21 | public static final String MANUAL = "index:manual:"; 22 | 23 | public static final String META_AUTO = "index:meta:auto:"; 24 | public static final String META_MANUAL = "index:meta:manual:"; 25 | 26 | public static final String META_INDICES_AUTO = "index:meta:indices:auto"; 27 | public static final String META_INDICES_MANUAL = "index:meta:indices:manual"; 28 | } 29 | -------------------------------------------------------------------------------- /src/assembly/distribution.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | zip 20 | 21 | 22 | 23 | 24 | pom.xml 25 | 26 | 27 | 36 | 37 | src 38 | 39 | 40 | data 41 | 42 | 43 | target/apidocs 44 | 45 | 46 | target/site 47 | 48 | 49 | target 50 | 51 | neo-*.jar 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /target/test-classes/com/tinkerpop/blueprints/pgm/parser/graph-example-1.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | marko 13 | 29 14 | 15 | 16 | vadas 17 | 27 18 | 19 | 20 | lop 21 | java 22 | 23 | 24 | josh 25 | 32 26 | 27 | 28 | ripple 29 | java 30 | 31 | 32 | peter 33 | 35 34 | 35 | 36 | 0.5 37 | 38 | 39 | 1.0 40 | 41 | 42 | 0.4 43 | 44 | 45 | 1.0 46 | 47 | 48 | 0.4 49 | 50 | 51 | 0.2 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/iterators/RedisVertexIterable.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.iterators; 18 | 19 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisElement; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 21 | import com.tinkerpop.blueprints.pgm.Vertex; 22 | 23 | import java.util.Iterator; 24 | 25 | public class RedisVertexIterable extends RedisElementIterable implements Iterable{ 26 | public RedisVertexIterable(final RedisGraph graph) { 27 | this(RedisElementType.TYPE.REDIS_ELEMENT_VERTEX, graph, null); 28 | } 29 | 30 | public RedisVertexIterable(final RedisGraph graph, final RedisElement element) { 31 | this(RedisElementType.TYPE.REDIS_ELEMENT_VERTEX, graph, element); 32 | } 33 | 34 | public RedisVertexIterable(RedisElementType.TYPE type, final RedisGraph graph, final RedisElement element) { 35 | super(type, graph, element); 36 | } 37 | 38 | @Override 39 | public Iterator iterator() { 40 | return new RedisVertexIterator(type, this); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/iterators/RedisEdgeIterable.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.iterators; 18 | 19 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisElement; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 21 | import com.tinkerpop.blueprints.pgm.Edge; 22 | 23 | import java.util.Iterator; 24 | 25 | public class RedisEdgeIterable extends RedisElementIterable implements Iterable{ 26 | 27 | private String label = null; 28 | 29 | public RedisEdgeIterable(final RedisGraph graph) { 30 | this(RedisElementType.TYPE.REDIS_ELEMENT_EDGE, graph, null); 31 | } 32 | 33 | public RedisEdgeIterable(final RedisElementType.TYPE type, final RedisGraph graph) { 34 | this(type, graph, null); 35 | } 36 | 37 | public RedisEdgeIterable(final RedisElementType.TYPE type, final RedisGraph graph, final RedisElement element) { 38 | super(type, graph, element); 39 | } 40 | 41 | public RedisEdgeIterable(final RedisElementType.TYPE type, final RedisGraph graph, final RedisElement element, String label) { 42 | super(type, graph, element, label); 43 | this.label = label; 44 | } 45 | 46 | @Override 47 | public Iterator iterator() { 48 | return new RedisEdgeIterator(this); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/iterators/RedisElementType.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.iterators; 18 | 19 | import com.tinkerpop.blueprints.pgm.Element; 20 | import com.tinkerpop.blueprints.pgm.Vertex; 21 | 22 | public class RedisElementType { 23 | public static enum TYPE { 24 | REDIS_ELEMENT_VERTEX, 25 | REDIS_ELEMENT_EDGE, 26 | REDIS_ELEMENT_EDGES_IN, 27 | REDIS_ELEMENT_EDGES_OUT 28 | //REDIS_ELEMENT_VERTEX_IN, only one in/out vertex per edge 29 | //REDIS_ELEMENT_VERTEX_OUT no need for an iterator 30 | } 31 | 32 | public static String key(TYPE type, Element element){ 33 | if(null == element){ 34 | if(type.equals(TYPE.REDIS_ELEMENT_VERTEX)) return "globals:vertices"; 35 | if(type.equals(TYPE.REDIS_ELEMENT_EDGE)) return "globals:edges"; 36 | } else { 37 | if(element instanceof Vertex){ 38 | if(type.equals(TYPE.REDIS_ELEMENT_EDGES_IN)) 39 | return "vertex:" + element.getId().toString() + ":edges:in"; 40 | if(type.equals(TYPE.REDIS_ELEMENT_EDGES_OUT)) 41 | return "vertex:" + element.getId().toString() + ":edges:out"; 42 | } 43 | /* 44 | else { 45 | if(type.equals(TYPE.REDIS_ELEMENT_VERTEX_IN)) 46 | return "edge:".concat(element.getId().toString()).concat(":in"); 47 | if(type.equals(TYPE.REDIS_ELEMENT_VERTEX_OUT)) 48 | return "edge:".concat(element.getId().toString()).concat(":out"); 49 | } */ 50 | } 51 | return ""; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/index/RedisAutomaticIndex.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.index; 18 | 19 | import com.tinkerpop.blueprints.pgm.AutomaticIndex; 20 | import com.tinkerpop.blueprints.pgm.Element; 21 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisElement; 22 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 23 | 24 | import java.text.Normalizer; 25 | import java.util.HashSet; 26 | import java.util.Set; 27 | 28 | /** 29 | * @see RedisIndex 30 | * @see RedisIndexManager 31 | */ 32 | 33 | public class RedisAutomaticIndex extends RedisIndex implements AutomaticIndex { 34 | 35 | public RedisAutomaticIndex(RedisGraph graph, String indexName, Class indexClass, Set keys) { 36 | super(graph, indexName, indexClass); 37 | if(null == keys){ 38 | this.indexKeys = new HashSet(); 39 | } else { 40 | this.indexKeys = new HashSet(keys); 41 | this.indexAll = false; 42 | } 43 | 44 | // convert any index name to a form that can be sent to redis, i.e. 45 | // convert "Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ" to "this_is_a_funky_string" 46 | this.indexName = RedisIndex.normalizeName(indexName); 47 | this.nodeName = RedisIndexKeys.AUTO + this.indexName + ":"; 48 | this.indexType = Type.AUTOMATIC; 49 | } 50 | 51 | public RedisAutomaticIndex(RedisGraph graph, final String indexName, final Class indexClass) { 52 | this(graph, indexName, indexClass, new HashSet()); 53 | } 54 | 55 | @Override 56 | public Set getAutoIndexKeys() { 57 | return this.indexKeys; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/RedisVertex.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis; 18 | 19 | import com.tinkerpop.blueprints.pgm.impls.blueredis.iterators.RedisEdgeIterable; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.iterators.RedisElementType; 21 | import com.tinkerpop.blueprints.pgm.Edge; 22 | import com.tinkerpop.blueprints.pgm.Vertex; 23 | import org.jredis.RedisException; 24 | 25 | public class RedisVertex extends RedisElement implements Vertex { 26 | 27 | public RedisVertex(RedisGraph db) { 28 | super(db, db.nextVertexId()); 29 | 30 | try { 31 | this.graph.getDatabase().zadd("globals:vertices", id, String.valueOf(id)); 32 | this.graph.getDatabase().set("vertex:" + String.valueOf(id), String.valueOf(id)); 33 | } catch(RedisException e) { 34 | e.printStackTrace(); 35 | } 36 | 37 | } 38 | 39 | public RedisVertex(Long id, RedisGraph db) { 40 | super(db, id); 41 | } 42 | 43 | @Override 44 | public Iterable getOutEdges() { 45 | return new RedisEdgeIterable(RedisElementType.TYPE.REDIS_ELEMENT_EDGES_OUT, graph, this); 46 | } 47 | 48 | @Override 49 | public Iterable getOutEdges(final String label) { 50 | return new RedisEdgeIterable(RedisElementType.TYPE.REDIS_ELEMENT_EDGES_OUT, graph, this, label); 51 | } 52 | 53 | @Override 54 | public Iterable getInEdges() { 55 | return new RedisEdgeIterable(RedisElementType.TYPE.REDIS_ELEMENT_EDGES_IN, graph, this); 56 | } 57 | 58 | @Override 59 | public Iterable getInEdges(final String label) { 60 | return new RedisEdgeIterable(RedisElementType.TYPE.REDIS_ELEMENT_EDGES_IN, graph, this, label); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/iterators/RedisElementIterable.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.iterators; 18 | 19 | import com.tinkerpop.blueprints.pgm.Edge; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisEdge; 21 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisElement; 22 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 23 | import org.jredis.JRedis; 24 | import org.jredis.RedisException; 25 | 26 | public class RedisElementIterable { 27 | 28 | protected RedisElementType.TYPE type; 29 | protected RedisGraph graph; 30 | private JRedis db; 31 | protected long count = 0, elementCount = 0; 32 | protected RedisElement element; 33 | protected String elementCollectionKey; 34 | protected String label = null; 35 | 36 | public RedisElementIterable(RedisElementType.TYPE type, RedisGraph graph, RedisElement element) { 37 | this.type = type; 38 | this.graph = graph; 39 | this.db = graph.getDatabase(); 40 | this.element = element; 41 | 42 | elementCollectionKey = RedisElementType.key(type, element); 43 | 44 | try { 45 | this.count = this.elementCount = this.db.zcard(elementCollectionKey); 46 | } catch(RedisException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | 51 | public RedisElementIterable(RedisElementType.TYPE type, RedisGraph graph, RedisElement element, final String label) { 52 | this(type, graph, element); 53 | this.label = label; 54 | 55 | if(null != this.label){ 56 | RedisEdgeIterable itr = new RedisEdgeIterable(type, graph, element); 57 | 58 | this.count = 0; 59 | for(Edge e : itr){ 60 | if(e.getLabel().equals(this.label)){ 61 | this.count++; 62 | } 63 | } 64 | } 65 | } 66 | public long count(){ 67 | return count; 68 | } 69 | 70 | 71 | /* used in iterators */ 72 | 73 | public RedisGraph getGraph() { 74 | return graph; 75 | } 76 | 77 | public long getCount() { 78 | return count; 79 | } 80 | 81 | public long getElementCount() { 82 | return elementCount; 83 | } 84 | 85 | public String getLabel() { 86 | return label; 87 | } 88 | 89 | public RedisElement getElement() { 90 | return element; 91 | } 92 | 93 | public String getElementCollectionKey() { 94 | return elementCollectionKey; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/RedisEdge.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis; 18 | 19 | import com.tinkerpop.blueprints.pgm.AutomaticIndex; 20 | import com.tinkerpop.blueprints.pgm.Edge; 21 | import com.tinkerpop.blueprints.pgm.Vertex; 22 | import com.tinkerpop.blueprints.pgm.impls.blueredis.index.RedisAutomaticIndex; 23 | import org.jredis.RedisException; 24 | 25 | public class RedisEdge extends RedisElement implements Edge { 26 | 27 | private String label = null; 28 | private RedisVertex in, out; 29 | 30 | public RedisEdge(RedisVertex in, RedisVertex out, String label, RedisGraph db) { 31 | super(db, db.nextEdgeId()); 32 | 33 | this.in = in; 34 | this.out = out; 35 | this.label = label; 36 | 37 | try { 38 | this.graph.getDatabase().set("edge:"+ String.valueOf(id) + ":out", String.valueOf(out.getId())); 39 | this.graph.getDatabase().set("edge:"+ String.valueOf(id) + ":in", String.valueOf(in.getId())); 40 | this.graph.getDatabase().set("edge:"+ String.valueOf(id) + ":label", label); 41 | 42 | this.graph.getDatabase().zadd("vertex:" + String.valueOf(in.getId()) + ":edges:in", id, String.valueOf(id)); 43 | this.graph.getDatabase().zadd("vertex:" + String.valueOf(out.getId()) + ":edges:out", id, String.valueOf(id)); 44 | 45 | this.graph.getDatabase().set("edge:" + String.valueOf(id), String.valueOf(id)); 46 | this.graph.getDatabase().zadd("globals:edges", id, String.valueOf(id)); 47 | 48 | for (RedisAutomaticIndex index : this.graph.getAutoIndices()) { 49 | index.autoUpdate(AutomaticIndex.LABEL, this.label, null, this); 50 | } 51 | 52 | } catch(RedisException e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | 57 | public RedisEdge(Long id, RedisGraph db) { 58 | super(db, id); 59 | try { 60 | this.label = new String(db.getDatabase().get(getIdentifier("label"))); 61 | } catch(RedisException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | 67 | @Override 68 | public Vertex getOutVertex() { 69 | try { 70 | byte[] b = graph.getDatabase().get(getIdentifier("out")); 71 | Long id = Long.parseLong(new String(b)); 72 | return new RedisVertex(id, graph); 73 | } catch(RedisException e) { 74 | e.printStackTrace(); 75 | } 76 | return null; 77 | } 78 | 79 | @Override 80 | public Vertex getInVertex() { 81 | try { 82 | byte[] b = graph.getDatabase().get(getIdentifier("in")); 83 | Long id = Long.parseLong(new String(b)); 84 | return new RedisVertex(id, graph); 85 | } catch(RedisException e) { 86 | e.printStackTrace(); 87 | } 88 | return null; 89 | } 90 | 91 | @Override 92 | public String getLabel() { 93 | return label; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/iterators/RedisElementIterator.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.iterators; 18 | 19 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisEdge; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisElement; 21 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 22 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisVertex; 23 | import com.tinkerpop.blueprints.pgm.Element; 24 | import org.jredis.JRedis; 25 | import org.jredis.RedisException; 26 | 27 | import java.util.Iterator; 28 | import java.util.List; 29 | 30 | public class RedisElementIterator implements Iterator { 31 | 32 | protected RedisGraph graph; 33 | private JRedis db; 34 | protected RedisElementType.TYPE type; 35 | protected RedisElement element; 36 | protected long count = 0, elementCount = 0, current = 0, currentElementIndex = 0; 37 | protected String label = null; 38 | protected RedisElementIterable iterable = null; 39 | 40 | private String elementCollectionKey; 41 | 42 | public RedisElementIterator(RedisElementType.TYPE type, RedisElementIterable iterable) { 43 | this.iterable = iterable; 44 | this.graph = iterable.getGraph(); 45 | this.db = this.graph.getDatabase(); 46 | this.label = iterable.getLabel(); 47 | this.count = iterable.getCount(); 48 | this.elementCount = iterable.getElementCount(); 49 | this.elementCollectionKey = iterable.getElementCollectionKey(); 50 | this.element = iterable.getElement(); 51 | this.type = type; 52 | } 53 | 54 | @Override 55 | public boolean hasNext() { 56 | return current < count; 57 | } 58 | 59 | @Override 60 | public Object next() { 61 | List db_vertices = null; 62 | Element el = null; 63 | 64 | try { 65 | db_vertices = db.zrange(elementCollectionKey, current, current); 66 | long id = Long.parseLong(new String(db_vertices.get(0))); 67 | 68 | if(type.equals(RedisElementType.TYPE.REDIS_ELEMENT_VERTEX)){ 69 | el = new RedisVertex(id, graph); 70 | } 71 | else{ 72 | el = new RedisEdge(id, graph); 73 | if(null != this.label && !((RedisEdge)el).getLabel().equals(this.label)){ 74 | el = null; 75 | while(true && this.currentElementIndex < this.elementCount){ 76 | this.currentElementIndex++; 77 | db_vertices = db.zrange(elementCollectionKey, this.currentElementIndex, this.currentElementIndex); 78 | id = Long.parseLong(new String(db_vertices.get(0))); 79 | 80 | el = new RedisEdge(id, graph); 81 | if(((RedisEdge)el).getLabel().equals(this.label)){ 82 | break; 83 | } 84 | } 85 | } 86 | } 87 | } catch(RedisException e) { 88 | e.printStackTrace(); 89 | } 90 | 91 | current++; 92 | 93 | return el; 94 | } 95 | 96 | @Override 97 | public void remove() { 98 | 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/index/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | *

3 | * Basic indexing support supplied with Blueredis is based on 4 | * A fast, fuzzy, full-text index using Redis 5 | *

6 | *

7 | * WARNING! Bundled indexing implemetation is quite resource intensive, creates lots of key-value pairs in redis 8 | * and negatively affects performance. Use it at your own risk. See below for description 9 | *

10 | *

11 | * Basically, the algorithm is as follows: 12 | *

    13 | *
  • take incoming key, value and vertex
  • 14 | *
  • calculate Metaphone for incoming value
  • 15 | *
  • add vertex id to key:metaphone_of_value
  • 16 | *
17 | *

18 | *

19 | * E.g. you have a vertex with id=123. It has a property "name" equal to "John Smith". It means: 20 | *

    21 | *
  • key="name", value="John Smith"
  • 22 | *
  • metaphone for John Smith is JNSM0
  • 23 | *
  • 123 will be added to name:JNSM0
  • 24 | *
25 | *

26 | *

27 | * Of course, reality is slightly more complicated than that: 28 | *

    29 | *
  • there are automatic and manual indices
  • 30 | *
  • indices are accessed by their names, there can be several automatic and several manual indices on a 31 | * database
  • 32 | *
  • an index may index vertices or edges, but not both
  • 33 | *
  • indices may index all keys or only a subset of keys
  • 34 | *
  • indices must persist and be accessible by their name and for the same set of keys as before after a 35 | * database restart
  • 36 | *
37 | * So, we have to: 38 | *
    39 | *
  • create a separate index entry for each auto/manual... with separate subentries for vertices and edges
  • 40 | *
  • 41 | *
      42 | *
    • for each index key in an index save that key 43 | *
        44 | *
      • for each key save vertex/edge ids the key points to
      • 45 | *
      46 | *
    • 47 | *
    • for each index entry save list of keys that it holds
    • 48 | *
    49 | *
  • 50 | *
  • save a list of all indices
  • 51 | *
52 | * Sounds like a lot of work and, well, it is. Since redis is a key-value only store, there's a lot of extra work 53 | * that you have to do to make all this work. 54 | *
    55 | *
  • Indices are stored in keys that all start with an index:...
  • 56 | *
  • index:auto - prefix for all automatic indices
  • 57 | *
  • index:manual - prefix for all manual indices
  • 58 | *
      59 | * For all of the above: 60 | *
    • index:type:index_name, where index_name 61 | * is a user-supplied index name - prefix for index data for index_name 62 | *
    • 63 | *
    • index:type:index_name:key_name, 64 | * where key_name 65 | * is a user-supplied key name - prefix for index data for key_name key 66 | *
    • 67 | *
    • index:type:index_name:key_name:metaphone, 68 | * where metaphone 69 | * is a calculated metaphone for a value - contains a list of vertex ids that have a key key_name 70 | * with a value whose metaphone matches metaphone 71 | *
    • 72 | *
    73 | *
  • index:meta:indices:auto - a list of automatic indices
  • 74 | *
  • index:meta:indices:manual - a list of manual indices
  • 75 | *
  • index:meta:auto - prefix for all meta information on automatic indices
  • 76 | *
  • index:meta:manual - prefix for all meta information on manual indices
  • 77 | *
      78 | * For index:meta:(auto|manual) above: 79 | *
    • index:meta:type:index_name:class, where index_name 80 | * is a user-supplied index name - contains what class the index works on: vertex, edge, etc. 81 | *
    • 82 | *
    • index:meta:type:index_name:keys, where index_name 83 | * is a user-supplied index name - contains a list of keys for this index 84 | *
    • 85 | *
    86 | *
87 | * So, as you see, indexing support manipulates quite a few values, so if speed is your primary concern, do not tur on 88 | * default indexing 89 | *

90 | * 91 | * @since 0.2 92 | * @since 0.3 93 | * @see A fast, fuzzy, full-text index using Redis 94 | */ 95 | package com.tinkerpop.blueprints.pgm.impls.blueredis.index; -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | This is a straightforward and rather naïve implementation of a [Blueprints](http://blueprints.tinkerpop.com/)-enabled 2 | graph over [Redis](http://code.google.com/p/redis/). 3 | 4 | Blueprints is a database-agnostic library for handling graphs. Blueredis allows you to run Blueprints on top of redis. 5 | Also see [Pipes](http://pipes.tinkerpop.com/) and [Gremlin](http://gremlin.tinkerpop.com/) to see what this integration 6 | will also allow you to do. 7 | 8 | Running 9 | === 10 | 11 | Requires Redis 2.x compatible version of [JRedis](http://github.com/alphazero/jredis) (git clone this repo, and you'll get a jar) 12 | 13 | 14 | Graph db = new RedisGraph(); 15 | 16 | String password = "pass"; 17 | Graph db1 = new RedisGraph(password); 18 | 19 | String host = "127.0.0.1"; 20 | int port = 6379; 21 | Graph db2 = new RedisGraph(host, port); 22 | 23 | int database = 10; 24 | Graph db3 = new RedisGraph(host, port, pass, database); 25 | 26 | After this you work with the database as with any Blueprints-enabled graph. 27 | 28 | Peculiarities 29 | === 30 | 31 | ID creation 32 | --- 33 | 34 | RedisGraph **handles id creation for you**. Any id parameter passed to addVertex/addEdge will be ignored. All ids are 35 | of type long 36 | 37 | Serializing properties (off by default) 38 | --- 39 | 40 | **By default all properties are saved and retrieved as strings**, regardless of the type of object you pass to 41 | `addVertex`. If you want to save actual objects/values, call `graph.serializeProperties(true)`. Note though, that it 42 | uses Base64 encoding on top of Java serialization so it may take up a lot of space. 43 | 44 | Transactions 45 | --- 46 | 47 | Currently **transactions are not supported** 48 | 49 | Indexing (off by default) 50 | === 51 | 52 | **WARNING!** Indexing support that's bundled with Blueredis is rather resource intensive, manipulates quite a few 53 | key-value pair in redis and negatively affects performance. If speed is your primary concern, don't use the default 54 | indexing implementation 55 | 56 | Blueredis implements an index for both vertex and edge properties. This implementation is based on 57 | [A fast, fuzzy, full-text index using Redis](http://playnice.ly/blog/2010/05/05/a-fast-fuzzy-full-text-index-using-redis/). 58 | 59 | Turning indexing on 60 | --- 61 | 62 | **By default indexing is off**. To turn it on, call `graph.setIndexing(true)`. To turn it back on, call `graph.setIndexing(false)`. 63 | 64 | Implementing your own 65 | --- 66 | 67 | You may wish to implement your own indexing service. To do this: 68 | 69 | * implement Blueprints' `Index` and `AutomaticIndex` interfaces 70 | * override Blueredis' `RedisIndexManager` class 71 | * call `graph.setIndexing(true, yourManagerInstance)` and pass an instance of your indexManager to it 72 | 73 | Implementation 74 | === 75 | 76 | Vertices 77 | --- 78 | 79 | Each vertex is represented by the following keys: 80 | 81 | * `vertex:ID`, vertex id. This one is used to test if a vertex exists 82 | * `vertex:ID:properties`, a hash of all vertex properties 83 | * `vertex:ID:edges:in`, a set of all incoming edges 84 | * `vertex:ID:edges:out`, a set of all outgoing edges 85 | 86 | Edges 87 | --- 88 | 89 | Each edge is represented by the following keys: 90 | 91 | * `edge:ID`, edge id. This one is used to test if an edge exists 92 | * `edge:ID:label`, a string containing the edge's label 93 | * `edge:ID:in`, index of "in" vertex 94 | * `edge:ID:out`, index of "out" vertex 95 | * `edge:ID:properties`, a hash of all edge properties 96 | 97 | Globals 98 | --- 99 | 100 | This are just some keys that hold values necessary for Blueredis to work: 101 | 102 | * `globals:next_vertex_id`, a counter that's incremented each time a new vertex is added 103 | * `globals:next_edge_id`, a counter that's incremented each time a new edge is added 104 | * `globals:vertices`, a sorted list of all vertex ids 105 | * `globals:edges`, a sorted list of all edge ids 106 | 107 | Indices 108 | --- 109 | **This section describes default indexing support that's bundled with Blueredis**. You're free to implement your own. 110 | 111 | Indices are stored in keys that all start with an `index:...` 112 | 113 | * `index:auto` - prefix for all automatic indices 114 | * `index:manual` - prefix for all manual indices 115 | For all of the above: 116 | * `index:type:index_name`, where `index_name` is a user-supplied index name - prefix for index data for `index_name` 117 | * `index:type:index_name:key_name`, where `key_name` is a user-supplied key name - prefix for index data for `key_name` key 118 | * `index:type:index_name:key_name:metaphone`, where `metaphone` is a calculated metaphone for a value - contains 119 | a list of vertex ids that have a key key_name with a value whose metaphone matches metaphone 120 | * `index:meta:indices:auto` - a list of automatic indices 121 | * `index:meta:indices:manual` - a list of manual indices 122 | * `index:meta:auto` - prefix for all meta information on automatic indices 123 | * `index:meta:manual` - prefix for all meta information on manual indices 124 | For index:meta:(auto|manual) above: 125 | * `index:meta:type:index_name:class`, where `index_name` is a user-supplied index name - contains what class 126 | the index works on: vertex, edge, etc. 127 | * `index:meta:type:index_name:keys`, where `index_name` is a user-supplied index name - contains a list of keys 128 | for this index 129 | 130 | Tests 131 | === 132 | 133 | RedisGraph now passes tinkerpop's tests. 134 | 135 | 136 | Benchmarks 137 | === 138 | 139 | An old benchmark can be found here: [benchmarks page in wiki](http://github.com/dmitriid/blueredis/wiki). Note that reeds 140 | seems to perform better under load than during singular requests. A newer benchmark will be published as soon as there's one :) -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | com.dmitriid 7 | blueredis 8 | 0.2.1 9 | jar 10 | http://dmitriid.com 11 | Blueprints-enabled graph over redis 12 | Blueprints-enabled graph over redis. 13 | 14 | 15 | Dmitrii 'Mamut' Dimandt 16 | dmitrii@dmitriid.com 17 | http://dmitriid.com 18 | 19 | 20 | 2010 21 | 22 | 27 | 28 | com.tinkerpop 29 | blueprints 30 | 0.4 31 | 32 | 33 | commons-codec 34 | commons-codec 35 | 1.3 36 | 37 | 38 | jredis 39 | org.jredis.ri 40 | a.0-SNAPSHOT-jar-with-dependencies 41 | system 42 | ${basedir}/deps/JRedis.jar 43 | 44 | 45 | 46 | junit 47 | junit 48 | 4.5 49 | test 50 | 51 | 52 | 53 | log4j 54 | log4j 55 | 1.2.14 56 | 57 | 58 | org.slf4j 59 | slf4j-log4j12 60 | 1.6.1 61 | 62 | 63 | 64 | UTF-8 65 | 66 | 67 | 68 | maven repository 69 | http://mvnrepository.com 70 | 71 | 72 | tinkerpop-repository 73 | TinkerPop Maven2 Repository 74 | http://tinkerpop.com/maven2 75 | 76 | true 77 | daily 78 | 79 | 80 | 81 | 82 | ${basedir}/target 83 | ${artifactId}-${version} 84 | 85 | ${basedir}/src/main/java 86 | 87 | ${basedir}/src/test/java 88 | 89 | ${basedir}/target/classes 90 | 91 | ${basedir}/target/test-classes 92 | 93 | 94 | 95 | ${basedir}/src/main/resources 96 | 97 | 98 | 99 | 100 | 101 | ${basedir}/src/test/resources 102 | 103 | 104 | 105 | 106 | 107 | maven-compiler-plugin 108 | 109 | 1.6 110 | 1.6 111 | 112 | 113 | 114 | maven-assembly-plugin 115 | 2.2-beta-4 116 | 117 | 118 | package 119 | 120 | attached 121 | 122 | 123 | 124 | 125 | 126 | src/assembly/standalone.xml 127 | src/assembly/distribution.xml 128 | 129 | blueredis-${project.version} 130 | target 131 | target/assembly/work 132 | warn 133 | 138 | 139 | 140 | 141 | org.apache.maven.plugins 142 | maven-surefire-plugin 143 | 2.4.2 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | testRedisGraph 152 | true 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | org.apache.maven.wagon 161 | wagon-ftp 162 | 1.0-alpha-6 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/RedisElement.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis; 18 | 19 | import com.tinkerpop.blueprints.pgm.impls.blueredis.index.RedisAutomaticIndex; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.utils.Base64Coder; 21 | import com.tinkerpop.blueprints.pgm.Edge; 22 | import com.tinkerpop.blueprints.pgm.Element; 23 | import org.jredis.RedisException; 24 | 25 | import java.io.*; 26 | import java.util.HashSet; 27 | import java.util.List; 28 | import java.util.Set; 29 | 30 | 31 | public class RedisElement implements Element { 32 | protected Long id = null; 33 | protected RedisGraph graph = null; 34 | 35 | RedisElement(RedisGraph graph, Long id) { 36 | this.graph = graph; 37 | this.id = id; 38 | } 39 | 40 | @Override 41 | public Object getProperty(String s) { 42 | try { 43 | byte[] o = (byte[]) graph.getDatabase().hget(getIdentifier("properties"), s); 44 | 45 | if(o != null){ 46 | if(graph.serializeProperties()){ 47 | return getObject(new String(o)); 48 | } else { 49 | return new String(o); 50 | } 51 | } 52 | } catch(RedisException e) { 53 | e.printStackTrace(); 54 | } catch(IOException e) { 55 | e.printStackTrace(); 56 | } catch(ClassNotFoundException e) { 57 | e.printStackTrace(); 58 | } 59 | return null; 60 | } 61 | 62 | @Override 63 | public Set getPropertyKeys() { 64 | try { 65 | List l = graph.getDatabase().hkeys(getIdentifier("properties")); 66 | if(l != null){ 67 | return new HashSet(l); 68 | } 69 | } catch(RedisException e) { 70 | e.printStackTrace(); 71 | } 72 | return null; 73 | } 74 | 75 | @Override 76 | public void setProperty(String s, Object o) { 77 | try { 78 | String val; 79 | if(graph.serializeProperties()){ 80 | val = writeObject(o); 81 | } else { 82 | val = String.valueOf(o); 83 | } 84 | if(graph.doIndexing()){ 85 | Object oldValue = this.getProperty(s); 86 | for (RedisAutomaticIndex index : this.graph.getAutoIndices()) { 87 | index.autoUpdate(s, o, oldValue, this); 88 | } 89 | } 90 | graph.getDatabase().hset(getIdentifier("properties"), s, val); 91 | } catch(RedisException e) { 92 | e.printStackTrace(); 93 | } catch(IOException e) { 94 | e.printStackTrace(); 95 | } 96 | } 97 | 98 | @Override 99 | public Object removeProperty(String s) { 100 | try { 101 | Object property = getProperty(s); 102 | if(graph.doIndexing()) { 103 | for (RedisAutomaticIndex index : this.graph.getAutoIndices()) { 104 | index.autoRemove(s, property, this); 105 | } 106 | } 107 | graph.getDatabase().hdel(getIdentifier("properties"), s); 108 | return property; 109 | } catch(RedisException e) { 110 | e.printStackTrace(); 111 | } 112 | return null; 113 | } 114 | 115 | @Override 116 | public Object getId() { 117 | return id; 118 | } 119 | 120 | public void remove() { 121 | if(this instanceof RedisVertex) { 122 | RedisVertex vertex = (RedisVertex) this; 123 | 124 | // 1. Remove all edges 125 | // 2. Remove self 126 | // 3. Remove from global registry 127 | 128 | Iterable edges; 129 | 130 | edges = vertex.getInEdges(); 131 | for(Edge edge : edges) { 132 | ((RedisEdge) edge).remove(); 133 | } 134 | 135 | edges = vertex.getOutEdges(); 136 | for(Edge edge : edges) { 137 | ((RedisEdge) edge).remove(); 138 | } 139 | 140 | try { 141 | graph.getDatabase().del(getIdentifier(null)); 142 | graph.getDatabase().del(getIdentifier("properties")); 143 | graph.getDatabase().del(getIdentifier("edges:in")); 144 | graph.getDatabase().del(getIdentifier("edges:out")); 145 | graph.getDatabase().del("vertex:" + String.valueOf(id)); 146 | graph.getDatabase().zrem("globals:vertices", String.valueOf(id)); 147 | 148 | } catch(RedisException e) { 149 | e.printStackTrace(); 150 | } 151 | } else { 152 | RedisEdge edge = (RedisEdge) this; 153 | 154 | // 1. Remove all vertices 155 | // 2. Remove self 156 | // 3. Remove from global registry 157 | 158 | RedisVertex in = (RedisVertex) edge.getInVertex(); 159 | RedisVertex out = (RedisVertex) edge.getOutVertex(); 160 | 161 | try { 162 | graph.getDatabase().del(getIdentifier("in")); 163 | graph.getDatabase().del(getIdentifier("out")); 164 | graph.getDatabase().del(getIdentifier("label")); 165 | graph.getDatabase().del(getIdentifier("properties")); 166 | graph.getDatabase().del("edge:" + String.valueOf(id)); 167 | 168 | graph.getDatabase().zrem(out.getIdentifier("edges:out"), String.valueOf(getId())); 169 | graph.getDatabase().zrem(in.getIdentifier("edges:in"), String.valueOf(getId())); 170 | 171 | graph.getDatabase().zrem("globals:edges", String.valueOf(id)); 172 | } catch(RedisException e) { 173 | e.printStackTrace(); 174 | } 175 | 176 | } 177 | } 178 | 179 | protected String getIdentifier(String suffix) { 180 | String prefix = this instanceof RedisVertex ? "vertex:" : "edge:"; 181 | String identifier = prefix + String.valueOf(id); 182 | if(suffix != null) identifier = identifier + ":" + suffix; 183 | 184 | return identifier; 185 | 186 | } 187 | 188 | public int hashCode() { 189 | return this.getId().hashCode(); 190 | } 191 | 192 | public boolean equals(Object object) { 193 | return (this.getClass().equals(object.getClass()) && this.getId().equals(((Element) object).getId())); 194 | } 195 | 196 | private String writeObject(Object o) throws IOException { 197 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 198 | ObjectOutputStream oos = new ObjectOutputStream(baos); 199 | 200 | oos.writeObject(o); 201 | oos.close(); 202 | 203 | return new String(Base64Coder.encode(baos.toByteArray())); 204 | } 205 | 206 | private Object getObject(String s) throws IOException, ClassNotFoundException { 207 | byte[] data = Base64Coder.decode(s); 208 | ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); 209 | Object o = ois.readObject(); 210 | ois.close(); 211 | return o; 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/index/RedisIndex.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | 18 | package com.tinkerpop.blueprints.pgm.impls.blueredis.index; 19 | 20 | import com.tinkerpop.blueprints.pgm.Element; 21 | import com.tinkerpop.blueprints.pgm.Index; 22 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisEdge; 23 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 24 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisVertex; 25 | import org.apache.commons.codec.language.DoubleMetaphone; 26 | import org.jredis.JRedis; 27 | import org.jredis.RedisException; 28 | 29 | import java.text.Normalizer; 30 | import java.util.ArrayList; 31 | import java.util.HashSet; 32 | import java.util.List; 33 | import java.util.Set; 34 | import java.util.regex.Matcher; 35 | import java.util.regex.Pattern; 36 | 37 | /** 38 | * @see RedisAutomaticIndex 39 | * @see RedisIndexManager 40 | */ 41 | 42 | public class RedisIndex implements Index { 43 | 44 | protected RedisGraph graph; 45 | protected JRedis database; 46 | 47 | protected String indexName; 48 | protected Class indexClass; 49 | protected String nodeName; 50 | 51 | protected Set indexKeys = new HashSet(); 52 | 53 | protected DoubleMetaphone metaphone = new DoubleMetaphone(); 54 | 55 | protected boolean indexAll = true; 56 | 57 | protected Type indexType = Type.MANUAL; 58 | 59 | public RedisIndex(RedisGraph graph, final String indexName, final Class indexClass) { 60 | this.graph = graph; 61 | this.database = graph.getDatabase(); 62 | this.indexClass = indexClass; 63 | 64 | // convert any index name to a form that can be sent to redis, i.e. 65 | // convert "Tĥïŝ ĩš â fůňķŷ Šťŕĭńġ" to "this_is_a_funky_string" 66 | this.indexName = RedisIndex.normalizeName(indexName); 67 | this.nodeName = RedisIndexKeys.MANUAL + this.indexName + ":"; 68 | this.metaphone.setMaxCodeLen(12); 69 | } 70 | public RedisIndex(RedisGraph graph, final String indexName, final Class indexClass, Set indexKeys) { 71 | this(graph, indexName, indexClass); 72 | this.indexKeys = indexKeys; 73 | } 74 | 75 | @Override 76 | public String getIndexName() { 77 | return this.indexName; 78 | } 79 | 80 | @Override 81 | public Class getIndexClass() { 82 | return this.indexClass; 83 | } 84 | 85 | @Override 86 | public Type getIndexType() { 87 | return this.indexType; 88 | } 89 | 90 | @Override 91 | public void put(final String key, final Object value, final T element) { 92 | if(!this.indexAll && !this.indexKeys.contains(key)) { 93 | return; 94 | } 95 | 96 | if (!this.indexClass.isAssignableFrom(element.getClass())) { 97 | return; 98 | } 99 | 100 | indexKeys.add(key); 101 | 102 | String val = getMetaphone(value); 103 | String node_name = this.nodeName + key + ":" + val; 104 | 105 | try { 106 | database.sadd(node_name, element.getId().toString()); 107 | } catch(RedisException e) { 108 | e.printStackTrace(); 109 | } 110 | 111 | if(this.indexAll){ 112 | this.updateIndexKey(key); 113 | } 114 | } 115 | 116 | @Override 117 | public Iterable get(String key, Object value) { 118 | if(!indexKeys.contains(key)){ 119 | return new ArrayList(); 120 | } 121 | ArrayList arr = new ArrayList(); 122 | 123 | String val = getMetaphone(value); 124 | String node_name; 125 | List l; 126 | 127 | try { 128 | node_name = this.nodeName + key + ":" + val; 129 | l = database.smembers(node_name); 130 | if(l != null) { 131 | for(byte[] o : l) { 132 | Long idx = Long.parseLong(new String(o)); 133 | if(this.indexClass.isAssignableFrom(RedisVertex.class)){ 134 | arr.add((T)new RedisVertex(idx, graph)); 135 | } else { 136 | arr.add((T)new RedisEdge(idx, graph)); 137 | } 138 | } 139 | } 140 | } catch(RedisException e) { 141 | e.printStackTrace(); 142 | } 143 | 144 | return arr; 145 | } 146 | 147 | @Override 148 | public void remove(String key, Object value, T element) { 149 | if(value == null) return; 150 | 151 | if (!this.indexClass.isAssignableFrom(element.getClass())) { 152 | return; 153 | } 154 | 155 | String val = getMetaphone(value); 156 | String node_name = this.nodeName + key + ":" + val; 157 | 158 | try { 159 | database.srem(node_name, element.getId().toString()); 160 | } catch(RedisException e) { 161 | e.printStackTrace(); 162 | } 163 | } 164 | 165 | public void removeElement(Element element){ 166 | if (!this.indexClass.isAssignableFrom(element.getClass())) { 167 | return; 168 | } 169 | 170 | // note: this may be VERY resource intensive (especially memory-wise) 171 | // this implementation retrieves all values from each key and attempts 172 | // to remove the current element from the key 173 | 174 | for(final String key : this.indexKeys){ 175 | try { 176 | List keys = database.keys(this.nodeName + "*"); 177 | 178 | for(final String val : keys){ 179 | try{ 180 | database.srem(val, element.getId().toString()); 181 | } catch (RedisException ignore) { 182 | } 183 | } 184 | } catch (RedisException ignore) { 185 | } 186 | } 187 | } 188 | 189 | public void autoUpdate(final String key, final Object newValue, final Object oldValue, final T element) { 190 | if (this.getIndexClass().isAssignableFrom(element.getClass()) && (this.indexAll || this.indexKeys.size() == 0 || this.indexKeys.contains(key))) { 191 | if (oldValue != null) 192 | this.remove(key, oldValue, element); 193 | this.put(key, newValue, element); 194 | if(this.indexAll){ 195 | this.updateIndexKey(key); 196 | } 197 | } 198 | } 199 | 200 | public void autoRemove(final String key, final Object oldValue, final T element) { 201 | if (this.getIndexClass().isAssignableFrom(element.getClass()) && (this.indexAll || this.indexKeys.size() == 0 || this.indexKeys.contains(key))) { 202 | this.remove(key, oldValue, element); 203 | } 204 | } 205 | 206 | protected void updateIndexKey(String key){ 207 | try{ 208 | if(this.indexType.equals(Type.AUTOMATIC)){ 209 | database.sadd(RedisIndexKeys.META_AUTO + this.indexName + ":keys", key); 210 | } else { 211 | database.sadd(RedisIndexKeys.META_MANUAL + this.indexName + ":keys", key); 212 | } 213 | } catch (RedisException ignore) { 214 | } 215 | 216 | } 217 | 218 | private String getMetaphone(Object o){ 219 | String val = ""; 220 | try{ 221 | val = (String) metaphone.encode(String.valueOf(o)); 222 | } catch(Exception ignored) { 223 | } 224 | 225 | if(val.equals("")) val = o.toString(); 226 | 227 | return val; 228 | } 229 | 230 | public static String normalizeName(final String indexName){ 231 | return Normalizer.normalize(indexName, Normalizer.Form.NFD) 232 | .replaceAll("[^\\p{ASCII}]", "") 233 | .replaceAll(" ", "_") 234 | .toLowerCase(); 235 | } 236 | 237 | public void addKey(String key){ 238 | this.indexKeys.add(key); 239 | this.updateIndexKey(key); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/index/RedisIndexManager.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.index; 18 | 19 | import com.tinkerpop.blueprints.pgm.*; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisEdge; 21 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisGraph; 22 | import com.tinkerpop.blueprints.pgm.impls.blueredis.RedisVertex; 23 | import com.tinkerpop.blueprints.pgm.util.AutomaticIndexHelper; 24 | import org.jredis.JRedis; 25 | import org.jredis.RedisException; 26 | 27 | import java.util.*; 28 | 29 | /** 30 | * @see RedisIndex 31 | * @see RedisAutomaticIndex 32 | */ 33 | 34 | public class RedisIndexManager { 35 | 36 | protected RedisGraph graph = null; 37 | protected Map> manualIndices = new HashMap>(); 38 | protected Map> autoIndices = new HashMap>(); 39 | protected boolean restoreMode = false; // when in restore mode, don't save data back to db 40 | 41 | public RedisIndexManager() { 42 | } 43 | 44 | public void setGraph(RedisGraph graph) { 45 | this.graph = graph; 46 | } 47 | 48 | public Index createManualIndex(final String indexName, final Class indexClass){ 49 | return this.createManualIndex(indexName, indexClass, new HashSet()); 50 | } 51 | public Index createManualIndex(final String indexName, final Class indexClass, Set keys) { 52 | String idxName = RedisIndex.normalizeName(indexName); 53 | RedisIndex idx = (RedisIndex)this.manualIndices.get(idxName); 54 | 55 | if(null != idx){ 56 | throw new RuntimeException("Index already exists: " + indexName); 57 | } else { 58 | idx = new RedisIndex(this.graph, idxName, indexClass, keys); 59 | 60 | this.manualIndices.put(idxName, (RedisIndex)idx); 61 | if(!this.restoreMode){ 62 | this.saveIndexMeta(RedisIndexKeys.MANUAL, idxName, indexClass.getCanonicalName(), keys); 63 | } 64 | } 65 | 66 | return idx; 67 | } 68 | 69 | public AutomaticIndex createAutomaticIndex(final String indexName, final Class indexClass, Set keys){ 70 | String idxName = RedisIndex.normalizeName(indexName); 71 | RedisAutomaticIndex idx = (RedisAutomaticIndex)this.autoIndices.get(idxName); 72 | 73 | if(null != idx){ 74 | throw new RuntimeException("Index already exists: " + indexName); 75 | } else { 76 | idx = new RedisAutomaticIndex(this.graph, idxName, indexClass, keys); 77 | 78 | this.autoIndices.put(idxName, (AutomaticIndex)idx); 79 | if(!this.restoreMode){ 80 | this.saveIndexMeta(RedisIndexKeys.AUTO, idxName, indexClass.getCanonicalName(), keys); 81 | } 82 | } 83 | 84 | return idx; 85 | } 86 | 87 | public Index getIndex(final String indexName, final Class indexClass) { 88 | String idxName = RedisIndex.normalizeName(indexName); 89 | Index index = this.manualIndices.get(idxName); 90 | if (null == index) 91 | index = this.autoIndices.get(idxName); 92 | 93 | if(null == index) 94 | throw new RuntimeException("No such index exists: " + indexName); 95 | if (!indexClass.isAssignableFrom(index.getIndexClass())) 96 | throw new RuntimeException(indexClass + " is not assignable from " + index.getIndexClass()); 97 | else 98 | return (Index) index; 99 | } 100 | 101 | public Iterable> getIndices() { 102 | List> list = new ArrayList>(); 103 | for (Index index : manualIndices.values()) { 104 | list.add(index); 105 | } 106 | for (Index index : autoIndices.values()) { 107 | list.add(index); 108 | } 109 | return list; 110 | } 111 | 112 | public void dropIndex(final String indexName) { 113 | String idxName = RedisIndex.normalizeName(indexName); 114 | JRedis db = this.graph.getDatabase(); 115 | 116 | try { 117 | List keyList = db.keys(RedisIndexKeys.AUTO + idxName + ":*"); 118 | for(String key : keyList){ 119 | db.del(key); 120 | } 121 | 122 | keyList = db.keys(RedisIndexKeys.META_AUTO + idxName + ":*"); 123 | for(String key : keyList){ 124 | db.del(key); 125 | } 126 | 127 | db.lrem(RedisIndexKeys.META_INDICES_AUTO, idxName, 0); 128 | 129 | keyList = db.keys(RedisIndexKeys.MANUAL + idxName + ":*"); 130 | for(String key : keyList){ 131 | db.del(key); 132 | } 133 | 134 | keyList = db.keys(RedisIndexKeys.META_MANUAL + idxName + ":*"); 135 | for(String key : keyList){ 136 | db.del(key); 137 | } 138 | 139 | db.lrem(RedisIndexKeys.META_INDICES_MANUAL, idxName, 0); 140 | } catch (RedisException e) { 141 | e.printStackTrace(); 142 | } 143 | 144 | this.manualIndices.remove(idxName); 145 | this.autoIndices.remove(idxName); 146 | 147 | } 148 | 149 | public Iterable getAutoIndices() { 150 | List list = new ArrayList(); 151 | for (Index index : autoIndices.values()) { 152 | list.add((RedisAutomaticIndex)index); 153 | } 154 | return list; 155 | } 156 | 157 | public Iterable getManualIndices() { 158 | List list = new ArrayList(); 159 | for (Index index : manualIndices.values()) { 160 | list.add((RedisIndex)index); 161 | } 162 | return list; 163 | } 164 | 165 | private void saveIndexMeta(final String type, final String indexName, final String className, Set keys){ 166 | JRedis db = this.graph.getDatabase(); 167 | 168 | try { 169 | if (type.equals(RedisIndexKeys.AUTO)) { 170 | db.lpush(RedisIndexKeys.META_INDICES_AUTO, indexName); 171 | db.set(RedisIndexKeys.META_AUTO + indexName + ":class", className); 172 | if(null != keys){ 173 | String key_list = RedisIndexKeys.META_AUTO + indexName + ":keys"; 174 | for(String key : keys){ 175 | db.sadd(key_list, key); 176 | } 177 | } 178 | } else { 179 | db.lpush(RedisIndexKeys.META_INDICES_MANUAL, indexName); 180 | db.set(RedisIndexKeys.META_MANUAL + indexName + ":class", className); 181 | } 182 | } catch (RedisException e) { 183 | e.printStackTrace(); 184 | } 185 | } 186 | 187 | public void restoreIndices(){ 188 | this.restoreMode = true; 189 | this.restoreIndices(RedisIndexKeys.AUTO); 190 | this.restoreIndices(RedisIndexKeys.MANUAL); 191 | this.restoreMode = false; 192 | } 193 | 194 | public void restoreIndices(String type){ 195 | JRedis db = this.graph.getDatabase(); 196 | 197 | String metaIndices = type.equals(RedisIndexKeys.AUTO) ? RedisIndexKeys.META_INDICES_AUTO : RedisIndexKeys.META_INDICES_MANUAL; 198 | String metaType = type.equals(RedisIndexKeys.AUTO) ? RedisIndexKeys.META_AUTO : RedisIndexKeys.META_MANUAL; 199 | 200 | try { 201 | List indices = db.lrange(metaIndices, 0, db.llen(metaIndices)); 202 | 203 | if(null == indices) return; 204 | 205 | for(byte[] idx: indices){ 206 | String indexName = new String(idx); 207 | 208 | String className = new String(db.get(metaType + indexName + ":class")); 209 | 210 | Class indexClass = Class.forName(className); 211 | 212 | List keys = db.smembers(metaType + indexName + ":keys"); 213 | HashSet indexKeys = new HashSet(); 214 | 215 | if(null != keys){ 216 | for(byte[] key : keys){ 217 | indexKeys.add(new String(key)); 218 | } 219 | } 220 | 221 | if(type.equals(RedisIndexKeys.AUTO)){ 222 | this.createAutomaticIndex(indexName, indexClass, indexKeys.size() != 0 ? indexKeys : null); 223 | } else { 224 | this.createManualIndex(indexName, indexClass, indexKeys.size() != 0 ? indexKeys : new HashSet()); 225 | } 226 | } 227 | } catch (RedisException e) { 228 | e.printStackTrace(); 229 | }catch (ClassNotFoundException e) { 230 | e.printStackTrace(); 231 | } 232 | } 233 | 234 | public void removeElement(Element el){ 235 | AutomaticIndexHelper.removeElement(this.graph, el); 236 | for (Index index : this.getManualIndices()) { 237 | if (Vertex.class.isAssignableFrom(index.getIndexClass())) { 238 | RedisIndex idx = (RedisIndex) index; 239 | idx.removeElement(el); 240 | } else if(Edge.class.isAssignableFrom(index.getIndexClass())) { 241 | RedisIndex idx = (RedisIndex) index; 242 | idx.removeElement(el); 243 | } 244 | } 245 | } 246 | 247 | public void clear(){ 248 | this.autoIndices.clear(); 249 | this.manualIndices.clear(); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/RedisGraph.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis; 18 | 19 | import com.tinkerpop.blueprints.pgm.*; 20 | import com.tinkerpop.blueprints.pgm.impls.blueredis.index.RedisAutomaticIndex; 21 | import com.tinkerpop.blueprints.pgm.impls.blueredis.index.RedisIndex; 22 | import com.tinkerpop.blueprints.pgm.impls.blueredis.index.RedisIndexManager; 23 | import com.tinkerpop.blueprints.pgm.impls.blueredis.iterators.RedisEdgeIterable; 24 | import com.tinkerpop.blueprints.pgm.impls.blueredis.iterators.RedisVertexIterable; 25 | import com.tinkerpop.blueprints.pgm.util.AutomaticIndexHelper; 26 | import org.jredis.JRedis; 27 | import org.jredis.RedisException; 28 | import org.jredis.ri.alphazero.JRedisClient; 29 | 30 | import java.util.*; 31 | 32 | public class RedisGraph implements IndexableGraph { 33 | 34 | private JRedis database = null; 35 | private boolean serializeProps = false; // if true, save type info with prop values 36 | private Index index; 37 | private boolean do_index = true; 38 | private RedisIndexManager indexManager; 39 | 40 | protected Map indices = new HashMap(); 41 | protected Map autoIndices = new HashMap(); 42 | 43 | public RedisGraph() { 44 | database = new JRedisClient(); 45 | } 46 | 47 | public RedisGraph(String password) { 48 | database = new JRedisClient(password); 49 | } 50 | 51 | public RedisGraph(String host, int port) { 52 | database = new JRedisClient(host, port); 53 | } 54 | 55 | public RedisGraph(String host, int port, String password, int database) { 56 | this.database = new JRedisClient(host, port, password, database); 57 | } 58 | 59 | public void serializeProperties(boolean serialize) { 60 | serializeProps = serialize; 61 | } 62 | 63 | public boolean serializeProperties() { 64 | return serializeProps; 65 | } 66 | 67 | public void setIndexing(boolean b) { 68 | do_index = b; 69 | if(b == true){ 70 | this.indexManager = new RedisIndexManager(); 71 | this.prepareIndexing(); 72 | } 73 | } 74 | 75 | public void setIndexing(boolean b, RedisIndexManager manager) { 76 | do_index = b; 77 | this.indexManager = manager; 78 | if(b == true){ 79 | this.prepareIndexing(); 80 | } 81 | } 82 | 83 | public boolean doIndexing(){ 84 | return do_index; 85 | } 86 | 87 | public long nextVertexId(){ 88 | try { 89 | return database.incr("globals:next_vertex_id"); 90 | } catch(RedisException e) { 91 | e.printStackTrace(); 92 | } 93 | return 0; 94 | } 95 | 96 | public long nextEdgeId(){ 97 | try { 98 | return database.incr("globals:next_edge_id"); 99 | } catch(RedisException e) { 100 | e.printStackTrace(); 101 | } 102 | return 0; 103 | } 104 | 105 | public JRedis getDatabase(){ 106 | return database; 107 | } 108 | 109 | @Override 110 | public Vertex addVertex(Object o) { 111 | final Vertex vertex = new RedisVertex(this); 112 | return vertex; 113 | } 114 | 115 | @Override 116 | public Vertex getVertex(Object o) { 117 | try{ 118 | Long id = getLong(o); 119 | 120 | Object v = database.get("vertex:" + String.valueOf(id)); 121 | 122 | if(v != null){ 123 | final Vertex vertex = new RedisVertex(id, this); 124 | return vertex; 125 | } 126 | 127 | return null; 128 | } catch (Exception e){ 129 | return null; 130 | } 131 | } 132 | 133 | @Override 134 | public void removeVertex(Vertex vertex) { 135 | if(this.doIndexing()) { 136 | this.indexManager.removeElement(vertex); 137 | } 138 | ((RedisElement)vertex).remove(); 139 | } 140 | 141 | @Override 142 | public Iterable getVertices() { 143 | return new RedisVertexIterable(this); 144 | } 145 | 146 | @Override 147 | public Edge addEdge(Object o, Vertex outVertex, Vertex inVertex, String s) { 148 | final Edge edge = new RedisEdge((RedisVertex)inVertex, (RedisVertex)outVertex, s, this); 149 | return edge; 150 | } 151 | 152 | @Override 153 | public Edge getEdge(Object o) { 154 | try{ 155 | Long id = getLong(o); 156 | 157 | Object e = database.get("edge:" + String.valueOf(id)); 158 | 159 | if(e != null) { 160 | final Edge edge = new RedisEdge(id, this); 161 | return edge; 162 | } 163 | return null; 164 | } catch(Exception e){ 165 | return null; 166 | } 167 | } 168 | 169 | @Override 170 | public void removeEdge(Edge edge) { 171 | if(this.doIndexing()) { 172 | this.indexManager.removeElement(edge); 173 | } 174 | ((RedisEdge) edge).remove(); 175 | } 176 | 177 | @Override 178 | public Iterable getEdges() { 179 | return new RedisEdgeIterable(this); 180 | } 181 | 182 | @Override 183 | public void clear() { 184 | try { 185 | database.flushdb(); 186 | if(this.doIndexing() && null != this.indexManager){ 187 | this.indexManager.clear(); 188 | // this is hackish in the following sense 189 | // if we've dropped automatic indices, *do not* recreate them on next start 190 | // we only automatically create indices for fresh databases 191 | this.database.set("indexing_set", 1); 192 | } 193 | } catch(RedisException e) { 194 | e.printStackTrace(); 195 | } 196 | } 197 | 198 | @Override 199 | public void shutdown() { 200 | database.quit(); 201 | } 202 | 203 | public static String getIdentifier(String prefix, Long id, String suffix) { 204 | String identifier = prefix + String.valueOf(id); 205 | if(suffix != null) identifier += ":" + suffix; 206 | 207 | return identifier; 208 | } 209 | 210 | public String toString() { 211 | try { 212 | Map info = this.database.info(); 213 | return "redis[" + info.get("redis_version") + "]"; 214 | } catch(RedisException e) { 215 | e.printStackTrace(); 216 | } 217 | return "redis[error retrieving info]"; 218 | } 219 | 220 | @Override 221 | public Index createManualIndex(final String indexName, final Class indexClass) { 222 | if(!this.doIndexing()){ 223 | return null; 224 | } 225 | return this.indexManager.createManualIndex(indexName, indexClass); 226 | } 227 | 228 | @Override 229 | public AutomaticIndex createAutomaticIndex(final String indexName, final Class indexClass, Set keys) { 230 | if(!this.doIndexing()){ 231 | return null; 232 | } 233 | return this.indexManager.createAutomaticIndex(indexName, indexClass, keys); 234 | } 235 | 236 | @Override 237 | public Index getIndex(final String indexName, final Class indexClass) { 238 | if(!this.doIndexing()){ 239 | return null; 240 | } 241 | return this.indexManager.getIndex(indexName, indexClass); 242 | } 243 | 244 | @Override 245 | public Iterable> getIndices() { 246 | if(!this.doIndexing()){ 247 | return new ArrayList>(); 248 | } 249 | return this.indexManager.getIndices(); 250 | } 251 | 252 | @Override 253 | public void dropIndex(final String indexName) { 254 | if(this.doIndexing()){ 255 | this.indexManager.dropIndex(indexName); 256 | } 257 | } 258 | 259 | protected Iterable getAutoIndices() { 260 | if(!this.doIndexing()){ 261 | return new ArrayList(); 262 | } 263 | return this.indexManager.getAutoIndices(); 264 | } 265 | 266 | protected Iterable getManualIndices() { 267 | if(!this.doIndexing()){ 268 | return new ArrayList(); 269 | } 270 | return this.indexManager.getManualIndices(); 271 | } 272 | 273 | private void prepareIndexing(){ 274 | 275 | this.indexManager.setGraph(this); 276 | this.indexManager.restoreIndices(); 277 | 278 | try { 279 | Object indexing_set = this.database.get("indexing_set"); 280 | if(null == indexing_set){ 281 | this.createAutomaticIndex(Index.VERTICES, RedisVertex.class, null); 282 | this.createAutomaticIndex(Index.EDGES, RedisEdge.class, null); 283 | } 284 | 285 | this.database.set("indexing_set", 1); 286 | } catch (RedisException e) { 287 | e.printStackTrace(); 288 | } 289 | } 290 | 291 | // see http://stackoverflow.com/questions/1302605/how-do-i-convert-from-int-to-long-in-java/2904999#2904999 292 | private final Long getLong(Object obj) throws IllegalArgumentException { 293 | Long rv; 294 | 295 | if((obj.getClass() == Integer.class) || (obj.getClass() == Long.class) || (obj.getClass() == Double.class)) { 296 | rv = Long.parseLong(obj.toString()); 297 | } else if((obj.getClass() == int.class) || (obj.getClass() == long.class) || (obj.getClass() == double.class)) { 298 | rv = (Long) obj; 299 | } else if(obj.getClass() == String.class) { 300 | rv = Long.parseLong(obj.toString()); 301 | } else { 302 | throw new IllegalArgumentException("getLong: type " + obj.getClass() + " = \"" + obj.toString() + "\" unaccounted for"); 303 | } 304 | 305 | return rv; 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /src/main/java/biz/source_code/base64Coder/Base64Coder.java: -------------------------------------------------------------------------------- 1 | // Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland 2 | // www.source-code.biz, www.inventec.ch/chdh 3 | // 4 | // This module is multi-licensed and may be used under the terms 5 | // of any of the following licenses: 6 | // 7 | // EPL, Eclipse Public License, http://www.eclipse.org/legal 8 | // LGPL, GNU Lesser General Public License, http://www.gnu.org/licenses/lgpl.html 9 | // AL, Apache License, http://www.apache.org/licenses 10 | // BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php 11 | // 12 | // Please contact the author if you need another license. 13 | // This module is provided "as is", without warranties of any kind. 14 | 15 | package biz.source_code.base64Coder; 16 | 17 | /** 18 | * A Base64 encoder/decoder. 19 | *

20 | *

21 | * This class is used to encode and decode data in Base64 format as described in RFC 1521. 22 | *

23 | *

24 | * Project home page: www.source-code.biz/base64coder/java
25 | * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
26 | * Multi-licensed: EPL / LGPL / AL / BSD. 27 | */ 28 | public class Base64Coder { 29 | 30 | // The line separator string of the operating system. 31 | private static final String systemLineSeparator = System.getProperty("line.separator"); 32 | 33 | // Mapping table from 6-bit nibbles to Base64 characters. 34 | private static char[] map1 = new char[64]; 35 | 36 | static { 37 | int i = 0; 38 | for(char c = 'A'; c <= 'Z'; c++) map1[i++] = c; 39 | for(char c = 'a'; c <= 'z'; c++) map1[i++] = c; 40 | for(char c = '0'; c <= '9'; c++) map1[i++] = c; 41 | map1[i++] = '+'; 42 | map1[i++] = '/'; 43 | } 44 | 45 | // Mapping table from Base64 characters to 6-bit nibbles. 46 | private static byte[] map2 = new byte[128]; 47 | 48 | static { 49 | for(int i = 0; i < map2.length; i++) map2[i] = -1; 50 | for(int i = 0; i < 64; i++) map2[map1[i]] = (byte) i; 51 | } 52 | 53 | /** 54 | * Encodes a string into Base64 format. 55 | * No blanks or line breaks are inserted. 56 | * 57 | * @param s A String to be encoded. 58 | * @return A String containing the Base64 encoded data. 59 | */ 60 | public static String encodeString(String s) { 61 | return new String(encode(s.getBytes())); 62 | } 63 | 64 | /** 65 | * Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters. 66 | * This method is compatible with sun.misc.BASE64Encoder.encodeBuffer(byte[]). 67 | * 68 | * @param in An array containing the data bytes to be encoded. 69 | * @return A String containing the Base64 encoded data, broken into lines. 70 | */ 71 | public static String encodeLines(byte[] in) { 72 | return encodeLines(in, 0, in.length, 76, systemLineSeparator); 73 | } 74 | 75 | /** 76 | * Encodes a byte array into Base 64 format and breaks the output into lines. 77 | * 78 | * @param in An array containing the data bytes to be encoded. 79 | * @param iOff Offset of the first byte in in to be processed. 80 | * @param iLen Number of bytes to be processed in in, starting at iOff. 81 | * @param lineLen Line length for the output data. Should be a multiple of 4. 82 | * @param lineSeparator The line separator to be used to separate the output lines. 83 | * @return A String containing the Base64 encoded data, broken into lines. 84 | */ 85 | public static String encodeLines(byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) { 86 | int blockLen = (lineLen * 3) / 4; 87 | if(blockLen <= 0) throw new IllegalArgumentException(); 88 | int lines = (iLen + blockLen - 1) / blockLen; 89 | int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length(); 90 | StringBuilder buf = new StringBuilder(bufLen); 91 | int ip = 0; 92 | while(ip < iLen) { 93 | int l = Math.min(iLen - ip, blockLen); 94 | buf.append(encode(in, iOff + ip, l)); 95 | buf.append(lineSeparator); 96 | ip += l; 97 | } 98 | return buf.toString(); 99 | } 100 | 101 | /** 102 | * Encodes a byte array into Base64 format. 103 | * No blanks or line breaks are inserted in the output. 104 | * 105 | * @param in An array containing the data bytes to be encoded. 106 | * @return A character array containing the Base64 encoded data. 107 | */ 108 | public static char[] encode(byte[] in) { 109 | return encode(in, 0, in.length); 110 | } 111 | 112 | /** 113 | * Encodes a byte array into Base64 format. 114 | * No blanks or line breaks are inserted in the output. 115 | * 116 | * @param in An array containing the data bytes to be encoded. 117 | * @param iLen Number of bytes to process in in. 118 | * @return A character array containing the Base64 encoded data. 119 | */ 120 | public static char[] encode(byte[] in, int iLen) { 121 | return encode(in, 0, iLen); 122 | } 123 | 124 | /** 125 | * Encodes a byte array into Base64 format. 126 | * No blanks or line breaks are inserted in the output. 127 | * 128 | * @param in An array containing the data bytes to be encoded. 129 | * @param iOff Offset of the first byte in in to be processed. 130 | * @param iLen Number of bytes to process in in, starting at iOff. 131 | * @return A character array containing the Base64 encoded data. 132 | */ 133 | public static char[] encode(byte[] in, int iOff, int iLen) { 134 | int oDataLen = (iLen * 4 + 2) / 3; // output length without padding 135 | int oLen = ((iLen + 2) / 3) * 4; // output length including padding 136 | char[] out = new char[oLen]; 137 | int ip = iOff; 138 | int iEnd = iOff + iLen; 139 | int op = 0; 140 | while(ip < iEnd) { 141 | int i0 = in[ip++] & 0xff; 142 | int i1 = ip < iEnd ? in[ip++] & 0xff : 0; 143 | int i2 = ip < iEnd ? in[ip++] & 0xff : 0; 144 | int o0 = i0 >>> 2; 145 | int o1 = ((i0 & 3) << 4) | (i1 >>> 4); 146 | int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); 147 | int o3 = i2 & 0x3F; 148 | out[op++] = map1[o0]; 149 | out[op++] = map1[o1]; 150 | out[op] = op < oDataLen ? map1[o2] : '='; 151 | op++; 152 | out[op] = op < oDataLen ? map1[o3] : '='; 153 | op++; 154 | } 155 | return out; 156 | } 157 | 158 | /** 159 | * Decodes a string from Base64 format. 160 | * No blanks or line breaks are allowed within the Base64 encoded input data. 161 | * 162 | * @param s A Base64 String to be decoded. 163 | * @return A String containing the decoded data. 164 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 165 | */ 166 | public static String decodeString(String s) { 167 | return new String(decode(s)); 168 | } 169 | 170 | /** 171 | * Decodes a byte array from Base64 format and ignores line separators, tabs and blanks. 172 | * CR, LF, Tab and Space characters are ignored in the input data. 173 | * This method is compatible with sun.misc.BASE64Decoder.decodeBuffer(String). 174 | * 175 | * @param s A Base64 String to be decoded. 176 | * @return An array containing the decoded data bytes. 177 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 178 | */ 179 | public static byte[] decodeLines(String s) { 180 | char[] buf = new char[s.length()]; 181 | int p = 0; 182 | for(int ip = 0; ip < s.length(); ip++) { 183 | char c = s.charAt(ip); 184 | if(c != ' ' && c != '\r' && c != '\n' && c != '\t') buf[p++] = c; 185 | } 186 | return decode(buf, 0, p); 187 | } 188 | 189 | /** 190 | * Decodes a byte array from Base64 format. 191 | * No blanks or line breaks are allowed within the Base64 encoded input data. 192 | * 193 | * @param s A Base64 String to be decoded. 194 | * @return An array containing the decoded data bytes. 195 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 196 | */ 197 | public static byte[] decode(String s) { 198 | return decode(s.toCharArray()); 199 | } 200 | 201 | /** 202 | * Decodes a byte array from Base64 format. 203 | * No blanks or line breaks are allowed within the Base64 encoded input data. 204 | * 205 | * @param in A character array containing the Base64 encoded data. 206 | * @return An array containing the decoded data bytes. 207 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 208 | */ 209 | public static byte[] decode(char[] in) { 210 | return decode(in, 0, in.length); 211 | } 212 | 213 | /** 214 | * Decodes a byte array from Base64 format. 215 | * No blanks or line breaks are allowed within the Base64 encoded input data. 216 | * 217 | * @param in A character array containing the Base64 encoded data. 218 | * @param iOff Offset of the first character in in to be processed. 219 | * @param iLen Number of characters to process in in, starting at iOff. 220 | * @return An array containing the decoded data bytes. 221 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 222 | */ 223 | public static byte[] decode(char[] in, int iOff, int iLen) { 224 | if(iLen % 4 != 0) throw new IllegalArgumentException("Length of Base64 encoded input string is not a multiple of 4."); 225 | while(iLen > 0 && in[iOff + iLen - 1] == '=') iLen--; 226 | int oLen = (iLen * 3) / 4; 227 | byte[] out = new byte[oLen]; 228 | int ip = iOff; 229 | int iEnd = iOff + iLen; 230 | int op = 0; 231 | while(ip < iEnd) { 232 | int i0 = in[ip++]; 233 | int i1 = in[ip++]; 234 | int i2 = ip < iEnd ? in[ip++] : 'A'; 235 | int i3 = ip < iEnd ? in[ip++] : 'A'; 236 | if(i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) 237 | throw new IllegalArgumentException("Illegal character in Base64 encoded data."); 238 | int b0 = map2[i0]; 239 | int b1 = map2[i1]; 240 | int b2 = map2[i2]; 241 | int b3 = map2[i3]; 242 | if(b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) 243 | throw new IllegalArgumentException("Illegal character in Base64 encoded data."); 244 | int o0 = (b0 << 2) | (b1 >>> 4); 245 | int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); 246 | int o2 = ((b2 & 3) << 6) | b3; 247 | out[op++] = (byte) o0; 248 | if(op < oLen) out[op++] = (byte) o1; 249 | if(op < oLen) out[op++] = (byte) o2; 250 | } 251 | return out; 252 | } 253 | 254 | // Dummy constructor. 255 | 256 | private Base64Coder() { 257 | } 258 | 259 | } // end class Base64Coder -------------------------------------------------------------------------------- /src/main/java/com/tinkerpop/blueprints/pgm/impls/blueredis/utils/Base64Coder.java: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2010-2011. Dmitrii Dimandt * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | ******************************************************************************/ 16 | 17 | package com.tinkerpop.blueprints.pgm.impls.blueredis.utils; 18 | 19 | /** 20 | * A Base64 encoder/decoder. 21 | *

22 | *

23 | * This class is used to encode and decode data in Base64 format as described in RFC 1521. 24 | *

25 | *

26 | * Project home page: www.source-code.biz/base64coder/java
27 | * Author: Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland
28 | * Multi-licensed: EPL / LGPL / AL / BSD. 29 | */ 30 | public class Base64Coder { 31 | 32 | // The line separator string of the operating system. 33 | private static final String systemLineSeparator = System.getProperty("line.separator"); 34 | 35 | // Mapping table from 6-bit nibbles to Base64 characters. 36 | private static char[] map1 = new char[64]; 37 | 38 | static { 39 | int i = 0; 40 | for(char c = 'A'; c <= 'Z'; c++) map1[i++] = c; 41 | for(char c = 'a'; c <= 'z'; c++) map1[i++] = c; 42 | for(char c = '0'; c <= '9'; c++) map1[i++] = c; 43 | map1[i++] = '+'; 44 | map1[i++] = '/'; 45 | } 46 | 47 | // Mapping table from Base64 characters to 6-bit nibbles. 48 | private static byte[] map2 = new byte[128]; 49 | 50 | static { 51 | for(int i = 0; i < map2.length; i++) map2[i] = -1; 52 | for(int i = 0; i < 64; i++) map2[map1[i]] = (byte) i; 53 | } 54 | 55 | /** 56 | * Encodes a string into Base64 format. 57 | * No blanks or line breaks are inserted. 58 | * 59 | * @param s A String to be encoded. 60 | * @return A String containing the Base64 encoded data. 61 | */ 62 | public static String encodeString(String s) { 63 | return new String(encode(s.getBytes())); 64 | } 65 | 66 | /** 67 | * Encodes a byte array into Base 64 format and breaks the output into lines of 76 characters. 68 | * This method is compatible with sun.misc.BASE64Encoder.encodeBuffer(byte[]). 69 | * 70 | * @param in An array containing the data bytes to be encoded. 71 | * @return A String containing the Base64 encoded data, broken into lines. 72 | */ 73 | public static String encodeLines(byte[] in) { 74 | return encodeLines(in, 0, in.length, 76, systemLineSeparator); 75 | } 76 | 77 | /** 78 | * Encodes a byte array into Base 64 format and breaks the output into lines. 79 | * 80 | * @param in An array containing the data bytes to be encoded. 81 | * @param iOff Offset of the first byte in in to be processed. 82 | * @param iLen Number of bytes to be processed in in, starting at iOff. 83 | * @param lineLen Line length for the output data. Should be a multiple of 4. 84 | * @param lineSeparator The line separator to be used to separate the output lines. 85 | * @return A String containing the Base64 encoded data, broken into lines. 86 | */ 87 | public static String encodeLines(byte[] in, int iOff, int iLen, int lineLen, String lineSeparator) { 88 | int blockLen = (lineLen * 3) / 4; 89 | if(blockLen <= 0) throw new IllegalArgumentException(); 90 | int lines = (iLen + blockLen - 1) / blockLen; 91 | int bufLen = ((iLen + 2) / 3) * 4 + lines * lineSeparator.length(); 92 | StringBuilder buf = new StringBuilder(bufLen); 93 | int ip = 0; 94 | while(ip < iLen) { 95 | int l = Math.min(iLen - ip, blockLen); 96 | buf.append(encode(in, iOff + ip, l)); 97 | buf.append(lineSeparator); 98 | ip += l; 99 | } 100 | return buf.toString(); 101 | } 102 | 103 | /** 104 | * Encodes a byte array into Base64 format. 105 | * No blanks or line breaks are inserted in the output. 106 | * 107 | * @param in An array containing the data bytes to be encoded. 108 | * @return A character array containing the Base64 encoded data. 109 | */ 110 | public static char[] encode(byte[] in) { 111 | return encode(in, 0, in.length); 112 | } 113 | 114 | /** 115 | * Encodes a byte array into Base64 format. 116 | * No blanks or line breaks are inserted in the output. 117 | * 118 | * @param in An array containing the data bytes to be encoded. 119 | * @param iLen Number of bytes to process in in. 120 | * @return A character array containing the Base64 encoded data. 121 | */ 122 | public static char[] encode(byte[] in, int iLen) { 123 | return encode(in, 0, iLen); 124 | } 125 | 126 | /** 127 | * Encodes a byte array into Base64 format. 128 | * No blanks or line breaks are inserted in the output. 129 | * 130 | * @param in An array containing the data bytes to be encoded. 131 | * @param iOff Offset of the first byte in in to be processed. 132 | * @param iLen Number of bytes to process in in, starting at iOff. 133 | * @return A character array containing the Base64 encoded data. 134 | */ 135 | public static char[] encode(byte[] in, int iOff, int iLen) { 136 | int oDataLen = (iLen * 4 + 2) / 3; // output length without padding 137 | int oLen = ((iLen + 2) / 3) * 4; // output length including padding 138 | char[] out = new char[oLen]; 139 | int ip = iOff; 140 | int iEnd = iOff + iLen; 141 | int op = 0; 142 | while(ip < iEnd) { 143 | int i0 = in[ip++] & 0xff; 144 | int i1 = ip < iEnd ? in[ip++] & 0xff : 0; 145 | int i2 = ip < iEnd ? in[ip++] & 0xff : 0; 146 | int o0 = i0 >>> 2; 147 | int o1 = ((i0 & 3) << 4) | (i1 >>> 4); 148 | int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); 149 | int o3 = i2 & 0x3F; 150 | out[op++] = map1[o0]; 151 | out[op++] = map1[o1]; 152 | out[op] = op < oDataLen ? map1[o2] : '='; 153 | op++; 154 | out[op] = op < oDataLen ? map1[o3] : '='; 155 | op++; 156 | } 157 | return out; 158 | } 159 | 160 | /** 161 | * Decodes a string from Base64 format. 162 | * No blanks or line breaks are allowed within the Base64 encoded input data. 163 | * 164 | * @param s A Base64 String to be decoded. 165 | * @return A String containing the decoded data. 166 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 167 | */ 168 | public static String decodeString(String s) { 169 | return new String(decode(s)); 170 | } 171 | 172 | /** 173 | * Decodes a byte array from Base64 format and ignores line separators, tabs and blanks. 174 | * CR, LF, Tab and Space characters are ignored in the input data. 175 | * This method is compatible with sun.misc.BASE64Decoder.decodeBuffer(String). 176 | * 177 | * @param s A Base64 String to be decoded. 178 | * @return An array containing the decoded data bytes. 179 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 180 | */ 181 | public static byte[] decodeLines(String s) { 182 | char[] buf = new char[s.length()]; 183 | int p = 0; 184 | for(int ip = 0; ip < s.length(); ip++) { 185 | char c = s.charAt(ip); 186 | if(c != ' ' && c != '\r' && c != '\n' && c != '\t') buf[p++] = c; 187 | } 188 | return decode(buf, 0, p); 189 | } 190 | 191 | /** 192 | * Decodes a byte array from Base64 format. 193 | * No blanks or line breaks are allowed within the Base64 encoded input data. 194 | * 195 | * @param s A Base64 String to be decoded. 196 | * @return An array containing the decoded data bytes. 197 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 198 | */ 199 | public static byte[] decode(String s) { 200 | return decode(s.toCharArray()); 201 | } 202 | 203 | /** 204 | * Decodes a byte array from Base64 format. 205 | * No blanks or line breaks are allowed within the Base64 encoded input data. 206 | * 207 | * @param in A character array containing the Base64 encoded data. 208 | * @return An array containing the decoded data bytes. 209 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 210 | */ 211 | public static byte[] decode(char[] in) { 212 | return decode(in, 0, in.length); 213 | } 214 | 215 | /** 216 | * Decodes a byte array from Base64 format. 217 | * No blanks or line breaks are allowed within the Base64 encoded input data. 218 | * 219 | * @param in A character array containing the Base64 encoded data. 220 | * @param iOff Offset of the first character in in to be processed. 221 | * @param iLen Number of characters to process in in, starting at iOff. 222 | * @return An array containing the decoded data bytes. 223 | * @throws IllegalArgumentException If the input is not valid Base64 encoded data. 224 | */ 225 | public static byte[] decode(char[] in, int iOff, int iLen) { 226 | if(iLen % 4 != 0) throw new IllegalArgumentException("Length of Base64 encoded input string is not a multiple of 4."); 227 | while(iLen > 0 && in[iOff + iLen - 1] == '=') iLen--; 228 | int oLen = (iLen * 3) / 4; 229 | byte[] out = new byte[oLen]; 230 | int ip = iOff; 231 | int iEnd = iOff + iLen; 232 | int op = 0; 233 | while(ip < iEnd) { 234 | int i0 = in[ip++]; 235 | int i1 = in[ip++]; 236 | int i2 = ip < iEnd ? in[ip++] : 'A'; 237 | int i3 = ip < iEnd ? in[ip++] : 'A'; 238 | if(i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) 239 | throw new IllegalArgumentException("Illegal character in Base64 encoded data."); 240 | int b0 = map2[i0]; 241 | int b1 = map2[i1]; 242 | int b2 = map2[i2]; 243 | int b3 = map2[i3]; 244 | if(b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) 245 | throw new IllegalArgumentException("Illegal character in Base64 encoded data."); 246 | int o0 = (b0 << 2) | (b1 >>> 4); 247 | int o1 = ((b1 & 0xf) << 4) | (b2 >>> 2); 248 | int o2 = ((b2 & 3) << 6) | b3; 249 | out[op++] = (byte) o0; 250 | if(op < oLen) out[op++] = (byte) o1; 251 | if(op < oLen) out[op++] = (byte) o2; 252 | } 253 | return out; 254 | } 255 | 256 | // Dummy constructor. 257 | 258 | private Base64Coder() { 259 | } 260 | 261 | } // end class Base64Coder --------------------------------------------------------------------------------