001 package org.apache.ojb.broker.core;
002
003 /* Copyright 2003-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import java.util.Collections;
019 import java.util.List;
020
021 import org.apache.ojb.broker.PBLifeCycleEvent;
022 import org.apache.ojb.broker.PBLifeCycleListener;
023 import org.apache.ojb.broker.PBListener;
024 import org.apache.ojb.broker.PBStateEvent;
025 import org.apache.ojb.broker.PBStateListener;
026 import org.apache.ojb.broker.PersistenceBrokerEvent;
027 import org.apache.ojb.broker.PersistenceBrokerException;
028 import org.apache.ojb.broker.PersistenceBrokerInternal;
029 import org.apache.ojb.broker.util.IdentityArrayList;
030 import org.apache.ojb.broker.util.configuration.Configuration;
031 import org.apache.ojb.broker.util.configuration.ConfigurationException;
032 import org.apache.ojb.broker.util.logging.LoggerFactory;
033
034 /**
035 * Abstract Implementation of the {@link org.apache.ojb.broker.PersistenceBroker}
036 * encapsulating the used PB-event/listener concept.
037 *
038 * @see org.apache.ojb.broker.PersistenceBroker
039 * @see org.apache.ojb.broker.PBLifeCycleListener
040 * @see org.apache.ojb.broker.PersistenceBrokerAware
041 * @see org.apache.ojb.broker.PBStateListener
042 *
043 * @version $Id: PersistenceBrokerAbstractImpl.java,v 1.1 2007-08-24 22:17:35 ewestfal Exp $
044 */
045 public abstract class PersistenceBrokerAbstractImpl implements PersistenceBrokerInternal
046 {
047 /**
048 * Returns <em>true</em> if PB-transaction check is enabled.
049 */
050 private boolean txCheck;
051
052 /**
053 * List containing all permanent {@link org.apache.ojb.broker.PBStateListener}
054 * instances.
055 */
056 private List permanentStateListeners = Collections.synchronizedList(new IdentityArrayList());
057
058 /**
059 * List containing all temporary {@link org.apache.ojb.broker.PBStateListener}
060 * instances.
061 */
062 private List temporaryStateListeners = Collections.synchronizedList(new IdentityArrayList());
063
064 /**
065 * List containing all permanent {@link org.apache.ojb.broker.PBLifeCycleListener}
066 * instances.
067 */
068 private List permanentLifeCycleListeners = Collections.synchronizedList(new IdentityArrayList(100));
069
070 /**
071 * List containing all temporary {@link org.apache.ojb.broker.PBLifeCycleListener}
072 * instances.
073 */
074 private List temporaryLifeCycleListeners = Collections.synchronizedList(new IdentityArrayList(100));
075
076 /**
077 * Override if needed.
078 *
079 * @see org.apache.ojb.broker.util.configuration.Configurable#configure(Configuration)
080 */
081 public void configure(Configuration pConfig) throws ConfigurationException
082 {
083 txCheck = pConfig.getBoolean("TxCheck", false);
084 }
085
086 /**
087 * Returns <em>true</em> if the development checks are enabled.
088 *
089 * @see #setTxCheck(boolean)
090 */
091 public boolean isTxCheck()
092 {
093 return txCheck;
094 }
095
096 /**
097 * This setting can be helpful during development if the PersistenceBroker transaction
098 * demarcation was used (this is true in most cases). If set 'true' on PB#store(...)
099 * and PB#delete(...) methods calls OJB check for active PB-tx and if no active tx is
100 * found a error is logged. This can help to avoid store/delete calls without a running
101 * PB-tx while development. Default setting is 'false'.
102 * <p/>
103 * <strong>Note:</strong> When using OJB in a managed
104 * environment <em>without</em> OJB-caching, it's valid to use store/delete
105 * calls without a running PB-tx.
106 *
107 * @param txCheck Set <em>true</em> to enable the checks
108 */
109 public void setTxCheck(boolean txCheck)
110 {
111 this.txCheck = txCheck;
112 }
113
114 /**
115 * @see org.apache.ojb.broker.PersistenceBroker#addListener(PBListener listener)
116 */
117 public void addListener(PBListener listener) throws PersistenceBrokerException
118 {
119 addListener(listener, false);
120 }
121
122 /**
123 * @see org.apache.ojb.broker.PersistenceBroker#addListener(PBListener listener, boolean permanent)
124 */
125 public void addListener(PBListener listener, boolean permanent) throws PersistenceBrokerException
126 {
127 if (listener instanceof PBStateListener)
128 {
129 if (permanent)
130 {
131 if (!permanentStateListeners.contains(listener))
132 {
133 permanentStateListeners.add(listener);
134 }
135 }
136 else
137 {
138 if (!temporaryStateListeners.contains(listener))
139 {
140 temporaryStateListeners.add(listener);
141 }
142 }
143 }
144
145 if (listener instanceof PBLifeCycleListener)
146 {
147 if (permanent)
148 {
149 if (!permanentLifeCycleListeners.contains(listener))
150 {
151 permanentLifeCycleListeners.add(listener);
152 }
153 }
154 else
155 {
156 if (!temporaryLifeCycleListeners.contains(listener))
157 {
158 temporaryLifeCycleListeners.add(listener);
159 }
160 }
161 }
162 }
163
164 /**
165 * @see org.apache.ojb.broker.PersistenceBroker#removeListener(PBListener listener)
166 */
167 public void removeListener(PBListener listener) throws PersistenceBrokerException
168 {
169 if (listener instanceof PBStateListener)
170 {
171 permanentStateListeners.remove(listener);
172 temporaryStateListeners.remove(listener);
173 }
174
175 if (listener instanceof PBLifeCycleListener)
176 {
177 permanentLifeCycleListeners.remove(listener);
178 temporaryLifeCycleListeners.remove(listener);
179 }
180 }
181
182 /**
183 * @see org.apache.ojb.broker.PersistenceBroker#removeAllListeners(boolean)
184 */
185 public void removeAllListeners(boolean permanent) throws PersistenceBrokerException
186 {
187 if (permanent)
188 {
189 // remove permanent listeners as well
190 permanentStateListeners.clear();
191 // this could be huge, thus simply replace instance
192 if(permanentLifeCycleListeners.size() > 10000)
193 {
194 permanentLifeCycleListeners = Collections.synchronizedList(new IdentityArrayList(100));
195 }
196 else
197 {
198 permanentLifeCycleListeners.clear();
199 }
200 }
201
202 temporaryStateListeners.clear();
203 // this could be huge, thus simply replace instance
204 if(temporaryLifeCycleListeners.size() > 10000)
205 {
206 temporaryLifeCycleListeners = Collections.synchronizedList(new IdentityArrayList(100));
207 }
208 else
209 {
210 temporaryLifeCycleListeners.clear();
211 }
212 }
213
214 /**
215 * @see org.apache.ojb.broker.PersistenceBroker#removeAllListeners()
216 */
217 public void removeAllListeners() throws PersistenceBrokerException
218 {
219 removeAllListeners(false);
220 }
221
222 public void fireBrokerEvent(PersistenceBrokerEvent event)
223 {
224 if (event instanceof PBLifeCycleEvent)
225 {
226 fireBrokerEvent((PBLifeCycleEvent) event);
227 }
228 else if (event instanceof PBStateEvent)
229 {
230 fireBrokerEvent((PBStateEvent) event);
231 }
232 else
233 {
234 LoggerFactory.getDefaultLogger().error(
235 PersistenceBrokerAbstractImpl.class.getName() + ": Unkown PersistenceBrokerEvent was fired " + event);
236 }
237 }
238
239 public void fireBrokerEvent(PBLifeCycleEvent event)
240 {
241 if (event.getPersitenceBrokerAware() != null)
242 {
243 // first we do the persistent object callback
244 performCallBack(event);
245 }
246
247 // now we notify the listeners
248 synchronized (permanentLifeCycleListeners) {
249 for (int i = permanentLifeCycleListeners.size() - 1; i >= 0; i--)
250 {
251 notifiyObjectLifeCycleListener((PBLifeCycleListener) permanentLifeCycleListeners.get(i), event);
252 }
253 }
254
255 synchronized (temporaryLifeCycleListeners) {
256 for(int i = temporaryLifeCycleListeners.size() - 1; i >= 0; i--)
257 {
258 notifiyObjectLifeCycleListener((PBLifeCycleListener) temporaryLifeCycleListeners.get(i), event);
259 }
260 }
261 }
262
263 public void fireBrokerEvent(PBStateEvent event)
264 {
265 synchronized (permanentStateListeners) {
266 for(int i = permanentStateListeners.size() - 1; i >= 0; i--)
267 {
268 notifiyStateListener((PBStateListener) permanentStateListeners.get(i), event);
269 }
270 }
271
272 synchronized (temporaryStateListeners) {
273 for(int i = temporaryStateListeners.size() - 1; i >= 0; i--)
274 {
275 notifiyStateListener((PBStateListener) temporaryStateListeners.get(i), event);
276 }
277 }
278 }
279
280 private void performCallBack(PBLifeCycleEvent event)
281 {
282 // Check for null
283 if (event.getPersitenceBrokerAware() == null) return;
284 switch (event.getEventType().typeId())
285 {
286 case PBLifeCycleEvent.TYPE_AFTER_LOOKUP:
287 event.getPersitenceBrokerAware().afterLookup(event.getTriggeringBroker());
288 break;
289 case PBLifeCycleEvent.TYPE_BEFORE_UPDATE:
290 event.getPersitenceBrokerAware().beforeUpdate(event.getTriggeringBroker());
291 break;
292 case PBLifeCycleEvent.TYPE_AFTER_UPDATE:
293 event.getPersitenceBrokerAware().afterUpdate(event.getTriggeringBroker());
294 break;
295 case PBLifeCycleEvent.TYPE_BEFORE_INSERT:
296 event.getPersitenceBrokerAware().beforeInsert(event.getTriggeringBroker());
297 break;
298 case PBLifeCycleEvent.TYPE_AFTER_INSERT:
299 event.getPersitenceBrokerAware().afterInsert(event.getTriggeringBroker());
300 break;
301 case PBLifeCycleEvent.TYPE_BEFORE_DELETE:
302 event.getPersitenceBrokerAware().beforeDelete(event.getTriggeringBroker());
303 break;
304 case PBLifeCycleEvent.TYPE_AFTER_DELETE:
305 event.getPersitenceBrokerAware().afterDelete(event.getTriggeringBroker());
306 break;
307 }
308 }
309
310 private void notifiyStateListener(PBStateListener listener, PBStateEvent stateEvent)
311 {
312 switch (stateEvent.getEventType().typeId())
313 {
314 case PBStateEvent.KEY_BEFORE_COMMIT:
315 listener.beforeCommit(stateEvent);
316 break;
317 case PBStateEvent.KEY_AFTER_COMMIT:
318 listener.afterCommit(stateEvent);
319 break;
320 case PBStateEvent.KEY_BEFORE_BEGIN:
321 listener.beforeBegin(stateEvent);
322 break;
323 case PBStateEvent.KEY_AFTER_BEGIN:
324 listener.afterBegin(stateEvent);
325 break;
326 case PBStateEvent.KEY_BEFORE_CLOSE:
327 listener.beforeClose(stateEvent);
328 break;
329 case PBStateEvent.KEY_AFTER_OPEN:
330 listener.afterOpen(stateEvent);
331 break;
332 case PBStateEvent.KEY_AFTER_ROLLBACK:
333 listener.afterRollback(stateEvent);
334 break;
335 case PBStateEvent.KEY_BEFORE_ROLLBACK:
336 listener.beforeRollback(stateEvent);
337 break;
338 }
339 }
340
341 private void notifiyObjectLifeCycleListener(PBLifeCycleListener listener, PBLifeCycleEvent lifeEvent)
342 {
343 switch (lifeEvent.getEventType().typeId())
344 {
345 case PBLifeCycleEvent.TYPE_AFTER_LOOKUP:
346 listener.afterLookup(lifeEvent);
347 break;
348 case PBLifeCycleEvent.TYPE_BEFORE_UPDATE:
349 listener.beforeUpdate(lifeEvent);
350 break;
351 case PBLifeCycleEvent.TYPE_AFTER_UPDATE:
352 listener.afterUpdate(lifeEvent);
353 break;
354 case PBLifeCycleEvent.TYPE_BEFORE_INSERT:
355 listener.beforeInsert(lifeEvent);
356 break;
357 case PBLifeCycleEvent.TYPE_AFTER_INSERT:
358 listener.afterInsert(lifeEvent);
359 break;
360 case PBLifeCycleEvent.TYPE_BEFORE_DELETE:
361 listener.beforeDelete(lifeEvent);
362 break;
363 case PBLifeCycleEvent.TYPE_AFTER_DELETE:
364 listener.afterDelete(lifeEvent);
365 break;
366 }
367 }
368
369 /*
370 arminw:
371 get the PB state event here, this helps to
372 avoid object instantiation for any event call
373 */
374 protected final PBStateEvent AFTER_OPEN_EVENT = new PBStateEvent(this, PBStateEvent.Type.AFTER_OPEN);
375 protected final PBStateEvent AFTER_BEGIN_EVENT = new PBStateEvent(this, PBStateEvent.Type.AFTER_BEGIN);
376 protected final PBStateEvent AFTER_COMMIT_EVENT = new PBStateEvent(this, PBStateEvent.Type.AFTER_COMMIT);
377 protected final PBStateEvent AFTER_ROLLBACK_EVENT = new PBStateEvent(this, PBStateEvent.Type.AFTER_ROLLBACK);
378 protected final PBStateEvent BEFORE_BEGIN_EVENT = new PBStateEvent(this, PBStateEvent.Type.BEFORE_BEGIN);
379 protected final PBStateEvent BEFORE_COMMIT_EVENT = new PBStateEvent(this, PBStateEvent.Type.BEFORE_COMMIT);
380 protected final PBStateEvent BEFORE_ROLLBACK_EVENT = new PBStateEvent(this, PBStateEvent.Type.BEFORE_ROLLBACK);
381 protected final PBStateEvent BEFORE_CLOSE_EVENT = new PBStateEvent(this, PBStateEvent.Type.BEFORE_CLOSE);
382
383 /*
384 arminw:
385 here we could get the PB state event, this helps to
386 avoid object instantiation on every event
387 NOTE: It's a little critical, because caller shouldn't forget
388 to set the target object using #setTargetObject(...) method.
389 This means that we use the same event object with changed
390 fields.
391 TODO: find a better solution (and performant)
392 */
393 protected PBLifeCycleEvent BEFORE_STORE_EVENT =
394 new PBLifeCycleEvent(this, PBLifeCycleEvent.Type.BEFORE_INSERT);
395 protected PBLifeCycleEvent AFTER_STORE_EVENT =
396 new PBLifeCycleEvent(this, PBLifeCycleEvent.Type.AFTER_INSERT);
397 protected PBLifeCycleEvent BEFORE_DELETE_EVENT =
398 new PBLifeCycleEvent(this, PBLifeCycleEvent.Type.BEFORE_DELETE);
399 protected PBLifeCycleEvent AFTER_DELETE_EVENT =
400 new PBLifeCycleEvent(this, PBLifeCycleEvent.Type.AFTER_DELETE);
401 protected PBLifeCycleEvent AFTER_LOOKUP_EVENT =
402 new PBLifeCycleEvent(this, PBLifeCycleEvent.Type.AFTER_LOOKUP);
403 protected PBLifeCycleEvent BEFORE_UPDATE_EVENT =
404 new PBLifeCycleEvent(this, PBLifeCycleEvent.Type.BEFORE_UPDATE);
405 protected PBLifeCycleEvent AFTER_UPDATE_EVENT =
406 new PBLifeCycleEvent(this, PBLifeCycleEvent.Type.AFTER_UPDATE);
407 }
408