KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > metadata > MetadataManager


1 package org.apache.ojb.broker.metadata;
2
3 /* Copyright 2002-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.io.FileNotFoundException JavaDoc;
19 import java.io.InputStream JavaDoc;
20 import java.util.Hashtable JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23
24 import org.apache.commons.lang.SerializationUtils;
25 import org.apache.ojb.broker.PBKey;
26 import org.apache.ojb.broker.core.PersistenceBrokerConfiguration;
27 import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
28 import org.apache.ojb.broker.util.logging.Logger;
29 import org.apache.ojb.broker.util.logging.LoggerFactory;
30
31 /**
32  * Central class for metadata operations/manipulations - manages OJB's
33  * metadata objects, in particular:
34  * <ul>
35  * <li>{@link org.apache.ojb.broker.metadata.DescriptorRepository} contains
36  * metadata of persistent objects</li>
37  * <li>{@link org.apache.ojb.broker.metadata.ConnectionRepository} contains
38  * all connection metadata information</li>
39  * </ul>
40  *
41  * This class allows transparent flexible metadata loading/manipulation at runtime.
42  *
43  * <p>
44  * <b>How to read/merge metadata</b><br/>
45  * Per default OJB loads default {@link org.apache.ojb.broker.metadata.DescriptorRepository}
46  * and {@link org.apache.ojb.broker.metadata.ConnectionRepository} instances, by reading the
47  * specified repository file. This is done first time the <code>MetadataManager</code> instance
48  * was used.
49  * <br/>
50  * To read metadata information at runtime use
51  * {@link #readDescriptorRepository readDescriptorRepository} and
52  * {@link #readConnectionRepository readConnectionRepository}
53  * methods.
54  * <br/>
55  * It is also possible to merge different repositories using
56  * {@link #mergeDescriptorRepository mergeDescriptorRepository}
57  * and {@link #mergeConnectionRepository mergeConnectionRepository}
58  *
59  * </p>
60  *
61  * <a name="perThread"/>
62  * <h3>Per thread handling of metadata</h3>
63  * <p>
64  * Per default the manager handle one global {@link org.apache.ojb.broker.metadata.DescriptorRepository}
65  * for all calling threads, but it is ditto possible to use different metadata <i>profiles</i> in a per thread
66  * manner - <i>profiles</i> means different copies of {@link org.apache.ojb.broker.metadata.DescriptorRepository}
67  * objects.
68  * <p/>
69  *
70  * <p>
71  * <a name="enablePerThreadMode"/>
72  * <b>Enable the per thread mode</b><br/>
73  * To enable the 'per thread' mode for {@link org.apache.ojb.broker.metadata.DescriptorRepository}
74  * instances:
75  * <pre>
76  * MetadataManager mm = MetadataManager.getInstance();
77  * // tell the manager to use per thread mode
78  * mm.setEnablePerThreadChanges(true);
79  * ...
80  * </pre>
81  * This could be done e.g. at start up.<br/>
82  * Now it's possible to use dedicated <code>DescriptorRepository</code> instances
83  * per thread:
84  * <pre>
85  * // e.g we get a coppy of the global repository
86  * DescriptorRepository dr = mm.copyOfGlobalRepository();
87  * // now we can manipulate the persistent object metadata of the copy
88  * ......
89  *
90  * // set the changed repository for this thread
91  * mm.setDescriptor(dr);
92  *
93  * // now let this thread lookup a PersistenceBroker instance
94  * // with the modified metadata
95  * // all other threads use the global metadata
96  * PersistenceBroker broker = Persis......
97  * </pre>
98  * Note: Change metadata <i>before</i> lookup the {@link org.apache.ojb.broker.PersistenceBroker}
99  * instance for current thread, because the metadata was bound to the PB at lookup.
100  * </p>
101  *
102  * <p>
103  * <b>How to use different metadata profiles</b><br/>
104  * MetadataManager was shipped with a simple mechanism to
105  * add, remove and load different persistent objects metadata
106  * profiles (different {@link org.apache.ojb.broker.metadata.DescriptorRepository}
107  * instances) in a per thread manner. Use
108  * <ul>
109  * <li>{@link #addProfile addProfile} add different persistent object metadata profiles</li>
110  * <li>{@link #removeProfile removeProfile} remove a persistent object metadata profiles</li>
111  * <li>{@link #loadProfile loadProfile} load a profile for the current thread</li>
112  * </ul>
113  * Note: method {@link #loadProfile loadProfile} only works if
114  * the <a HREF="#enablePerThreadMode">per thread mode</a> is enabled.
115  * </p>
116  *
117  *
118  * @author <a HREF="mailto:armin@codeAuLait.de">Armin Waibel</a>
119  * @version $Id: MetadataManager.java,v 1.19.2.7 2005/12/21 22:26:10 tomdz Exp $
120  */

