├── .gitignore ├── pom.xml └── src ├── main └── java │ └── fr │ └── pilato │ └── hibernate │ └── plugins │ └── elasticsearch │ ├── ElasticSearchEventListener.java │ ├── ElasticSearchEventListenerFactory.java │ ├── ElasticSearchHelper.java │ ├── ElasticSearchHibernateAnnotationIntrospector.java │ ├── ElasticSearchJacksonHibernateModule.java │ └── annotations │ ├── ESAnalyzer.java │ └── ESIndexed.java └── test ├── java └── fr │ └── pilato │ └── hibernate │ └── plugins │ └── elasticsearch │ ├── HibernateTestCase.java │ ├── JSonBuilderTest.java │ └── testcase1 │ ├── ChildEntity.java │ ├── EntityMaker.java │ └── SimpleEntity.java └── resources ├── log4j.xml └── myconfig └── myelasticsearch.yml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .classpath 3 | .settings 4 | .project 5 | 6 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | fr.pilato.hibernate.plugins 5 | es-hibernate-connector 6 | 0.0.1-SNAPSHOT 7 | jar 8 | Hibernate to ElasticSearch Connector 9 | Make your Hibernate Search more Elastic ! 10 | 11 | https://github.com/dadoonet/es-hibernate-connector/wiki 12 | 13 | 14 | 15 | The Apache Software License, Version 2.0 16 | http://www.apache.org/licenses/LICENSE-2.0.txt 17 | repo 18 | 19 | 20 | 21 | 22 | git://github.com/dadoonet/es-hibernate-connector.git 23 | git@github.com:dadoonet/es-hibernate-connector.git 24 | https://github.com/dadoonet/es-hibernate-connector 25 | 26 | 27 | 2011 28 | 29 | 30 | dadoonet 31 | David Pilato 32 | david+github@pilato.fr 33 | 34 | 35 | 36 | 37 | 0.16.0 38 | 3.6.1.Final 39 | 1.8.0 40 | 41 | 42 | 43 | 44 | dev-pilato-fr 45 | http://dev.david.pilato.fr/projects/es-hibernate-connector/${project.version}/ 46 | 47 | 48 | sonatype-releases 49 | OSS Sonatype Releases 50 | https://oss.sonatype.org/content/repositories/releases/ 51 | 52 | 53 | sonatype-snapshots 54 | OSS Sonatype Snapshots 55 | https://oss.sonatype.org/content/repositories/snapshots/ 56 | 57 | 58 | 59 | 60 | github 61 | https://github.com/dadoonet/es-hibernate-connector/issues/ 62 | 63 | 64 | 65 | 66 | 67 | 68 | maven-help-plugin 69 | 2.1.1 70 | 71 | 72 | maven-antrun-plugin 73 | 1.6 74 | 75 | 76 | maven-changes-plugin 77 | 2.4 78 | 79 | 80 | maven-project-info-reports-plugin 81 | 2.3.1 82 | 83 | 84 | maven-assembly-plugin 85 | 2.2.1 86 | 87 | 88 | maven-clean-plugin 89 | 2.4.1 90 | 91 | 92 | maven-compiler-plugin 93 | 2.3.2 94 | 95 | true 96 | true 97 | 1.6 98 | 1.6 99 | 100 | 101 | 102 | maven-dependency-plugin 103 | 2.2 104 | 105 | 106 | maven-deploy-plugin 107 | 2.5 108 | 109 | 110 | maven-ear-plugin 111 | 2.5 112 | 113 | 114 | maven-ejb-plugin 115 | 2.3 116 | 117 | 118 | maven-install-plugin 119 | 2.3.1 120 | 121 | 122 | maven-jar-plugin 123 | 2.3.1 124 | 125 | 126 | maven-plugin-plugin 127 | 2.7 128 | 129 | 130 | maven-rar-plugin 131 | 2.2 132 | 133 | 134 | maven-release-plugin 135 | 2.1 136 | 137 | 138 | maven-resources-plugin 139 | 2.5 140 | 141 | 142 | maven-site-plugin 143 | 2.2 144 | 145 | 146 | maven-pdf-plugin 147 | 1.1 148 | 149 | 150 | com.artofsolving 151 | jodconverter-maven-plugin 152 | 2.2.3 153 | 154 | 155 | maven-source-plugin 156 | 2.1.2 157 | 158 | 159 | maven-surefire-plugin 160 | 2.8 161 | 162 | 163 | maven-war-plugin 164 | 2.1.1 165 | 166 | 167 | maven-eclipse-plugin 168 | 2.8 169 | 170 | 171 | org.codehaus.mojo 172 | sonar-maven-plugin 173 | 1.0-beta-2 174 | 175 | 176 | org.apache.maven.plugins 177 | maven-enforcer-plugin 178 | 1.0 179 | 180 | 181 | validate 182 | 183 | enforce 184 | 185 | 186 | 187 | 188 | [1.6,) 189 | 190 | 191 | [2.1.0,) 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | src/test/resources 204 | true 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | sonatype-releases 213 | Miroir Sonatype 214 | default 215 | https://oss.sonatype.org/content/repositories/releases/ 216 | 217 | false 218 | 219 | 220 | 221 | jboss-releases 222 | JBoss Repo 223 | default 224 | https://repository.jboss.org/nexus/content/groups/public/ 225 | 226 | false 227 | 228 | 229 | 230 | 231 | 232 | 233 | org.elasticsearch 234 | elasticsearch 235 | ${elasticsearch.version} 236 | 237 | 238 | org.hibernate 239 | hibernate-core 240 | ${hibernate.version} 241 | 242 | 243 | org.hibernate 244 | hibernate-entitymanager 245 | ${hibernate.version} 246 | 247 | 248 | org.codehaus.jackson 249 | jackson-mapper-asl 250 | ${jackson.version} 251 | 252 | 253 | commons-logging 254 | commons-logging 255 | 1.1.1 256 | 257 | 258 | commons-beanutils 259 | commons-beanutils 260 | 1.8.3 261 | 262 | 263 | log4j 264 | log4j 265 | 1.2.13 266 | provided 267 | 268 | 269 | junit 270 | junit 271 | 4.4 272 | test 273 | 274 | 275 | org.hsqldb 276 | hsqldb 277 | 2.0.0 278 | test 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/hibernate/plugins/elasticsearch/ElasticSearchEventListener.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch; 2 | 3 | import static org.elasticsearch.node.NodeBuilder.nodeBuilder; 4 | 5 | import java.util.HashMap; 6 | import java.util.Iterator; 7 | import java.util.Map; 8 | 9 | import javax.persistence.Id; 10 | 11 | import org.apache.commons.logging.Log; 12 | import org.apache.commons.logging.LogFactory; 13 | import org.elasticsearch.client.Client; 14 | import org.elasticsearch.node.Node; 15 | import org.elasticsearch.node.NodeBuilder; 16 | import org.hibernate.cfg.Configuration; 17 | import org.hibernate.event.Destructible; 18 | import org.hibernate.event.Initializable; 19 | import org.hibernate.event.PostDeleteEvent; 20 | import org.hibernate.event.PostDeleteEventListener; 21 | import org.hibernate.event.PostInsertEvent; 22 | import org.hibernate.event.PostInsertEventListener; 23 | import org.hibernate.event.PostUpdateEvent; 24 | import org.hibernate.event.PostUpdateEventListener; 25 | import org.hibernate.mapping.PersistentClass; 26 | import org.hibernate.util.ReflectHelper; 27 | 28 | import fr.pilato.hibernate.plugins.elasticsearch.annotations.ESIndexed; 29 | 30 | /** 31 | * ElasticSearch Hibernate Listener implementation : 32 | *
Supported Annotations (and properties) : 33 | * 38 | * Set es.client.only system property to false if you want to start a real node (for tests)
39 | * Use es.config system property to define your node configuration or add a config 40 | * file in the classpath 41 | * @see Indexed 42 | * @see DocumentId 43 | * @see Id 44 | * @author David Pilato 45 | */ 46 | @SuppressWarnings("serial") 47 | public class ElasticSearchEventListener implements Initializable, 48 | PostDeleteEventListener, 49 | PostInsertEventListener, 50 | PostUpdateEventListener, Destructible 51 | { 52 | 53 | private static final Log log = LogFactory.getLog(ElasticSearchEventListener.class); 54 | 55 | private Node node = null; 56 | 57 | private Client client = null; 58 | 59 | private Map> entityMapper = new HashMap>(); 60 | 61 | private boolean used = false; 62 | 63 | public void initialize(Configuration cfg) { 64 | // We need to configure ES to handle HSearch annotations 65 | if (log.isDebugEnabled()) log.debug( "Elastic Search Starting Configuration" ); 66 | 67 | try { 68 | if (log.isDebugEnabled()) log.debug( "ES Indexed classes :" ); 69 | for (Iterator itMappings = cfg.getClassMappings(); itMappings.hasNext();) { 70 | PersistentClass persistentClass = itMappings.next(); 71 | 72 | // Looking if the Entity is Indexed 73 | Class clazz = ReflectHelper.classForName( persistentClass.getEntityName() ); 74 | boolean isIndexed = isEntityIndexed(clazz); 75 | 76 | if (isIndexed) { 77 | used = true; 78 | log.debug(" + " + clazz.getSimpleName()); 79 | registerEntityHolder(clazz); 80 | } 81 | } 82 | } catch (ClassNotFoundException e) { 83 | // It should not be possible to get here ! 84 | log.warn( "Can not find Entity Class : " + e.getMessage() ); 85 | } 86 | 87 | if (log.isDebugEnabled()) log.debug( "Elastic Search Event Listener " + (used ? "activated" : "deactivated") ); 88 | } 89 | 90 | /** 91 | * Starting cluster connexion 92 | */ 93 | public ElasticSearchEventListener() { 94 | log.info( "Starting Elastic Search Plugin..." ); 95 | boolean isClientOnly = true; 96 | 97 | // Let's find in system properties if we must start the client 98 | // as a Node also (for testing purpose) 99 | String strIsClientOnly = System.getProperty("es.client.only"); 100 | if (strIsClientOnly != null && strIsClientOnly.toLowerCase().equals("false")) isClientOnly = false; 101 | 102 | log.info( "Starting Node " + (isClientOnly ? "(Client Only)" : "(Full)") + " for Elastic Search..." ); 103 | 104 | NodeBuilder nodeBuilder = nodeBuilder().client(isClientOnly); 105 | node = nodeBuilder.node(); 106 | client = node.client(); 107 | 108 | log.info( "Node [" + node.settings().get("name") + "] for [" + node.settings().get("cluster.name") + "] cluster started..." ); 109 | log.info( " - data : " + node.settings().get("path.data") ); 110 | log.info( " - logs : " + node.settings().get("path.logs") ); 111 | } 112 | 113 | public boolean isUsed() { 114 | return used; 115 | } 116 | 117 | public void onPostDelete(PostDeleteEvent event) { 118 | final Object entity = event.getEntity(); 119 | if (isEntityIndexed(entity)) { 120 | if (log.isDebugEnabled()) log.debug("Processing Delete event on " + getEntityName(entity)); 121 | ElasticSearchHelper.removeElastic(client, entity); 122 | } 123 | } 124 | 125 | public void onPostInsert(PostInsertEvent event) { 126 | final Object entity = event.getEntity(); 127 | if (isEntityIndexed(entity)) { 128 | if (log.isDebugEnabled()) log.debug("Processing Insert event on " + getEntityName(entity)); 129 | ElasticSearchHelper.pushElastic(client, entity); 130 | } 131 | } 132 | 133 | public void onPostUpdate(PostUpdateEvent event) { 134 | final Object entity = event.getEntity(); 135 | if (isEntityIndexed(entity)) { 136 | if (log.isDebugEnabled()) log.debug("Processing Insert event on " + getEntityName(entity)); 137 | ElasticSearchHelper.pushElastic(client, entity); 138 | } 139 | } 140 | 141 | private void registerEntityHolder(Class clazz) { 142 | entityMapper.put(clazz.getName(), clazz); 143 | } 144 | 145 | private boolean isEntityIndexed(final Object entity) { 146 | // Can we find the class for this entity in ou Map ? 147 | if (entity == null) return false; 148 | String className = entity.getClass().getName(); 149 | 150 | return entityMapper.containsKey(className); 151 | } 152 | 153 | /** 154 | * Find if the Entity Class is Indexed 155 | * @param clazz 156 | * @return true if true ( ;-) ) 157 | */ 158 | private boolean isEntityIndexed(Class clazz) { 159 | ESIndexed annotation = (ESIndexed) clazz.getAnnotation(ESIndexed.class); 160 | if (annotation != null) { 161 | return true; 162 | } 163 | return false; 164 | } 165 | 166 | private String getEntityName(Object entity) { 167 | if (entity == null) return null; 168 | return entity.getClass().getSimpleName(); 169 | } 170 | 171 | public void cleanup() { 172 | log.info("Closing Elastic Search Plugin..."); 173 | if (client != null) client.close(); 174 | if (node != null) node.close(); 175 | } 176 | 177 | /** 178 | * Get the Elastic Search current node. 179 | *
If you only need a client, please use {@link #getESClient()} as 180 | * it will managed automatically by es-hb-connector. 181 | * @return Current ES node 182 | */ 183 | public Node getESNode() { 184 | return node; 185 | } 186 | 187 | /** 188 | * Get the Elastic Search current client 189 | * @return an ES client 190 | */ 191 | public Client getESClient() { 192 | return node.client(); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/hibernate/plugins/elasticsearch/ElasticSearchEventListenerFactory.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch; 2 | 3 | import org.apache.commons.logging.Log; 4 | import org.apache.commons.logging.LogFactory; 5 | import org.hibernate.cfg.Configuration; 6 | import org.hibernate.event.Destructible; 7 | import org.hibernate.event.Initializable; 8 | import org.hibernate.event.PostDeleteEvent; 9 | import org.hibernate.event.PostDeleteEventListener; 10 | import org.hibernate.event.PostInsertEvent; 11 | import org.hibernate.event.PostInsertEventListener; 12 | import org.hibernate.event.PostUpdateEvent; 13 | import org.hibernate.event.PostUpdateEventListener; 14 | 15 | /** 16 | * ElasticSearch Hibernate Listener implementation
17 | * You can configure elasticSearch by specifying es.config file. 18 | * @author David Pilato 19 | */ 20 | @SuppressWarnings("serial") 21 | public class ElasticSearchEventListenerFactory implements 22 | PostDeleteEventListener, 23 | PostInsertEventListener, 24 | PostUpdateEventListener, 25 | Initializable, 26 | Destructible { 27 | 28 | private static final Log log = LogFactory.getLog(ElasticSearchEventListenerFactory.class); 29 | 30 | private static ElasticSearchEventListener listener; 31 | 32 | public static ElasticSearchEventListener getInstance() { 33 | if (listener == null) { 34 | listener = new ElasticSearchEventListener(); 35 | } 36 | 37 | return listener; 38 | } 39 | 40 | public void initialize(Configuration cfg) { 41 | // The first time we get here, listener is null ! 42 | if (listener == null) { 43 | getInstance().initialize(cfg); 44 | } 45 | } 46 | 47 | public void onPostDelete(PostDeleteEvent event) { 48 | getInstance(); 49 | if ( listener.isUsed() ) listener.onPostDelete(event); 50 | } 51 | 52 | public void onPostInsert(PostInsertEvent event) { 53 | getInstance(); 54 | if ( listener.isUsed() ) listener.onPostInsert(event); 55 | } 56 | 57 | public void onPostUpdate(PostUpdateEvent event) { 58 | getInstance(); 59 | if ( listener.isUsed() ) listener.onPostUpdate(event); 60 | } 61 | 62 | public void cleanup() { 63 | if (listener != null) { 64 | if (log.isDebugEnabled()) log.debug("Stopping Elastic Search Event Listener"); 65 | listener.cleanup(); 66 | listener = null; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/hibernate/plugins/elasticsearch/ElasticSearchHelper.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.lang.reflect.Field; 5 | 6 | import javax.persistence.Id; 7 | 8 | import org.apache.commons.beanutils.PropertyUtils; 9 | import org.apache.commons.logging.Log; 10 | import org.apache.commons.logging.LogFactory; 11 | import org.codehaus.jackson.map.ObjectMapper; 12 | import org.codehaus.jackson.map.SerializationConfig; 13 | import org.elasticsearch.action.delete.DeleteResponse; 14 | import org.elasticsearch.action.index.IndexResponse; 15 | import org.elasticsearch.client.Client; 16 | 17 | import fr.pilato.hibernate.plugins.elasticsearch.annotations.ESIndexed; 18 | 19 | /** 20 | * ElasticSearch Hibernate Listener implementation :
21 | * Supported Annotations (and properties) : 22 | *
    23 | *
  • ESIndexed(indexName=) 24 | *
25 | * 26 | * @see ESIndexed 27 | * @author David Pilato 28 | */ 29 | public class ElasticSearchHelper { 30 | 31 | private static final Log log = LogFactory.getLog(ElasticSearchHelper.class); 32 | 33 | /** 34 | * Push an entity to Elastic 35 | * @param client Elastic Search Client 36 | * @param entity Entity to remove 37 | */ 38 | public static void pushElastic(Client client, Object entity) { 39 | if (entity == null) 40 | throw new RuntimeException( 41 | "Trying to index a null entity ? What a strange idea !"); 42 | if (client == null) { 43 | log.error("Trying to push index to a non existing client ? Bad idea !"); 44 | return; 45 | } 46 | 47 | String indexName = getEntityIndexName(entity); 48 | String entityName = getEntityName(entity); 49 | String documentId = getDocumentId(entity); 50 | 51 | if (log.isDebugEnabled()) 52 | log.debug("Trying to prepare EntityBuilder for " + indexName + "/" 53 | + entityName + "/" + documentId); 54 | 55 | ObjectMapper mapper = new ObjectMapper(); 56 | 57 | mapper.registerModule(new ElasticSearchJacksonHibernateModule()); 58 | 59 | mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, true); 60 | mapper.configure(SerializationConfig.Feature.AUTO_DETECT_FIELDS, true); 61 | mapper.configure(SerializationConfig.Feature.AUTO_DETECT_GETTERS, false); 62 | mapper.configure(SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS, false); 63 | mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, false); 64 | 65 | try { 66 | String result = mapper.writeValueAsString(entity); 67 | if (log.isDebugEnabled()) 68 | log.debug(" - Entity will be indexed as " 69 | + result); 70 | 71 | IndexResponse response = client 72 | .prepareIndex(indexName, entityName, documentId) 73 | .setSource(result).execute().actionGet(); 74 | 75 | if (log.isDebugEnabled() && response != null) 76 | log.debug(" - Entity succesfully indexed..."); 77 | if (response == null) 78 | log.warn("Unable to index entity " + entityName + " : " + entity); 79 | } catch (Exception e) { 80 | log.warn("Unable to push entity into Elastic Search..."); 81 | } 82 | } 83 | 84 | /** 85 | * Remove an entity from Elastic 86 | * @param client Elastic Search Client 87 | * @param entity Entity to remove 88 | */ 89 | public static void removeElastic(Client client, Object entity) { 90 | if (entity == null) 91 | throw new RuntimeException( 92 | "Trying to remove a null entity ? What a strange idea !"); 93 | if (client == null) { 94 | log.error("Trying to remove an index to a non existing client ? Bad idea !"); 95 | return; 96 | } 97 | 98 | String indexName = getEntityIndexName(entity); 99 | String entityName = getEntityName(entity); 100 | String documentId = getDocumentId(entity); 101 | 102 | if (log.isDebugEnabled()) 103 | log.debug("Trying to remove entity " + indexName + "/" + entityName 104 | + "/" + documentId); 105 | DeleteResponse response = client 106 | .prepareDelete(indexName, entityName, documentId).execute() 107 | .actionGet(); 108 | 109 | // Just for debugging purpose 110 | if (log.isDebugEnabled() && response != null) { 111 | if (response.isNotFound()) 112 | log.debug(" - Entity was not found..."); 113 | else 114 | log.debug(" - Entity successfully removed..."); 115 | } 116 | if (response == null) 117 | log.warn("Unable to remove entity " + entityName + " : " + entity); 118 | } 119 | 120 | private static Object getMemberValue(Object bean, String field) { 121 | Object value; 122 | try { 123 | value = PropertyUtils.getNestedProperty(bean, field); 124 | } catch (Exception e) { 125 | throw new IllegalStateException("Could not get property value", e); 126 | } 127 | return value; 128 | } 129 | 130 | /** 131 | * Shortcut to entity.getClass().getSimpleName() 132 | * @param entity Entity 133 | * @return simple Name for entity class 134 | */ 135 | private static String getEntityName(Object entity) { 136 | if (entity == null) 137 | return null; 138 | return entity.getClass().getSimpleName(); 139 | } 140 | 141 | /** 142 | * Get the index name as it was declared in ESIndexed annotation 143 | * for a given entity 144 | * @param entity Entity where to find index name 145 | * @return The index name 146 | */ 147 | private static String getEntityIndexName(Object entity) { 148 | Class clazz = entity.getClass(); 149 | ESIndexed annotation = clazz.getAnnotation(ESIndexed.class); 150 | 151 | if (annotation != null) { 152 | return annotation.indexName().toLowerCase(); 153 | } else { 154 | // Entity not annoted ! What the hell ???? 155 | throw new RuntimeException("Entity is not annoted ! " + entity); 156 | } 157 | } 158 | 159 | /** 160 | * Get the document id 161 | * 162 | * @param entity 163 | * Entity 164 | * @return 165 | */ 166 | private static String getDocumentId(Object entity) { 167 | Class clazz = entity.getClass(); 168 | 169 | String anno = findIdAnnotation(clazz); 170 | 171 | if (anno == null) { 172 | // Oh oh !!!! Big trouble ! 173 | throw new RuntimeException( 174 | "Cannot find any Id or DocumentId annotation for " 175 | + clazz.getName()); 176 | } 177 | 178 | // Now, we need to get the value of this field 179 | Object value = getMemberValue(entity, anno); 180 | 181 | if (value == null) 182 | throw new RuntimeException( 183 | "Cannont index an entity without a correct Id"); 184 | 185 | // We expect that toString() will return the Id 186 | return value.toString(); 187 | } 188 | 189 | /** 190 | * Get the document id 191 | * 192 | * @param clazz 193 | * Annoted Class 194 | * @return 195 | */ 196 | private static String findIdAnnotation(Class clazz) { 197 | if (clazz == null) 198 | return null; 199 | Field[] fields = clazz.getDeclaredFields(); 200 | for (int i = 0; i < fields.length; i++) { 201 | Annotation anno = (Annotation) fields[i].getAnnotation(Id.class); 202 | 203 | if (anno != null) { 204 | return fields[i].getName(); 205 | } 206 | } 207 | 208 | // We will look in parent classes 209 | return findIdAnnotation(clazz.getSuperclass()); 210 | } 211 | 212 | // /** 213 | // * Get all the fields annoted by Field 214 | // * 215 | // * @param clazz 216 | // * @return 217 | // */ 218 | // private static Collection getAllFieldsAnnotation(Class clazz) { 219 | // Collection returnFields = new ArrayList(); 220 | // if (clazz == null) 221 | // return returnFields; 222 | // Field[] fields = clazz.getDeclaredFields(); 223 | // for (int i = 0; i < fields.length; i++) { 224 | // Annotation anno = (Annotation) fields[i] 225 | // .getAnnotation(ESField.class); 226 | // 227 | // if (anno != null) { 228 | // returnFields.add(fields[i]); 229 | // } 230 | // } 231 | // 232 | // return returnFields; 233 | // } 234 | 235 | // /** 236 | // * Get all the fields annoted by Field even in Parents ! 237 | // * 238 | // * @param clazz 239 | // * @return 240 | // */ 241 | // private static Collection getAllFieldsAnnotationRecursive( 242 | // Class clazz) { 243 | // Collection returnFields = new ArrayList(); 244 | // returnFields.addAll(getAllFieldsAnnotation(clazz)); 245 | // if (clazz != null) 246 | // returnFields.addAll(getAllFieldsAnnotationRecursive(clazz 247 | // .getSuperclass())); 248 | // return returnFields; 249 | // } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/hibernate/plugins/elasticsearch/ElasticSearchHibernateAnnotationIntrospector.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch; 2 | 3 | import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector; 4 | 5 | // TODO for future use only 6 | public class ElasticSearchHibernateAnnotationIntrospector extends JacksonAnnotationIntrospector { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/hibernate/plugins/elasticsearch/ElasticSearchJacksonHibernateModule.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch; 2 | 3 | import org.codehaus.jackson.Version; 4 | import org.codehaus.jackson.map.Module; 5 | 6 | //TODO for future use only 7 | public class ElasticSearchJacksonHibernateModule extends Module { 8 | private final String NAME = "ESJacksonHibernateModule"; 9 | 10 | private final static Version VERSION = new Version(0, 1, 0, null); 11 | 12 | @Override 13 | public String getModuleName() { 14 | return NAME; 15 | } 16 | 17 | @Override 18 | public Version version() { 19 | return VERSION; 20 | } 21 | 22 | @Override 23 | public void setupModule(SetupContext context) { 24 | context.insertAnnotationIntrospector(new ElasticSearchHibernateAnnotationIntrospector()); 25 | // context.addSerializers(...); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/fr/pilato/hibernate/plugins/elasticsearch/annotations/ESAnalyzer.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch.annotations; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Indicate the analyze for field or method (default : "default")
11 | * E.g. : ESAnalyzer(analyzer="french") 12 | *
See Elastic Search Guide 13 | * @deprecated by Annotations from OSEM Elastic Annotations 14 | * @author David Pilato 15 | */ 16 | @Retention( RetentionPolicy.RUNTIME ) 17 | @Target( {ElementType.FIELD, ElementType.METHOD} ) 18 | @Documented 19 | @Deprecated 20 | public @interface ESAnalyzer { 21 | /** 22 | * @return the analyzer used by Elastic (Default : "default") 23 | */ 24 | String analyzer() default "default"; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fr/pilato/hibernate/plugins/elasticsearch/annotations/ESIndexed.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch.annotations; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Indicate that the entity will be indexed by Elastic Search 11 | * @deprecated by Annotations from OSEM Elastic Annotations 12 | * @author David Pilato 13 | */ 14 | @Retention( RetentionPolicy.RUNTIME ) 15 | @Target( ElementType.TYPE ) 16 | @Documented 17 | @Deprecated 18 | public @interface ESIndexed { 19 | /** 20 | * @return the index Name used by Elastic (Default : "default") 21 | */ 22 | String indexName() default "default"; 23 | } 24 | -------------------------------------------------------------------------------- /src/test/java/fr/pilato/hibernate/plugins/elasticsearch/HibernateTestCase.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch; 2 | 3 | import static org.elasticsearch.index.query.xcontent.QueryBuilders.termQuery; 4 | import static org.elasticsearch.node.NodeBuilder.nodeBuilder; 5 | import static org.junit.Assert.assertEquals; 6 | import static org.junit.Assert.fail; 7 | 8 | import java.io.File; 9 | import java.net.URL; 10 | 11 | import org.elasticsearch.action.count.CountResponse; 12 | import org.elasticsearch.action.search.SearchResponse; 13 | import org.elasticsearch.action.search.SearchType; 14 | import org.elasticsearch.client.Client; 15 | import org.elasticsearch.node.Node; 16 | import org.hibernate.Session; 17 | import org.hibernate.SessionFactory; 18 | import org.hibernate.Transaction; 19 | import org.hibernate.cfg.Configuration; 20 | import org.hibernate.tool.hbm2ddl.SchemaExport; 21 | import org.junit.AfterClass; 22 | import org.junit.BeforeClass; 23 | import org.junit.Test; 24 | 25 | import fr.pilato.hibernate.plugins.elasticsearch.testcase1.ChildEntity; 26 | import fr.pilato.hibernate.plugins.elasticsearch.testcase1.EntityMaker; 27 | import fr.pilato.hibernate.plugins.elasticsearch.testcase1.SimpleEntity; 28 | 29 | public class HibernateTestCase { 30 | 31 | protected static Configuration cfg; 32 | protected static Session session; 33 | protected static Node node; 34 | protected static Client client; 35 | 36 | private static boolean deleteDirectory(File path) { 37 | if( path.exists() ) { 38 | File[] files = path.listFiles(); 39 | for(int i=0; i settings = node.settings().getAsMap(); 66 | // 67 | // for (String string : settings.keySet()) { 68 | // System.out.println(string + " : " + settings.get(string)); 69 | // } 70 | 71 | // Removing old test files 72 | // We ask to the node where are datas 73 | String dataDir = node.settings().get("path.data"); 74 | HibernateTestCase.deleteDirectory( new File(dataDir) ); 75 | 76 | // Setting things for Hibernate 77 | if (cfg == null) { 78 | cfg = new Configuration(); 79 | cfg.setProperty(org.hibernate.cfg.Environment.SHOW_SQL, "true"); 80 | cfg.setProperty(org.hibernate.cfg.Environment.HBM2DDL_AUTO, "create"); 81 | cfg.setProperty(org.hibernate.cfg.Environment.DIALECT, "org.hibernate.dialect.HSQLDialect"); 82 | cfg.setProperty(org.hibernate.cfg.Environment.DRIVER, "org.hsqldb.jdbcDriver"); 83 | cfg.setProperty(org.hibernate.cfg.Environment.URL, "jdbc:hsqldb:mem:aname"); 84 | cfg.setProperty(org.hibernate.cfg.Environment.USER, "sa"); 85 | cfg.setProperty(org.hibernate.cfg.Environment.PASS, ""); 86 | cfg.addAnnotatedClass(SimpleEntity.class); 87 | cfg.addAnnotatedClass(ChildEntity.class); 88 | 89 | // Activate automatic indexing with ES 90 | cfg.setProperty("hibernate.search.autoregister_listeners", "false"); 91 | cfg.setListener("post-delete", "fr.pilato.hibernate.plugins.elasticsearch.ElasticSearchEventListenerFactory"); 92 | cfg.setListener("post-update", "fr.pilato.hibernate.plugins.elasticsearch.ElasticSearchEventListenerFactory"); 93 | cfg.setListener("post-insert", "fr.pilato.hibernate.plugins.elasticsearch.ElasticSearchEventListenerFactory"); 94 | 95 | SessionFactory sf = cfg.buildSessionFactory(); 96 | session = sf.openSession(); 97 | SchemaExport export = new SchemaExport( cfg ); 98 | export.create( true, true ); 99 | } 100 | 101 | SimpleEntity entity = EntityMaker.getEntity4_2(); 102 | 103 | Transaction tx = null; 104 | try { 105 | tx = session.beginTransaction(); 106 | 107 | session.persist(entity); 108 | 109 | tx.commit(); 110 | } 111 | catch (Exception e) { 112 | if (tx != null) tx.rollback(); 113 | fail("Cannot persist entity before running the real Elastic Test : " + e.getMessage()); 114 | } 115 | 116 | node = node.start(); 117 | client = node.client(); 118 | 119 | // Just wait a while for synchronizing the two nodes (HibernateNode and JUnitTestNode) 120 | Thread.sleep(1000); 121 | } 122 | 123 | @AfterClass 124 | public static void tearDown() throws Exception { 125 | if (client != null) { 126 | client.close(); 127 | } 128 | if (node != null) { 129 | node.close(); 130 | } 131 | 132 | if (session != null) { 133 | session.getSessionFactory().close(); 134 | session.close(); 135 | } 136 | } 137 | 138 | @Test 139 | public void countEntity() { 140 | CountResponse response = client.prepareCount("default") 141 | .setQuery(termQuery("value", "child")) 142 | .execute() 143 | .actionGet(); 144 | 145 | assertEquals("We should find one document with this criteria", 1, response.count()); 146 | } 147 | 148 | @Test 149 | public void findEntity() { 150 | SearchResponse response = client.prepareSearch("default") 151 | .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) 152 | .setQuery(termQuery("value", "child")) 153 | .setFrom(0).setSize(60).setExplain(true) 154 | .execute() 155 | .actionGet(); 156 | 157 | assertEquals("We should find one document with this criteria", 1, response.getHits().getTotalHits()); 158 | } 159 | 160 | @Test 161 | public void doNotFindEntity() { 162 | SearchResponse response = client.prepareSearch("default") 163 | .setSearchType(SearchType.DFS_QUERY_THEN_FETCH) 164 | .setQuery(termQuery("value", "djsfhdhfhifhize")) 165 | .setFrom(0).setSize(60).setExplain(true) 166 | .execute() 167 | .actionGet(); 168 | 169 | assertEquals("We should find nothing with this criteria", 0, response.getHits().getTotalHits()); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/test/java/fr/pilato/hibernate/plugins/elasticsearch/JSonBuilderTest.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.junit.Assert.fail; 6 | 7 | import java.io.IOException; 8 | 9 | import org.codehaus.jackson.JsonGenerationException; 10 | import org.codehaus.jackson.map.JsonMappingException; 11 | import org.codehaus.jackson.map.ObjectMapper; 12 | import org.codehaus.jackson.map.SerializationConfig; 13 | import org.junit.Test; 14 | 15 | import fr.pilato.hibernate.plugins.elasticsearch.ElasticSearchJacksonHibernateModule; 16 | import fr.pilato.hibernate.plugins.elasticsearch.testcase1.EntityMaker; 17 | 18 | /** 19 | * @author PILATO 20 | * 21 | */ 22 | public class JSonBuilderTest { 23 | 24 | private String generateJsonFromEntity(Object entity, String expected) { 25 | ObjectMapper mapper = new ObjectMapper(); 26 | 27 | // mapper.getSerializationConfig().setAnnotationIntrospector(new ElasticSearchHibernateAnnotationIntrospector()); 28 | mapper.registerModule(new ElasticSearchJacksonHibernateModule()); 29 | 30 | mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); 31 | mapper.configure(SerializationConfig.Feature.AUTO_DETECT_FIELDS, true); 32 | mapper.configure(SerializationConfig.Feature.AUTO_DETECT_GETTERS, false); 33 | mapper.configure(SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS, false); 34 | mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, false); 35 | 36 | String s = null; 37 | try { 38 | s = mapper.writeValueAsString(entity); 39 | } catch (JsonGenerationException e) { 40 | fail("JsonGenerationException : " + e.getMessage()); 41 | } catch (JsonMappingException e) { 42 | fail("JsonMappingException : " + e.getMessage()); 43 | } catch (IOException e) { 44 | fail("IOException : " + e.getMessage()); 45 | } 46 | 47 | assertNotNull(s); 48 | 49 | if (expected != null) assertEquals(expected, s); 50 | 51 | // System.out.println(s); 52 | return s; 53 | } 54 | 55 | @Test 56 | public void testModelEntity1() throws IOException { 57 | String expected = "{\"field\":null,\"sentities\":[]}"; 58 | generateJsonFromEntity(EntityMaker.getEntity1(), expected); 59 | } 60 | 61 | @Test 62 | public void testModelEntity2() throws IOException { 63 | String expected = "{\"field\":\"my field 1\",\"sentities\":[]}"; 64 | generateJsonFromEntity(EntityMaker.getEntity2(), expected); 65 | } 66 | 67 | @Test 68 | public void testModelEntity3_1() throws IOException { 69 | String expected = "{\"field\":\"my field 1\",\"sentities\":[{\"value\":null}]}"; 70 | generateJsonFromEntity(EntityMaker.getEntity3_1(), expected); 71 | } 72 | 73 | @Test 74 | public void testModelEntity3_2() throws IOException { 75 | String expected = "{\"field\":\"my field 1\",\"sentities\":[{\"value\":\"my child 1 field\"}]}"; 76 | generateJsonFromEntity(EntityMaker.getEntity3_2(), expected); 77 | } 78 | 79 | @Test 80 | public void testModelEntity4_1() throws IOException { 81 | String expected = "{\"field\":\"my field 1\",\"sentities\":[{\"value\":null},{\"value\":null}]}"; 82 | generateJsonFromEntity(EntityMaker.getEntity4_1(), expected); 83 | } 84 | 85 | @Test 86 | public void testModelEntity4_2() throws IOException { 87 | String expected = "{\"field\":\"my field 1\",\"sentities\":[{\"value\":\"my child 1 field\"},{\"value\":\"my child 2 field\"}]}"; 88 | generateJsonFromEntity(EntityMaker.getEntity4_2(), expected); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/fr/pilato/hibernate/plugins/elasticsearch/testcase1/ChildEntity.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch.testcase1; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | 7 | import org.codehaus.jackson.map.annotate.JsonSerialize; 8 | 9 | @Entity 10 | public class ChildEntity { 11 | @Id 12 | @GeneratedValue 13 | private Long id; 14 | 15 | @JsonSerialize 16 | private String value; 17 | 18 | public Long getId() { 19 | return id; 20 | } 21 | 22 | public void setId(Long id) { 23 | this.id = id; 24 | } 25 | public String getValue() { 26 | return value; 27 | } 28 | 29 | public void setValue(String value) { 30 | this.value = value; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/fr/pilato/hibernate/plugins/elasticsearch/testcase1/EntityMaker.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch.testcase1; 2 | 3 | public class EntityMaker { 4 | 5 | /** 6 | * @return An empty entity 7 | */ 8 | public static SimpleEntity getEntity1() { 9 | SimpleEntity entity = new SimpleEntity(); 10 | return entity; 11 | } 12 | 13 | /** 14 | * @return {@link #getEntity1()} + field set to "my field 1" 15 | */ 16 | public static SimpleEntity getEntity2() { 17 | SimpleEntity entity = getEntity1(); 18 | entity.setField("my field 1"); 19 | return entity; 20 | } 21 | 22 | /** 23 | * @return {@link #getEntity2()} + one empty child entity 24 | */ 25 | public static SimpleEntity getEntity3_1() { 26 | SimpleEntity entity = getEntity2(); 27 | ChildEntity centity = new ChildEntity(); 28 | entity.addToSentities(centity); 29 | 30 | return entity; 31 | } 32 | 33 | /** 34 | * @return {@link #getEntity2()} + one child entity with field "my child 1 field" 35 | */ 36 | public static SimpleEntity getEntity3_2() { 37 | SimpleEntity entity = getEntity2(); 38 | ChildEntity centity = new ChildEntity(); 39 | centity.setValue("my child 1 field"); 40 | entity.addToSentities(centity); 41 | 42 | return entity; 43 | } 44 | 45 | /** 46 | * @return {@link #getEntity3_1()} + one empty child entity 47 | */ 48 | public static SimpleEntity getEntity4_1() { 49 | SimpleEntity entity = getEntity3_1(); 50 | ChildEntity centity = new ChildEntity(); 51 | entity.addToSentities(centity); 52 | 53 | return entity; 54 | } 55 | 56 | /** 57 | * @return {@link #getEntity3_2()} + one child entity with field "my child 2 field" 58 | */ 59 | public static SimpleEntity getEntity4_2() { 60 | SimpleEntity entity = getEntity3_2(); 61 | ChildEntity centity = new ChildEntity(); 62 | centity.setValue("my child 2 field"); 63 | entity.addToSentities(centity); 64 | 65 | return entity; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/fr/pilato/hibernate/plugins/elasticsearch/testcase1/SimpleEntity.java: -------------------------------------------------------------------------------- 1 | package fr.pilato.hibernate.plugins.elasticsearch.testcase1; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | 6 | import javax.persistence.CascadeType; 7 | import javax.persistence.Entity; 8 | import javax.persistence.GeneratedValue; 9 | import javax.persistence.Id; 10 | import javax.persistence.OneToMany; 11 | import javax.persistence.Transient; 12 | 13 | import org.codehaus.jackson.map.annotate.JsonSerialize; 14 | import org.hibernate.annotations.Cascade; 15 | 16 | import fr.pilato.hibernate.plugins.elasticsearch.annotations.ESIndexed; 17 | 18 | @Entity 19 | @ESIndexed 20 | public class SimpleEntity { 21 | @Id 22 | @GeneratedValue 23 | private Long id; 24 | 25 | @JsonSerialize 26 | private String field; 27 | 28 | // Won't be serialize so not annotated with JsonSerialize 29 | @Transient 30 | private Collection stringsfield = new ArrayList(); 31 | 32 | @JsonSerialize 33 | @OneToMany( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) 34 | @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, 35 | org.hibernate.annotations.CascadeType.DELETE}) 36 | private Collection sentities = new ArrayList(); 37 | 38 | public Long getId() { 39 | return id; 40 | } 41 | 42 | public void setId(Long id) { 43 | this.id = id; 44 | } 45 | 46 | public Collection getSentities() { 47 | return sentities; 48 | } 49 | 50 | public void setSentities(Collection sentities) { 51 | this.sentities = sentities; 52 | } 53 | 54 | public void addToSentities(ChildEntity entity) { 55 | this.sentities.add(entity); 56 | } 57 | 58 | public String getField() { 59 | return field; 60 | } 61 | 62 | public void setField(String field) { 63 | this.field = field; 64 | } 65 | 66 | public Collection getStringsfield() { 67 | return stringsfield; 68 | } 69 | 70 | public void setStringsfield(Collection stringsfield) { 71 | this.stringsfield = stringsfield; 72 | } 73 | 74 | public void addToStringsfield(String string) { 75 | this.stringsfield.add(string); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/test/resources/myconfig/myelasticsearch.yml: -------------------------------------------------------------------------------- 1 | # Cluster Settings 2 | cluster: 3 | name: myTestCluster 4 | 5 | path: 6 | data: ${project.build.directory}/esdata 7 | logs: ${project.build.directory}/eslogs 8 | work: ${project.build.directory}/eswork 9 | 10 | 11 | # Gateway Settings 12 | #gateway: 13 | # recover_after_nodes: 1 14 | # recover_after_time: 5m 15 | # expected_nodes: 2 16 | --------------------------------------------------------------------------------