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