121 public class MetadataManager
122 {
123     private static Logger log = LoggerFactory.getLogger(MetadataManager.class);
124
125     private static final String JavaDoc MSG_STR = "* Can't find DescriptorRepository for current thread, use default one *";
126     private static ThreadLocal JavaDoc threadedRepository = new ThreadLocal JavaDoc();
127     private static ThreadLocal JavaDoc currentProfileKey = new ThreadLocal JavaDoc();
128     private static MetadataManager singleton;
129
130     private Hashtable JavaDoc metadataProfiles;
131     private DescriptorRepository globalRepository;
132     private ConnectionRepository connectionRepository;
133     private boolean enablePerThreadChanges;
134     private PBKey defaultPBKey;
135
136     // singleton
137
private MetadataManager()
138     {
139         init();
140     }
141
142     private void init()
143     {
144         metadataProfiles = new Hashtable JavaDoc();
145         final String JavaDoc repository = ((PersistenceBrokerConfiguration) OjbConfigurator.getInstance()
146                 .getConfigurationFor(null)).getRepositoryFilename();
147         try
148         {
149             globalRepository = new RepositoryPersistor().readDescriptorRepository(repository);
150             connectionRepository = new RepositoryPersistor().readConnectionRepository(repository);
151         }
152         catch (FileNotFoundException JavaDoc ex)
153         {
154             log.warn("Could not access '" + repository + "' or a DOCTYPE/DTD-dependency. "
155                      + "(Check letter case for file names and HTTP-access if using DOCTYPE PUBLIC)"
156                      + " Starting with empty metadata and connection configurations.", ex);
157             globalRepository = new DescriptorRepository();
158             connectionRepository = new ConnectionRepository();
159         }
160         catch (Exception JavaDoc ex)
161         {
162             throw new MetadataException("Can't read repository file '" + repository + "'", ex);
163         }
164     }
165
166     public void shutdown()
167     {
168         threadedRepository = null;
169         currentProfileKey = null;
170         globalRepository = null;
171         metadataProfiles = null;
172         singleton = null;
173     }
174
175     /**
176      * Returns an instance of this class.
177      */

178     public static synchronized MetadataManager getInstance()
179     {
180         // lazy initialization
181
if (singleton == null)
182         {
183             singleton = new MetadataManager();
184         }
185         return singleton;
186     }
187
188     /**
189      * Returns the current valid {@link org.apache.ojb.broker.metadata.DescriptorRepository} for
190      * the caller. This is the provided way to obtain the
191      * {@link org.apache.ojb.broker.metadata.DescriptorRepository}.
192      * <br>
193      * When {@link #isEnablePerThreadChanges per thread descriptor handling} is enabled
194      * it search for a specific {@link org.apache.ojb.broker.metadata.DescriptorRepository}
195      * for the calling thread, if none can be found the global descriptor was returned.
196      *
197      * @see MetadataManager#getGlobalRepository
198      * @see MetadataManager#copyOfGlobalRepository
199      */

200     public DescriptorRepository getRepository()
201     {
202         DescriptorRepository repository;
203         if (enablePerThreadChanges)
204         {
205             repository = (DescriptorRepository) threadedRepository.get();
206             if (repository == null)
207             {
208                 repository = getGlobalRepository();
209                 log.info(MSG_STR);
210             }
211 // arminw:
212
// TODO: Be more strict in per thread mode and throw a exception when not find descriptor for calling thread?
213
// if (repository == null)
214
// {
215
// throw new MetadataException("Can't find a DescriptorRepository for current thread, don't forget" +
216
// " to set a DescriptorRepository if enable per thread changes before perform other action");
217
// }
218
return repository;
219         }
220         else
221         {
222             return globalRepository;
223         }
224     }
225
226     /**
227      * Returns explicit the global {@link org.apache.ojb.broker.metadata.DescriptorRepository} - use with
228      * care, because it ignores the {@link #isEnablePerThreadChanges per thread mode}.
229      *
230      * @see MetadataManager#getRepository
231      * @see MetadataManager#copyOfGlobalRepository
232      */

233     public DescriptorRepository getGlobalRepository()
234     {
235         return globalRepository;
236     }
237
238     /**
239      * Returns the {@link ConnectionRepository}.
240      */

241     public ConnectionRepository connectionRepository()
242     {
243         return connectionRepository;
244     }
245
246     /**
247      * Merge the given {@link ConnectionRepository} with the existing one (without making
248      * a deep copy of the containing connection descriptors).
249      * @see #mergeConnectionRepository(ConnectionRepository targetRepository, ConnectionRepository sourceRepository, boolean deep)
250      */

251     public void mergeConnectionRepository(ConnectionRepository repository)
252     {
253         mergeConnectionRepository(connectionRepository(), repository, false);
254     }
255
256     /**
257      * Merge the given source {@link ConnectionRepository} with the
258      * existing target. If parameter
259      * <tt>deep</tt> is set <code>true</code> deep copies of source objects were made.
260      * <br/>
261      * Note: Using <tt>deep copy mode</tt> all descriptors will be serialized
262      * by using the default class loader to resolve classes. This can be problematic
263      * when classes are loaded by a context class loader.
264      * <p>
265      * Note: All classes within the repository structure have to implement
266      * <code>java.io.Serializable</code> to be able to create a cloned copy.
267      */

268     public void mergeConnectionRepository(
269             ConnectionRepository targetRepository, ConnectionRepository sourceRepository, boolean deep)
270     {
271         List JavaDoc list = sourceRepository.getAllDescriptor();
272         for (Iterator JavaDoc iterator = list.iterator(); iterator.hasNext();)
273         {
274             JdbcConnectionDescriptor jcd = (JdbcConnectionDescriptor) iterator.next();
275             if (deep)
276             {
277                 //TODO: adopt copy/clone methods for metadata classes?
278
jcd = (JdbcConnectionDescriptor) SerializationUtils.clone(jcd);
279             }
280             targetRepository.addDescriptor(jcd);
281         }
282     }
283
284     /**
285      * Merge the given {@link org.apache.ojb.broker.metadata.DescriptorRepository}
286      * (without making a deep copy of containing class-descriptor objects) with the
287      * global one, returned by method {@link #getRepository()} - keep
288      * in mind if running in <a HREF="#perThread">per thread mode</a>
289      * merge maybe only takes effect on current thread.
290      *
291      * @see #mergeDescriptorRepository(DescriptorRepository targetRepository, DescriptorRepository sourceRepository, boolean deep)
292      */

293     public void mergeDescriptorRepository(DescriptorRepository repository)
294     {
295         mergeDescriptorRepository(getRepository(), repository, false);
296     }
297
298     /**
299      * Merge the given {@link org.apache.ojb.broker.metadata.DescriptorRepository}
300      * files, the source objects will be pushed to the target repository. If parameter
301      * <tt>deep</tt> is set <code>true</code> deep copies of source objects were made.
302      * <br/>
303      * Note: Using <tt>deep copy mode</tt> all descriptors will be serialized
304      * by using the default class loader to resolve classes. This can be problematic
305      * when classes are loaded by a context class loader.
306      * <p>
307      * Note: All classes within the repository structure have to implement
308      * <code>java.io.Serializable</code> to be able to create a cloned copy.
309      *
310      * @see #isEnablePerThreadChanges
311      * @see #setEnablePerThreadChanges
312      */

313     public void mergeDescriptorRepository(
314             DescriptorRepository targetRepository, DescriptorRepository sourceRepository, boolean deep)
315     {
316         Iterator JavaDoc it = sourceRepository.iterator();
317         while (it.hasNext())
318         {
319             ClassDescriptor cld = (ClassDescriptor) it.next();
320             if (deep)
321             {
322                 //TODO: adopt copy/clone methods for metadata classes?
323
cld = (ClassDescriptor) SerializationUtils.clone(cld);
324             }
325             targetRepository.put(cld.getClassOfObject(), cld);
326             cld.setRepository(targetRepository);
327         }
328     }
329
330     /**
331      * Read ClassDescriptors from the given repository file.
332      * @see #mergeDescriptorRepository
333      */

334     public DescriptorRepository readDescriptorRepository(String JavaDoc fileName)
335     {
336         try
337         {
338             RepositoryPersistor persistor = new RepositoryPersistor();
339             return persistor.readDescriptorRepository(fileName);
340         }
341         catch (Exception JavaDoc e)
342         {
343             throw new MetadataException("Can not read repository " + fileName, e);
344         }
345     }
346
347     /**
348      * Read ClassDescriptors from the given InputStream.
349      * @see #mergeDescriptorRepository
350      */

351     public DescriptorRepository readDescriptorRepository(InputStream JavaDoc inst)
352     {
353         try
354         {
355             RepositoryPersistor persistor = new RepositoryPersistor();
356             return persistor.readDescriptorRepository(inst);
357         }
358         catch (Exception JavaDoc e)
359         {
360             throw new MetadataException("Can not read repository " + inst, e);
361         }
362     }
363
364     /**
365      * Read JdbcConnectionDescriptors from the given repository file.
366      *
367      * @see #mergeConnectionRepository
368      */

369     public ConnectionRepository readConnectionRepository(String JavaDoc fileName)
370     {
371         try
372         {
373             RepositoryPersistor persistor = new RepositoryPersistor();
374             return persistor.readConnectionRepository(fileName);
375         }
376         catch (Exception JavaDoc e)
377         {
378             throw new MetadataException("Can not read repository " + fileName, e);
379         }
380     }
381
382     /**
383      * Read JdbcConnectionDescriptors from this InputStream.
384      *
385      * @see #mergeConnectionRepository
386      */

387     public ConnectionRepository readConnectionRepository(InputStream JavaDoc inst)
388     {
389         try
390         {
391             RepositoryPersistor persistor = new RepositoryPersistor();
392             return persistor.readConnectionRepository(inst);
393         }
394         catch (Exception JavaDoc e)
395         {
396             throw new MetadataException("Can not read repository from " + inst, e);
397         }
398     }
399
400     /**
401      * Set the {@link org.apache.ojb.broker.metadata.DescriptorRepository} - if <i>global</i> was true, the
402      * given descriptor aquire global availability (<i>use with care!</i>),
403      * else the given descriptor was associated with the calling thread.
404      *
405      * @see #isEnablePerThreadChanges
406      * @see #setEnablePerThreadChanges
407      */

408     public void setDescriptor(DescriptorRepository repository, boolean global)
409     {
410         if (global)
411         {
412             if (log.isDebugEnabled()) log.debug("Set new global repository: " + repository);
413             globalRepository = repository;
414         }
415         else
416         {
417             if (log.isDebugEnabled()) log.debug("Set new threaded repository: " + repository);
418             threadedRepository.set(repository);
419         }
420     }
421
422     /**
423      * Set {@link DescriptorRepository} for the current thread.
424      * Convenience method for
425      * {@link #setDescriptor(DescriptorRepository repository, boolean global) setDescriptor(repository, false)}.
426      */

427     public void setDescriptor(DescriptorRepository repository)
428     {
429         setDescriptor(repository, false);
430     }
431
432     /**
433      * Convenience method for
434      * {@link #setDescriptor setDescriptor(repository, false)}.
435      * @deprecated use {@link #setDescriptor}
436      */

437     public void setPerThreadDescriptor(DescriptorRepository repository)
438     {
439         setDescriptor(repository, false);
440     }
441
442     /**
443      * Returns a copy of the current global
444      * {@link org.apache.ojb.broker.metadata.DescriptorRepository}
445      * <p>
446      * Note: All classes within the repository structure have to implement
447      * <code>java.io.Serializable</code> to be able to create a cloned copy.
448      *
449      * @see MetadataManager#getGlobalRepository
450      * @see MetadataManager#getRepository
451      */

452     public DescriptorRepository copyOfGlobalRepository()
453     {
454         return (DescriptorRepository) SerializationUtils.clone(globalRepository);
455     }
456
457     /**
458      * If returns <i>true</i> if <a HREF="#perThread">per thread</a> runtime
459      * changes of the {@link org.apache.ojb.broker.metadata.DescriptorRepository}
460      * is enabled and the {@link #getRepository} method returns a threaded
461      * repository file if set, or the global if no threaded was found.
462      * <br>
463      * If returns <i>false</i> the {@link #getRepository} method return
464      * always the {@link #getGlobalRepository() global} repository.
465      *
466      * @see #setEnablePerThreadChanges
467      */

468     public boolean isEnablePerThreadChanges()
469     {
470         return enablePerThreadChanges;
471     }
472
473     /**
474      * Enable the possibility of making <a HREF="#perThread">per thread</a> runtime changes
475      * of the {@link org.apache.ojb.broker.metadata.DescriptorRepository}.
476      *
477      * @see #isEnablePerThreadChanges
478      */

479     public void setEnablePerThreadChanges(boolean enablePerThreadChanges)
480     {
481         this.enablePerThreadChanges = enablePerThreadChanges;
482     }
483
484     /**
485      * Add a metadata profile.
486      * @see #loadProfile
487      */

488     public void addProfile(Object JavaDoc key, DescriptorRepository repository)
489     {
490         if (metadataProfiles.contains(key))
491         {
492             throw new MetadataException("Duplicate profile key. Key '" + key + "' already exists.");
493         }
494         metadataProfiles.put(key, repository);
495     }
496
497     /**
498      * Load the given metadata profile for the current thread.
499      *
500      */

501     public void loadProfile(Object JavaDoc key)
502     {
503         if (!isEnablePerThreadChanges())
504         {
505             throw new MetadataException("Can not load profile with disabled per thread mode");
506         }
507         DescriptorRepository rep = (DescriptorRepository) metadataProfiles.get(key);
508         if (rep == null)
509         {
510             throw new MetadataException("Can not find profile for key '" + key + "'");
511         }
512         currentProfileKey.set(key);
513         setDescriptor(rep);
514     }
515
516     /**
517      * Returns the last activated profile key.
518      * @return the last activated profile key or null if no profile has been loaded
519      * @throws MetadataException if per-thread changes has not been activated
520      * @see #loadProfile(Object)
521      */

522     public Object JavaDoc getCurrentProfileKey() throws MetadataException
523     {
524         if (!isEnablePerThreadChanges())
525         {
526             throw new MetadataException("Call to this method is undefined, since per-thread mode is disabled.");
527         }
528         return currentProfileKey.get();
529     }
530
531     /**
532      * Remove the given metadata profile.
533      */

534     public DescriptorRepository removeProfile(Object JavaDoc key)
535     {
536         return (DescriptorRepository) metadataProfiles.remove(key);
537     }
538
539     /**
540      * Remove all metadata profiles.
541      */

542     public void clearProfiles()
543     {
544         metadataProfiles.clear();
545         currentProfileKey.set(null);
546     }
547
548     /**
549      * Remove all profiles
550      *
551      * @see #removeProfile
552      * @see #addProfile
553      */

554     public void removeAllProfiles()
555     {
556         metadataProfiles.clear();
557         currentProfileKey.set(null);
558     }
559
560     /**
561      * Return the default {@link PBKey} used in convinience method
562      * {@link org.apache.ojb.broker.PersistenceBrokerFactory#defaultPersistenceBroker}.
563      * <br/>
564      * If in {@link JdbcConnectionDescriptor} the
565      * {@link JdbcConnectionDescriptor#isDefaultConnection() default connection}
566      * is enabled, OJB will detect the default {@link org.apache.ojb.broker.PBKey} by itself.
567      *
568      * @see #setDefaultPBKey
569      */

570     public PBKey getDefaultPBKey()
571     {
572         if(defaultPBKey == null)
573         {
574             defaultPBKey = buildDefaultKey();
575         }
576         return defaultPBKey;
577     }
578
579     /**
580      * Set the {@link PBKey} used in convinience method
581      * {@link org.apache.ojb.broker.PersistenceBrokerFactory#defaultPersistenceBroker}.
582      * <br/>
583      * It's only allowed to use one {@link JdbcConnectionDescriptor} with enabled
584      * {@link JdbcConnectionDescriptor#isDefaultConnection() default connection}. In this case
585      * OJB will automatically set the default key.
586      * <br/>
587      * Note: It's recommended to set this key only once and not to change at runtime
588      * of OJB to avoid side-effects.
589      * If set more then one time a warning will be logged.
590      * @throws MetadataException if key was set more than one time
591      */

592     public void setDefaultPBKey(PBKey defaultPBKey)
593     {
594         if(this.defaultPBKey != null)
595         {
596             log.warn("The used default PBKey change. Current key is " + this.defaultPBKey + ", new key will be " + defaultPBKey);
597         }
598         this.defaultPBKey = defaultPBKey;
599         log.info("Set default PBKey for convenience broker creation: " + defaultPBKey);
600     }
601
602     /**
603      * Try to build an default PBKey for convenience PB create method.
604      *
605      * @return PBKey or <code>null</code> if default key was not declared in
606      * metadata
607      */

608     private PBKey buildDefaultKey()
609     {
610         List JavaDoc descriptors = connectionRepository().getAllDescriptor();
611         JdbcConnectionDescriptor descriptor;
612         PBKey result = null;
613         for (Iterator JavaDoc iterator = descriptors.iterator(); iterator.hasNext();)
614         {
615             descriptor = (JdbcConnectionDescriptor) iterator.next();
616             if (descriptor.isDefaultConnection())
617             {
618                 if(result != null)
619                 {
620                     log.error("Found additional connection descriptor with enabled 'default-connection' "
621                             + descriptor.getPBKey() + ". This is NOT allowed. Will use the first found descriptor " + result
622                             + " as default connection");
623                 }
624                 else
625                 {
626                     result = descriptor.getPBKey();
627                 }
628             }
629         }
630
631         if(result == null)
632         {
633             log.info("No 'default-connection' attribute set in jdbc-connection-descriptors," +
634                     " thus it's currently not possible to use 'defaultPersistenceBroker()' " +
635                     " convenience method to lookup PersistenceBroker instances. But it's possible"+
636                     " to enable this at runtime using 'setDefaultKey' method.");
637         }
638         return result;
639     }
640 }
641
Popular Tags