001: /*
002: * Copyright 2004-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.compass.gps.device.hibernate;
018:
019: import java.util.HashMap;
020: import java.util.Map;
021:
022: import org.compass.core.util.Assert;
023: import org.compass.gps.CompassGpsException;
024: import org.compass.gps.PassiveMirrorGpsDevice;
025: import org.compass.gps.device.hibernate.entities.DefaultHibernateEntitiesLocator;
026: import org.compass.gps.device.hibernate.entities.EntityInformation;
027: import org.compass.gps.device.hibernate.entities.HibernateEntitiesLocator;
028: import org.compass.gps.device.hibernate.indexer.HibernateIndexEntitiesIndexer;
029: import org.compass.gps.device.hibernate.indexer.ScrollableHibernateIndexEntitiesIndexer;
030: import org.compass.gps.device.hibernate.lifecycle.DefaultHibernateEntityLifecycleInjector;
031: import org.compass.gps.device.hibernate.lifecycle.HibernateEntityLifecycleInjector;
032: import org.compass.gps.device.hibernate.lifecycle.HibernateMirrorFilter;
033: import org.compass.gps.device.support.parallel.AbstractParallelGpsDevice;
034: import org.compass.gps.device.support.parallel.IndexEntitiesIndexer;
035: import org.compass.gps.device.support.parallel.IndexEntity;
036: import org.hibernate.SessionFactory;
037:
038: /**
039: * <p>A Hibernate Gps Device.
040: *
041: * <p>The hibernate device provides support for using jpa to index a database. The path can
042: * be viewed as: Database <-> Hibernate <-> Objects <-> Compass::Gps
043: * <-> Compass::Core (Search Engine). What it means is that for every object that has both
044: * Hibernate and compass mappings, you will be able to index it's data, as well as real time mirroring of
045: * data changes.
046: *
047: * <p>When creating the object, a <code>SessionFactory</code> must be provided to the Device.
048: *
049: * <p>Indexing uses {@link HibernateEntitiesLocator} to locate all the entities that can be
050: * indexed (i.e. entities that have both Compass and Hibernate mappings). The default implementaion
051: * used it the {@link org.compass.gps.device.hibernate.entities.DefaultHibernateEntitiesLocator}.
052: *
053: * <p>The indexing process itself is done through an implementation of
054: * {@link HibernateIndexEntitiesIndexer}. It has two different implementation, the
055: * {@link org.compass.gps.device.hibernate.indexer.PaginationHibernateIndexEntitiesIndexer} and the
056: * {@link org.compass.gps.device.hibernate.indexer.ScrollableHibernateIndexEntitiesIndexer}. The default
057: * used is the scrollable indexer.
058: *
059: * <p>Mirroring is done by injecting lifecycle listeners into Hibernate. It is done using
060: * {@link org.compass.gps.device.hibernate.lifecycle.HibernateEntityLifecycleInjector} with
061: * a default implementation of {@link org.compass.gps.device.hibernate.lifecycle.DefaultHibernateEntityLifecycleInjector}.
062: *
063: * <p>Mirroring can be turned off using the {@link #setMirrorDataChanges(boolean)} to <code>false</code>.
064: * It defaults to <code>true<code>.
065: *
066: * <p>The device allows for {@link org.compass.gps.device.hibernate.NativeHibernateExtractor} to be set,
067: * for applications that use a framework or by themself wrap the actual
068: * <code>SessionFactory</code> implementation.
069: *
070: * <p>The device extends the parallel device provinding supprot for parallel indexing.
071: *
072: * @author kimchy
073: */
074: public class HibernateGpsDevice extends AbstractParallelGpsDevice
075: implements PassiveMirrorGpsDevice {
076:
077: private SessionFactory sessionFactory;
078:
079: private boolean mirrorDataChanges = true;
080:
081: private int fetchCount = 200;
082:
083: private HibernateEntitiesLocator entitiesLocator;
084:
085: private HibernateEntityLifecycleInjector lifecycleInjector;
086:
087: private boolean ignoreMirrorExceptions;
088:
089: private HibernateMirrorFilter mirrorFilter;
090:
091: private NativeHibernateExtractor nativeExtractor;
092:
093: private HibernateIndexEntitiesIndexer entitiesIndexer;
094:
095: private Map<Class, HibernateQueryProvider> queryProviderByClass = new HashMap<Class, HibernateQueryProvider>();
096:
097: private Map<String, HibernateQueryProvider> queryProviderByName = new HashMap<String, HibernateQueryProvider>();
098:
099: private SessionFactory nativeSessionFactory;
100:
101: public HibernateGpsDevice() {
102:
103: }
104:
105: public HibernateGpsDevice(String name, SessionFactory sessionFactory) {
106: setName(name);
107: setSessionFactory(sessionFactory);
108: }
109:
110: protected void doStart() throws CompassGpsException {
111: Assert.notNull(sessionFactory,
112: buildMessage("Must set Hibernate SessionFactory"));
113:
114: nativeSessionFactory = sessionFactory;
115: if (nativeExtractor != null) {
116: nativeSessionFactory = nativeExtractor
117: .extractNative(sessionFactory);
118: if (nativeSessionFactory == null) {
119: throw new HibernateGpsDeviceException(
120: buildMessage("Native SessionFactory extractor returned null"));
121: }
122: if (log.isDebugEnabled()) {
123: log
124: .debug(buildMessage("Using native EntityManagerFactory ["
125: + nativeSessionFactory.getClass()
126: .getName()
127: + "] extracted by ["
128: + nativeExtractor.getClass().getName()
129: + "]"));
130: }
131: }
132:
133: if (entitiesLocator == null) {
134: entitiesLocator = new DefaultHibernateEntitiesLocator();
135: }
136: if (log.isDebugEnabled()) {
137: log.debug(buildMessage("Using index entityLocator ["
138: + entitiesLocator.getClass().getName() + "]"));
139: }
140:
141: if (mirrorDataChanges) {
142: if (lifecycleInjector == null) {
143: lifecycleInjector = new DefaultHibernateEntityLifecycleInjector();
144: }
145: if (log.isDebugEnabled()) {
146: log
147: .debug(buildMessage("Using lifecycleInjector ["
148: + lifecycleInjector.getClass()
149: .getName() + "]"));
150: }
151: lifecycleInjector.injectLifecycle(nativeSessionFactory,
152: this );
153: }
154:
155: if (entitiesIndexer == null) {
156: entitiesIndexer = new ScrollableHibernateIndexEntitiesIndexer();
157: }
158: if (log.isDebugEnabled()) {
159: log.debug(buildMessage("Using entities indexer ["
160: + entitiesIndexer.getClass().getName() + "]"));
161: }
162: entitiesIndexer.setHibernateGpsDevice(this );
163: }
164:
165: protected void doStop() throws CompassGpsException {
166: if (mirrorDataChanges) {
167: lifecycleInjector.removeLifecycle(nativeSessionFactory,
168: this );
169: }
170: }
171:
172: protected IndexEntity[] doGetIndexEntities()
173: throws CompassGpsException {
174: EntityInformation[] entitiesInformation = entitiesLocator
175: .locate(nativeSessionFactory, this );
176: // apply specific select statements
177: for (EntityInformation entityInformation : entitiesInformation) {
178: if (queryProviderByClass.get(entityInformation
179: .getEntityClass()) != null) {
180: entityInformation.setQueryProvider(queryProviderByClass
181: .get(entityInformation.getEntityClass()));
182: }
183: if (queryProviderByName.get(entityInformation.getName()) != null) {
184: entityInformation.setQueryProvider(queryProviderByName
185: .get(entityInformation.getName()));
186: }
187: }
188: return entitiesInformation;
189: }
190:
191: protected IndexEntitiesIndexer doGetIndexEntitiesIndexer() {
192: return entitiesIndexer;
193: }
194:
195: /**
196: * Sets the Hibernate <code>SessionFactory</code> to be used before the start operation.
197: */
198: public void setSessionFactory(SessionFactory sessionFactory) {
199: this .sessionFactory = sessionFactory;
200: }
201:
202: /**
203: * Sets the fetch count for the indexing process. A large number will perform the indexing faster,
204: * but will consume more memory. Defaults to <code>200</code>.
205: */
206: public void setFetchCount(int fetchCount) {
207: this .fetchCount = fetchCount;
208: }
209:
210: /**
211: * Returns the fetch count for the indexing process. A large number will perform the indexing faster,
212: * but will consume more memory. Default to <code>200</code>.
213: */
214: public int getFetchCount() {
215: return this .fetchCount;
216: }
217:
218: /**
219: * @see org.compass.gps.MirrorDataChangesGpsDevice#isMirrorDataChanges()
220: */
221: public boolean isMirrorDataChanges() {
222: return mirrorDataChanges;
223: }
224:
225: /**
226: * Should exceptions be ignored during the mirroring operations (the Hibernate event listeners).
227: * Defaults to <code>false</code>.
228: */
229: public boolean isIgnoreMirrorExceptions() {
230: return ignoreMirrorExceptions;
231: }
232:
233: /**
234: * Should exceptions be ignored during the mirroring operations (the Hibernate event listeners).
235: * Defaults to <code>false</code>.
236: */
237: public void setIgnoreMirrorExceptions(boolean ignoreMirrorExceptions) {
238: this .ignoreMirrorExceptions = ignoreMirrorExceptions;
239: }
240:
241: /**
242: * @see org.compass.gps.MirrorDataChangesGpsDevice#setMirrorDataChanges(boolean)
243: */
244: public void setMirrorDataChanges(boolean mirrorDataChanges) {
245: this .mirrorDataChanges = mirrorDataChanges;
246: }
247:
248: /**
249: * Sets a pluggable index entities locator allowing to control the indexes entties that
250: * will be used. Defaults to {@link org.compass.gps.device.hibernate.entities.DefaultHibernateEntitiesLocator}.
251: */
252: public void setEntitiesLocator(
253: HibernateEntitiesLocator entitiesLocator) {
254: this .entitiesLocator = entitiesLocator;
255: }
256:
257: /**
258: * Returns mirroring filter that can filter hibernate mirror events. If no mirror filter is set
259: * no filtering will happen.
260: */
261: public HibernateMirrorFilter getMirrorFilter() {
262: return mirrorFilter;
263: }
264:
265: /**
266: * Sets a mirroring filter that can filter hibernate mirror events. If no mirror filter is set
267: * no filtering will happen.
268: *
269: * @param mirrorFilter The mirror filter handler
270: */
271: public void setMirrorFilter(HibernateMirrorFilter mirrorFilter) {
272: this .mirrorFilter = mirrorFilter;
273: }
274:
275: /**
276: * Sets a native Hibernate extractor to work with frameworks that wrap the actual
277: * SessionFactory.
278: */
279: public void setNativeExtractor(
280: NativeHibernateExtractor nativeExtractor) {
281: this .nativeExtractor = nativeExtractor;
282: }
283:
284: /**
285: * Sets a custom entities indexer allowing to control the indexing process.
286: * Defaults to {@link org.compass.gps.device.hibernate.indexer.PaginationHibernateIndexEntitiesIndexer}.
287: */
288: public void setEntitiesIndexer(
289: HibernateIndexEntitiesIndexer entitiesIndexer) {
290: this .entitiesIndexer = entitiesIndexer;
291: }
292:
293: /**
294: * Sets a custom lifecycle injector controlling the injection of Hibernate lifecycle
295: * listeners for mirroring operations. Defaults to {@link org.compass.gps.device.hibernate.lifecycle.DefaultHibernateEntityLifecycleInjector}.
296: */
297: public void setLifecycleInjector(
298: HibernateEntityLifecycleInjector lifecycleInjector) {
299: this .lifecycleInjector = lifecycleInjector;
300: }
301:
302: /**
303: * <p>Sets a specific select statement for the index process of the given
304: * entity class.
305: *
306: * <p>Note, when using {@link org.compass.gps.device.hibernate.indexer.ScrollableHibernateIndexEntitiesIndexer}
307: * it is preferable not to use this mehotd, instead use
308: * {@link #setIndexQueryProvider(Class, HibernateQueryProvider)} and return a
309: * Hibernate <code>Criteria</code> object instead.
310: *
311: * <p>Note, this information is used when the device starts.
312: *
313: * @param entityClass The Entity class to associate the select query with
314: * @param selectQuery The select query to execute when indexing the given entity
315: */
316: public void setIndexSelectQuery(Class entityClass,
317: String selectQuery) {
318: setIndexQueryProvider(entityClass,
319: new DefaultHibernateQueryProvider(selectQuery));
320: }
321:
322: /**
323: * Sets a specific select statement for the index process of the given
324: * entity name.
325: *
326: * <p>Note, when using {@link org.compass.gps.device.hibernate.indexer.ScrollableHibernateIndexEntitiesIndexer}
327: * it is preferable not to use this mehotd, instead use
328: * {@link #setIndexQueryProvider(String, HibernateQueryProvider)} and return a
329: * Hibernate <code>Criteria</code> object instead.
330: *
331: * <p>Note, this information is used when the device starts.
332: *
333: * @param entityName The entity name to associate the select query with
334: * @param selectQuery The select query to execute when indexing the given entity
335: */
336: public void setIndexSelectQuery(String entityName,
337: String selectQuery) {
338: setIndexQueryProvider(entityName,
339: new DefaultHibernateQueryProvider(selectQuery));
340: }
341:
342: /**
343: * Sets a specific query provider for the index process of the given entity class.
344: * <p>Note, this information is used when the device starts.
345: *
346: * @param entityClass The Entity class to associate the query provider with
347: * @param queryProvider The query provider to execute when indexing the given entity
348: */
349: public void setIndexQueryProvider(Class entityClass,
350: HibernateQueryProvider queryProvider) {
351: queryProviderByClass.put(entityClass, queryProvider);
352: }
353:
354: /**
355: * Sets a specific query provider for the index process of the given entity name.
356: * <p>Note, this information is used when the device starts.
357: *
358: * @param entityName The Entity name to associate the query provider with
359: * @param queryProvider The query provider to execute when indexing the given entity
360: */
361: public void setIndexQueryProvider(String entityName,
362: HibernateQueryProvider queryProvider) {
363: queryProviderByName.put(entityName, queryProvider);
364: }
365:
366: /**
367: * Returns a native Hibernate extractor to work with frameworks that wrap the actual
368: * SessionFactory.
369: */
370: public SessionFactory getSessionFactory() {
371: return sessionFactory;
372: }
373: }
|