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.ojb;
018:
019: import java.util.Collection;
020: import java.util.Iterator;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.apache.ojb.broker.PBLifeCycleEvent;
025: import org.apache.ojb.broker.PBLifeCycleListener;
026: import org.apache.ojb.broker.PersistenceBroker;
027: import org.apache.ojb.broker.PersistenceBrokerException;
028: import org.apache.ojb.broker.query.QueryByCriteria;
029: import org.compass.core.CompassCallbackWithoutResult;
030: import org.compass.core.CompassException;
031: import org.compass.core.CompassSession;
032: import org.compass.core.mapping.CascadeMapping;
033: import org.compass.core.mapping.ResourceMapping;
034: import org.compass.core.mapping.osem.ClassMapping;
035: import org.compass.core.spi.InternalCompass;
036: import org.compass.gps.CompassGpsException;
037: import org.compass.gps.PassiveMirrorGpsDevice;
038: import org.compass.gps.device.AbstractGpsDevice;
039: import org.compass.gps.spi.CompassGpsInterfaceDevice;
040:
041: /**
042: * An ObJectRelationalBridge (OJB) device, provides support for using ojb and
043: * ojb mapping files to index a database. The path can be views as: Database <->
044: * OJB <-> Objects <-> Compass::Gps <-> Compass::Core (Search Engine). What it
045: * means is that for every object that has both ojb and compass mappings, you
046: * will be able to index it's data, as well as real time mirroring of data
047: * changes.
048: * <p/>
049: * Indexing the data (using the <code>index()</code> operation) requires the
050: * <code>batchPersistentBroker</code> property to be set, before the
051: * <code>index()</code> operation is called.
052: * <p/>
053: * Real-time mirroring of data changes requires to use the
054: * {@link OjbGpsDevice#attachLifecycleListeners(PersistenceBroker)} to let the
055: * device listen for any data changes, and
056: * {@link OjbGpsDevice#removeLifecycleListeners(PersistenceBroker)} to remove
057: * the listener. Since the lifecycle listener can only be set on the instance
058: * level, and not the factory level (why would you do that, ojb developers?),
059: * attach and remove must be called every time a PersistentBroker is
060: * instantiated. You can use the
061: * {@link OjbGpsDeviceUtils#attachPersistenceBrokerForIndex(org.compass.gps.CompassGpsDevice, org.apache.ojb.broker.PersistenceBroker)}
062: * and
063: * {@link OjbGpsDeviceUtils#removePersistenceBrokerForMirror(org.compass.gps.CompassGpsDevice, org.apache.ojb.broker.PersistenceBroker)}
064: * as helper methods if attache to a generic device is required (which must be
065: * the OjbGpsDevice).
066: * <p/>
067: * Since the real time mirroring and the event listener registration sounds like
068: * an aspect for Ojb aware classes/methods, Compass::Spring utilizes spring
069: * support for OJB and aspects for a much simpler event registration, please see
070: * Compass::Spring for more documentation.
071: *
072: * @author kimchy
073: */
074: public class OjbGpsDevice extends AbstractGpsDevice implements
075: PassiveMirrorGpsDevice {
076:
077: protected static Log log = LogFactory.getLog(OjbGpsDevice.class);
078:
079: private boolean mirrorDataChanges = true;
080:
081: private PersistenceBroker indexPersistenceBroker;
082:
083: private CompassGpsPBLifecycleListener lifecycleListener;
084:
085: public OjbGpsDevice() {
086:
087: }
088:
089: public OjbGpsDevice(String name,
090: PersistenceBroker indexPersistenceBroker) {
091: setName(name);
092: this .indexPersistenceBroker = indexPersistenceBroker;
093: }
094:
095: protected void doIndex(CompassSession session)
096: throws CompassGpsException {
097: final PersistenceBroker persistenceBroker = doGetIndexPersistentBroker();
098: if (persistenceBroker == null) {
099: throw new OjbGpsDeviceException(
100: buildMessage("Must set the index persistent broker"));
101: }
102: if (log.isInfoEnabled()) {
103: log.info(buildMessage("Indexing the database"));
104: }
105: final ResourceMapping[] resourceMappings = ((InternalCompass) compassGps
106: .getIndexCompass()).getMapping().getRootMappings();
107:
108: for (int i = 0; i < resourceMappings.length; i++) {
109: if (!(resourceMappings[i] instanceof ClassMapping)) {
110: continue;
111: }
112: final ClassMapping classMapping = (ClassMapping) resourceMappings[i];
113: final Class clazz = classMapping.getClazz();
114: if (isFilteredForIndex(clazz.getName())) {
115: continue;
116: }
117: try {
118: QueryByCriteria query = new QueryByCriteria(clazz);
119: Collection datas = null;
120: try {
121: datas = persistenceBroker
122: .getCollectionByQuery(query);
123: } catch (Exception e) {
124: // no mapping for the class in ojb
125: }
126: if (datas == null) {
127: continue;
128: }
129: if (log.isDebugEnabled()) {
130: log.debug(buildMessage("Indexing alias ["
131: + classMapping.getAlias()
132: + "] with object count [" + datas.size()
133: + "]"));
134: }
135: for (Iterator it = datas.iterator(); it.hasNext();) {
136: session.create(it.next());
137: }
138: } catch (Exception e) {
139: log.error(buildMessage("Failed to index the database"),
140: e);
141: throw new OjbGpsDeviceException(
142: buildMessage("Failed to index the database"), e);
143: }
144: }
145:
146: if (log.isInfoEnabled()) {
147: log.info(buildMessage("Finished indexing the database"));
148: }
149: }
150:
151: /**
152: * A method which can be used by derived classes to supply the persistent
153: * broker by a means of a centrelized registry (for example).
154: */
155: protected PersistenceBroker doGetIndexPersistentBroker()
156: throws CompassGpsException {
157: return getIndexPersistenceBroker();
158: }
159:
160: protected void doStart() throws CompassGpsException {
161: lifecycleListener = new CompassGpsPBLifecycleListener(
162: compassGps, this );
163: }
164:
165: protected void doStop() throws CompassGpsException {
166: }
167:
168: /**
169: * Attached the OjbGpsDevice lifecycle listener to the instance of the
170: * persistence broker.
171: *
172: * @param pb The persistence broker
173: */
174: public void attachLifecycleListeners(PersistenceBroker pb) {
175: pb.addListener(lifecycleListener);
176: }
177:
178: /**
179: * Removed the OjbGpsDevice lifecycle listener from the instance of the
180: * persistence broker.
181: *
182: * @param pb The persistence broker
183: */
184: public void removeLifecycleListeners(PersistenceBroker pb) {
185: pb.removeListener(lifecycleListener);
186: }
187:
188: /**
189: * Returns the batch persistence broker used for indexing.
190: */
191: public PersistenceBroker getIndexPersistenceBroker() {
192: return indexPersistenceBroker;
193: }
194:
195: /**
196: * Sets the batch persistence broker used for indexing.
197: */
198: public void setIndexPersistenceBroker(
199: PersistenceBroker indexPersistenceBroker) {
200: this .indexPersistenceBroker = indexPersistenceBroker;
201: }
202:
203: public boolean isMirrorDataChanges() {
204: return mirrorDataChanges;
205: }
206:
207: public void setMirrorDataChanges(boolean mirrorDataChanges) {
208: this .mirrorDataChanges = mirrorDataChanges;
209: }
210:
211: private class CompassGpsPBLifecycleListener implements
212: PBLifeCycleListener {
213:
214: private OjbGpsDevice ojbGpsDevice;
215:
216: private CompassGpsInterfaceDevice compassGps;
217:
218: public CompassGpsPBLifecycleListener(
219: CompassGpsInterfaceDevice compassGps,
220: OjbGpsDevice ojbGpsDevice) {
221: this .compassGps = compassGps;
222: this .ojbGpsDevice = ojbGpsDevice;
223: }
224:
225: public void beforeInsert(PBLifeCycleEvent lifeCycleEvent)
226: throws PersistenceBrokerException {
227: }
228:
229: public void afterInsert(PBLifeCycleEvent lifeCycleEvent)
230: throws PersistenceBrokerException {
231: if (!ojbGpsDevice.shouldMirrorDataChanges()
232: || isPerformingIndexOperation()) {
233: return;
234: }
235: final Object entity = lifeCycleEvent.getTarget();
236: if (!compassGps.hasMappingForEntityForMirror(entity
237: .getClass(), CascadeMapping.Cascade.CREATE)) {
238: return;
239: }
240: try {
241: if (log.isDebugEnabled()) {
242: log.debug("{" + ojbGpsDevice.getName()
243: + "}: Creating [" + entity + "]");
244: }
245: compassGps
246: .executeForMirror(new CompassCallbackWithoutResult() {
247: protected void doInCompassWithoutResult(
248: CompassSession session)
249: throws CompassException {
250: session.create(entity);
251: }
252: });
253: } catch (Exception e) {
254: log.error("{" + ojbGpsDevice.getName()
255: + "}: Failed while creating [" + entity + "]",
256: e);
257: }
258: }
259:
260: public void beforeUpdate(PBLifeCycleEvent lifeCycleEvent)
261: throws PersistenceBrokerException {
262: }
263:
264: public void afterUpdate(PBLifeCycleEvent lifeCycleEvent)
265: throws PersistenceBrokerException {
266: if (!ojbGpsDevice.shouldMirrorDataChanges()
267: || isPerformingIndexOperation()) {
268: return;
269: }
270: final Object entity = lifeCycleEvent.getTarget();
271: if (!compassGps.hasMappingForEntityForMirror(entity
272: .getClass(), CascadeMapping.Cascade.SAVE)) {
273: return;
274: }
275: try {
276: if (log.isDebugEnabled()) {
277: log.debug("{" + ojbGpsDevice.getName()
278: + "}: Updating [" + entity + "]");
279: }
280: compassGps
281: .executeForMirror(new CompassCallbackWithoutResult() {
282: protected void doInCompassWithoutResult(
283: CompassSession session)
284: throws CompassException {
285: session.save(entity);
286: }
287: });
288: } catch (Exception e) {
289: log.error("{" + ojbGpsDevice.getName()
290: + "}: Failed while updating [" + entity + "]",
291: e);
292: }
293: }
294:
295: public void beforeDelete(PBLifeCycleEvent lifeCycleEvent)
296: throws PersistenceBrokerException {
297: }
298:
299: public void afterDelete(PBLifeCycleEvent lifeCycleEvent)
300: throws PersistenceBrokerException {
301: if (!ojbGpsDevice.shouldMirrorDataChanges()
302: || isPerformingIndexOperation()) {
303: return;
304: }
305: final Object entity = lifeCycleEvent.getTarget();
306: if (!compassGps.hasMappingForEntityForMirror(entity
307: .getClass(), CascadeMapping.Cascade.DELETE)) {
308: return;
309: }
310: try {
311: if (log.isDebugEnabled()) {
312: log.debug("{" + ojbGpsDevice.getName()
313: + "}: Deleting [" + entity + "]");
314: }
315: compassGps
316: .executeForMirror(new CompassCallbackWithoutResult() {
317: protected void doInCompassWithoutResult(
318: CompassSession session)
319: throws CompassException {
320: session.delete(entity);
321: }
322: });
323: } catch (Exception e) {
324: log.error("{" + ojbGpsDevice.getName()
325: + "}: Failed while deleting [" + entity + "]",
326: e);
327: }
328: }
329:
330: public void afterLookup(PBLifeCycleEvent lifeCycleEvent)
331: throws PersistenceBrokerException {
332: }
333:
334: }
335:
336: }
|