View Javadoc

1   /*
2    * Copyright 2000-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.jetspeed.services.registry;
18  
19  // Java classes
20  import java.io.Reader;
21  import java.util.Hashtable;
22  import java.util.Enumeration;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Vector;
27  
28  import javax.servlet.ServletConfig;
29  
30  //turbine stuff
31  import org.apache.turbine.services.InitializationException;
32  import org.apache.turbine.services.TurbineBaseService;
33  import org.apache.turbine.services.TurbineServices;
34  import org.apache.turbine.services.resources.ResourceService;
35  import org.apache.turbine.services.servlet.ServletService;
36  
37  // Jetspeed classes
38  import org.apache.jetspeed.om.registry.DBRegistry;
39  import org.apache.jetspeed.om.registry.Registry;
40  import org.apache.jetspeed.om.registry.RegistryEntry;
41  import org.apache.jetspeed.om.registry.RegistryException;
42  import org.apache.jetspeed.om.registry.base.BaseRegistry;
43  import org.apache.jetspeed.om.registry.base.LocalRegistry;
44  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
45  import org.apache.jetspeed.services.logging.JetspeedLogger;
46  
47  /***
48   * <p>This is an implementation of the <code>RegistryService</code>
49   * based on the Jetspeed Database Persistence Manager</p>
50   *
51   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
52   * @author <a href="mailto:susinha@cisco.com">Suchisubhra Sinha</a>
53   * @version $Id: DatabaseRegistryService.java,v 1.6 2004/02/23 03:31:50 jford Exp $
54   */
55  public class DatabaseRegistryService
56      extends TurbineBaseService
57      implements RegistryService , FileRegistry 
58  {
59      private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(CastorRegistryService.class.getName());
60          
61      /*** The name of this service */
62      public static String SERVICE_NAME = "DatabaseRegistry";
63  
64      public static final int DEFAULT_VERBOSE = 1;
65  
66      /*** regsitry type keyed list of entries */
67      private Hashtable registries = new Hashtable();
68  
69      /*** The list of default fragments stores for newly created objects */
70      private Hashtable defaults = new Hashtable();
71  
72      /*** The Castor generated RegsitryFragment objects */
73      private Hashtable fragments = new Hashtable();
74  
75      /*** Associates entries with their fragments name for quick lookup */
76      private Hashtable entryIndex = new Hashtable();
77  
78      /*** the Watcher object which monitors the regsitry directory */
79      private DatabaseRegistryWatcher watcher = null;
80  
81      /*** Assign  the default  poolname */
82      private final static String POOL_NAME = "database";
83  
84      
85      /*** controls amount of debug output, the bigger the more output will be generated */
86      private int verbose = DEFAULT_VERBOSE;
87  
88      /*** Base class to implement  */
89      private static Hashtable baseClass = new Hashtable();
90      
91      /***
92       * Returns a Registry object for further manipulation
93       *
94       * @param regName the name of the registry to fetch
95       * @return a Registry object if found by the manager or null
96       */
97      public Registry get(String regName) 
98      {
99          return (Registry) registries.get(regName);
100     }
101 
102     /***
103      *  List all the registry currently available to this service
104      *
105      * @return an Enumeration of registry names.
106      */
107     public Enumeration getNames() 
108     {
109         return registries.keys();
110     }
111 
112     /***
113      * Creates a new RegistryEntry instance compatible with the current
114      * Registry instance implementation
115      *
116      * @param regName the name of the registry to use
117      * @return the newly created RegistryEntry
118      */
119     public RegistryEntry createEntry(String regName) 
120     {
121         RegistryEntry entry = null;
122         Registry registry = (Registry) registries.get(regName);
123 
124         if (registry != null) 
125         {
126             entry = registry.createEntry();
127         }
128 
129         return entry;
130     }
131 
132     
133     /***
134      * Returns a RegistryEntry from the named Registry.
135      * This is a convenience wrapper around {@link
136      * org.apache.jetspeed.om.registry.Registry#getEntry }
137      *
138      * @param regName the name of the registry
139      * @param entryName the name of the entry to retrieve from the registry.
140      * @return a RegistryEntry object if the key is found or null
141      */
142     public RegistryEntry getEntry(String regName, String entryName) 
143     {
144         try 
145         {
146             return ((Registry) registries.get(regName)).getEntry(entryName);
147         } 
148         catch (RegistryException e) 
149         {
150             if (logger.isInfoEnabled()) 
151             {
152                 logger.info(
153                     "RegistryService: Failed to retrieve "
154                         + entryName
155                         + " from "
156                         + regName);
157             }
158         } 
159         catch (NullPointerException e) 
160         {
161             logger.error(
162                 "DatabaseRegistryService: "
163                     + regName
164                     + " registry is not known ");
165             logger.error(e);
166         }
167 
168         return null;
169     }
170             
171     /***
172      * Add a new RegistryEntry in the named Registry.
173      * This is a convenience wrapper around {@link
174      * org.apache.jetspeed.om.registry.Registry#addEntry }
175      *
176      * @param regName the name of the registry
177      * @param entry the Registry entry to add
178      * @exception Sends a RegistryException if the manager can't add
179      *            the provided entry
180      */
181     public void addEntry(String regName, RegistryEntry entry)
182         throws RegistryException 
183     {
184         if (entry == null) 
185         {
186             return;
187         }
188 
189         LocalRegistry registry = (LocalRegistry) registries.get(regName);
190 
191         if (registry != null) 
192         {
193             String fragmentName = (String) entryIndex.get(entry.getName());
194 
195             if (fragmentName == null) 
196             {
197                 // either the entry was deleted or it does not exist
198                 // in both cases, use the default fragment
199                 fragmentName = (String) defaults.get(regName);
200             }
201 
202             RegistryFragment fragment =
203                 (RegistryFragment) fragments.get(fragmentName);
204 
205             //Fragment can be (and sometimes is, but should not be) null
206             if (fragment == null) 
207             {
208                 fragment = new RegistryFragment();
209                 fragment.put(regName, new Vector());
210                 fragments.put(fragmentName, fragment);
211             } 
212             else 
213             {
214                 Vector vectRegistry = (Vector) fragment.get(regName);
215                 if (vectRegistry == null) 
216                 {
217                     fragment.put(regName, new Vector());
218                 }
219             }
220 
221             synchronized (entryIndex) 
222             {
223                 if (registry.hasEntry(entry.getName())) 
224                 {
225                     fragment.setEntry(regName, entry);
226                     registry.setLocalEntry(entry);
227                 } 
228                 else 
229                 {
230                     fragment.addEntry(regName, entry);
231                     registry.addLocalEntry(entry);
232                 }
233 
234                 entryIndex.put(entry.getName(), fragmentName);
235                 // mark this fragment so that it's persisted next time
236                 // the registry watcher is running
237                 fragment.setDirty(true);
238             }
239         }
240     }
241     
242    /***
243     * Deletes a RegistryEntry from the named Registry
244     * This is a convenience wrapper around {@link
245     * org.apache.jetspeed.om.registry.Registry#removeEntry }
246     *
247     * @param regName the name of the registry
248     * @param entryName the name of the entry to remove
249     */
250     public void removeEntry(String regName, String entryName) 
251     {
252         if (entryName == null) 
253         {
254             return;
255         }
256 
257         LocalRegistry registry = (LocalRegistry) registries.get(regName);
258 
259         if (registry != null) 
260         {
261             String fragmentName = (String) entryIndex.get(entryName);
262 
263             if (fragmentName != null) 
264             {
265                 RegistryFragment fragment =
266                     (RegistryFragment) fragments.get(fragmentName);
267 
268                 synchronized (entryIndex) 
269                 {
270                     fragment.removeEntry(regName, entryName);
271                     entryIndex.remove(entryName);
272 
273                     // mark this fragment so that it's persisted next time
274                     // the registry watcher is running
275                     fragment.setDirty(true);
276                 }
277             }
278 
279             // the entry is physically removed, remove the dangling reference
280             registry.removeLocalEntry(entryName);
281         }
282     }
283     
284     /***
285      * This is the early initialization method called by the
286      * Turbine <code>Service</code> framework
287      */
288     public synchronized void init(ServletConfig conf)
289     throws InitializationException 
290     {
291         int refreshRate = 0;
292         Vector names = new Vector();
293     
294         //Ensure that the servlet service is initialized
295         TurbineServices.getInstance().initService(ServletService.SERVICE_NAME, conf);
296 
297         ResourceService serviceConf =
298             ((TurbineServices) TurbineServices.getInstance()).getResources(SERVICE_NAME);
299 
300         //build the map of default fragments, eahc registry must be associated
301         //with at least one fragment
302         try 
303         {            
304             refreshRate = serviceConf.getInt("refreshRate", DEFAULT_REFRESH);
305             ResourceService defaults = serviceConf.getResources("default");
306             Iterator i = defaults.getKeys();
307 
308             while (i.hasNext()) 
309             {
310                 String name = (String) i.next();
311                 // add this name in the list of available registries
312                 
313                 names.add(name);
314                 try 
315                 {
316                     String registryClass =
317                         "org.apache.jetspeed.om.registry.database.BaseJetspeed"
318                             + name
319                             + "Peer";
320 
321                     baseClass.put(
322                         name,
323                         (DBRegistry) Class
324                             .forName(registryClass)
325                             .newInstance());
326                 } 
327                 catch (Exception e) 
328                 {
329                     if (logger.isWarnEnabled()) 
330                     {
331                         logger.warn(
332                             "DatabaseRegistryService: Class "
333                                 + name
334                                 + " not found");
335                     }
336 
337                 }
338 
339             }
340         } 
341         catch (Throwable t) 
342         {
343             throw new InitializationException("Unable to initialize DatabaseRegistryService, missing config keys");
344         }
345     
346         this.watcher = new DatabaseRegistryWatcher();
347         this.watcher.setSubscriber(this);
348 
349         if (refreshRate == 0) 
350         {
351             this.watcher.setDone();
352         } 
353         else 
354         {
355             this.watcher.setRefreshRate(refreshRate);
356         }
357         // changing the base will trigger a synchronous loading of the fragments
358         this.watcher.changeBase(names);
359 
360         //Mark that we are done
361         setInit(true);
362 
363         // load the registries
364         Enumeration en = names.elements();
365 
366         RegistryService localeService =
367             (RegistryService) TurbineServices
368                 .getInstance()
369                 .getService(RegistryService.SERVICE_NAME);
370 
371         while (en.hasMoreElements()) 
372         {
373             String name = (String) en.nextElement();
374             Registry registry = (Registry) registries.get(name);
375 
376             if (registry == null) 
377             {
378                 String registryClass = null;
379                 try 
380                 {
381                     registry = localeService.get(name);
382                 } 
383                 catch (Exception e) 
384                 {
385                     if (logger.isWarnEnabled()) 
386                     {
387                         logger.warn(
388                             "DatabaseRegistryService: Class "
389                                 + registryClass
390                                 + " not found, reverting to default Registry");
391                     }
392                     registry = new BaseRegistry();
393                 }
394                 registries.put(name, registry);
395             }
396 
397             refresh(name);
398         }
399         
400         // Start the directory watcher thread and rely on its refresh process
401         // to completely load all registries
402         if (this.watcher != null) 
403         {
404             this.watcher.start();
405         }
406 
407         if (logger.isDebugEnabled()) 
408         {
409             logger.debug(
410                 "DatabaseRegistryService: early init()....end!, this.getInit()= "
411                     + getInit());
412         }
413     }
414     /***
415          * @return a Map of all fragments keyed by file names
416          */
417     public Map getFragmentMap() 
418     {
419         return (Map) fragments.clone();
420     }
421 
422     /*** Late init method from Turbine Service model */
423     public void init() throws InitializationException 
424     {
425         if (logger.isDebugEnabled()) 
426         {
427             logger.debug("DatabaseRegistryService: Late init called");
428         }
429         while (!getInit()) 
430         {
431             //Not yet...
432             try 
433             {
434                 Thread.sleep(500);
435                 if ((verbose > 2) && logger.isDebugEnabled()) 
436                 {
437                     logger.debug(
438                         "DatabaseRegistryService: Waiting for init of Registry...");
439                 }
440             } 
441             catch (InterruptedException ie) 
442             {
443                 logger.error(ie);
444             }
445         }
446 
447         if (logger.isDebugEnabled()) 
448         {
449             logger.debug("DatabaseRegistryService: We are done");
450         }
451     }
452     
453     /***
454      * This is the shutdown method called by the
455      * Turbine <code>Service</code> framework
456      */
457     public void shutdown() 
458     {
459         this.watcher.setDone();
460 
461         Iterator i = fragments.keySet().iterator();
462         while (i.hasNext()) 
463         {
464             saveFragment((String) i.next());
465         }
466     }
467 
468     /***
469      * Scan all the registry fragments for new entries relevant to
470      * this registry and update its definition.
471      *
472      * @param regName the name of the Registry to refresh
473      */
474     protected void refresh(String regName) 
475     {
476         if (logger.isDebugEnabled()) 
477         {
478             logger.debug(
479                 "DatabaseRegistryService: Updating the "
480                     + regName
481                     + " registry");
482         }
483 
484         int count = 0;
485         int counDeleted = 0;
486         LocalRegistry registry = (LocalRegistry) get(regName);
487 
488         Vector toDelete = new Vector();
489         Iterator i = registry.listEntryNames();
490 
491         while (i.hasNext()) 
492         {
493             toDelete.add(i.next());
494         }
495 
496         if (registry == null) 
497         {
498             logger.error(
499                 "DatabaseRegistryService: Null "
500                     + name
501                     + " registry in refresh");
502             return;
503         }
504 
505         // for each fragment...
506         Enumeration en = fragments.keys();
507         while (en.hasMoreElements()) 
508         {
509             String location = (String) en.nextElement();
510             RegistryFragment fragment =
511                 (RegistryFragment) fragments.get(location);
512             int fragCount = 0;
513 
514             if (!fragment.hasChanged()) 
515             {
516                 if ((verbose > 2) && logger.isDebugEnabled()) 
517                 {
518                     logger.debug(
519                         "DatabaseRegistryService: Skipping fragment "
520                             + location);
521                 }
522 
523                 //remove this fragment entries from the delete list
524                 Vector entries = fragment.getEntries(regName);
525                 i = entries.iterator();
526                 while (i.hasNext()) 
527                 {
528                     toDelete.remove(((RegistryEntry) i.next()).getName());
529                 }
530                 continue;
531             }
532 
533             //the fragment has some changes, iterate over its entries...
534             Vector entries = fragment.getEntries(regName);
535             //... if it has entries related to this regsistry,
536             if (entries != null) 
537             {
538                 // for all these entries
539                 Enumeration en2 = entries.elements();
540                 while (en2.hasMoreElements()) 
541                 {
542                     RegistryEntry entry = (RegistryEntry) en2.nextElement();
543                     // update or add the entry in the registry
544                     try 
545                     {
546                         if (registry.hasEntry(entry.getName())) 
547                         {
548                             if (registry
549                                 .getEntry(entry.getName())
550                                 .equals(entry)) 
551                             {
552                                 if ((verbose > 2)
553                                     && logger.isDebugEnabled()) 
554                                 {
555                                     logger.debug(
556                                         "DatabaseRegistryService: No changes to entry "
557                                             + entry.getName());
558                                 }
559                             } 
560                             else 
561                             {
562                                 if ((verbose > 1)
563                                     && logger.isDebugEnabled()) 
564                                 {
565                                     logger.debug(
566                                         "DatabaseRegistryService: Updating entry "
567                                             + entry.getName()
568                                             + " of class "
569                                             + entry.getClass()
570                                             + " to registry "
571                                             + name);
572                                 }
573                                 registry.setLocalEntry(entry);
574                                 // Initialize the entry index
575                                 this.entryIndex.put(entry.getName(), location);
576                                 ++fragCount;
577                             }
578                         } 
579                         else 
580                         {
581                             registry.addLocalEntry(entry);
582                             // Initialize the entry index
583                             this.entryIndex.put(entry.getName(), location);
584                             ++fragCount;
585 
586                             if ((verbose > 1)
587                                 && logger.isDebugEnabled()) 
588                             {
589                                 logger.debug(
590                                     "DatabaseRegistryService: Adding entry "
591                                         + entry.getName()
592                                         + " of class "
593                                         + entry.getClass()
594                                         + " to registry "
595                                         + name);
596                             }
597                         }
598                     } 
599                     catch (RegistryException e) 
600                     {
601                         logger.error(
602                             "DatabaseRegistryService: RegistryException while adding "
603                                 + entry.getName()
604                                 + "from "
605                                 + location,
606                             e);
607                     }
608                     //remove this entry from the delete list
609                     toDelete.remove(entry.getName());
610                 }
611             }
612 
613             count += fragCount;
614         }
615 
616         //now delete the entries not found in any fragment
617         i = toDelete.iterator();
618         while (i.hasNext()) 
619         {
620             String entryName = (String) i.next();
621 
622             if ((verbose > 1) && logger.isDebugEnabled()) 
623             {
624                 logger.debug(
625                     "DatabaseRegistryService: removing entry " + entryName);
626             }
627             //TODO may  be I will  do it later 
628             //it should  delete only portlets which  is coming from  database
629             
630             //registry.removeLocalEntry(entryName);
631         }
632 
633         if ((verbose > 1) && logger.isDebugEnabled()) 
634         {
635             logger.debug(
636                 "DatabaseRegistryService: Merged "
637                     + count
638                     + " entries and deleted "
639                     + toDelete.size()
640                     + " in "
641                     + name);
642         }
643     }
644 
645     // FileRegistry interface
646 
647     /*** Refresh the state of the registry implementation. Should be called
648      *   whenever the underlying fragments are modified
649      */
650     public void refresh() 
651     {
652         synchronized (watcher) 
653         {
654             Enumeration en = getNames();
655             while (en.hasMoreElements()) 
656             {
657                 refresh((String) en.nextElement());
658             }
659         }
660     }
661 
662     /***
663         * Load and unmarshal a RegistryFragment from the file
664         * @param file the absolute file path storing this fragment
665         */
666     public void loadFragment(String file ) 
667     {
668         try 
669         {            
670             RegistryFragment fragment = createFragment(file);
671             //mark this fragment as changed
672             fragment.setChanged(true);
673 
674             // if we get here, we successfully loaded the new fragment
675             updateFragment(file, fragment);
676 
677         } 
678         catch (Throwable t) 
679         {
680             logger.error(
681                 "DatabaseRegistryService: Could not unmarshal: " + file,
682                 t);
683         }
684     }
685 
686     /***
687         * Read and unmarshal a fragment in memory
688         * @param name the name of this fragment
689         * @param persistent whether this fragment should be persisted on disk in
690         * the registry
691         */
692     public void createFragment(
693         String name,
694         Reader reader,
695         boolean persistent)
696     {
697         String file = null;
698 
699         try 
700         {
701         } 
702         catch (Throwable t) 
703         {
704             logger.error(
705                 "DatabaseRegistryService: Could not create fragment: " + file, t);
706         } 
707         finally 
708         {
709             try 
710             {
711                 reader.close();
712             } 
713             catch (Exception e) 
714             {
715                 logger.error(e); // At least log the exception.
716             }
717         }
718     }
719     /***
720      * Marshal and save a RegistryFragment to disk
721      * @param file the absolute file path storing this fragment
722      */
723     public void saveFragment(String file) 
724     {
725 
726         /***
727          * TODO I will  implement this 
728          * should go  to  database
729          */
730 
731     }
732     /***
733      * Remove a fragment from storage
734      * @param file the absolute file path storing this fragment
735      */
736     public void removeFragment(String file) 
737     {
738         RegistryFragment fragment = (RegistryFragment) fragments.get(file);
739 
740         if (fragment != null) 
741         {
742             synchronized (entryIndex) 
743             {
744                 // clear the entry index
745                 Iterator i = entryIndex.keySet().iterator();
746                 while (i.hasNext()) 
747                 {
748                     if (file.equals(entryIndex.get(i.next()))) 
749                     {
750                         i.remove();
751                     }
752                 }
753 
754                 // make sure the keys & entries are freed for this fragment
755                 // only the entries not replaced by the next registry refresh will
756                 // stay in memory
757                 fragment.clear();
758                 // remove the actual fragment from memory
759                 fragments.remove(file);
760             }
761         }
762     }
763     
764     /***
765      * Updates a fragment in storage and the associated entryIndex
766      */
767     protected void updateFragment(String name, RegistryFragment fragment) 
768     {
769         synchronized (entryIndex) 
770         {
771             // remove the old keys
772             Iterator i = entryIndex.keySet().iterator();
773             while (i.hasNext()) 
774             {
775                 if (name.equals(entryIndex.get(i.next()))) 
776                 {
777                     i.remove();
778                 }
779             }
780             // store the new fragment
781             fragments.put(name, fragment);
782 
783             // recreate the index entries (only this fragment)
784 
785             Enumeration enum = fragment.keys();
786             while (enum.hasMoreElements()) 
787             {
788                 String strReg = (String) enum.nextElement();
789                 Vector v = fragment.getEntries(strReg);
790                 for (int counter = 0; counter < v.size(); counter++) 
791                 {
792                     RegistryEntry str = (RegistryEntry) v.elementAt(counter);
793                     entryIndex.put(str.getName(), name);
794                 }
795             }
796         }
797     }
798 
799     //class  specific implementation 
800     private static List getData(String name) 
801     {
802         List list = null;
803         try 
804         {        
805             DBRegistry BaseClass = (DBRegistry) baseClass.get(name);
806             if (BaseClass != null) 
807             {
808                 list = BaseClass.getXREGDataFromDb();
809             }
810             else
811             {
812                 logger.warn(
813                     "DatabaseRegistryService: Base class  for service " + name + " not found");
814             }
815         } 
816         catch (Exception ex) 
817         {
818             logger.warn(
819                 "DatabaseRegistryService: Base class  for service " + name + " not found");
820         }
821         return list;
822     }
823     
824     private RegistryFragment createFragment(String regName) 
825     {
826         RegistryFragment fragment = (RegistryFragment) fragments.get(regName);
827 
828         //Fragment can be (and sometimes is, but should not be) null
829         if (fragment == null) 
830         {
831             fragment = new RegistryFragment();
832             fragment.put(regName, new Vector());
833         } 
834         else 
835         {
836             Vector vectRegistry = (Vector) fragment.get(regName);
837             if (vectRegistry == null) 
838             {
839                 fragment.put(regName, new Vector());
840             }
841         }
842         List entries = getData(regName);
843         if (entries != null) 
844         {
845             for (int i = 0; i < entries.size(); i++) 
846             {
847                 fragment.setEntry(regName, (RegistryEntry) entries.get(i));
848                 // mark this fragment so that it's persisted next time
849                 // the registry watcher is running
850                 fragment.setDirty(true);
851             }
852         }
853         else
854         {            
855             logger.warn(
856                 "DatabaseRegistryService:no data fouund for service " + name );
857 
858         }
859         return fragment;
860 
861     }
862 }