View Javadoc

1   /*
2    * Copyright 2000-2001,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.psmlmanager.db;
18  
19  import java.sql.Connection;
20  
21  // PSML Manager Service interface
22  import org.apache.jetspeed.services.psmlmanager.PsmlManagerService;
23  
24  
25  // Jetspeed Security service
26  import org.apache.jetspeed.services.JetspeedSecurity;
27  
28  // Profile and ProfileLocator interface
29  import org.apache.jetspeed.om.profile.Profile;
30  import org.apache.jetspeed.om.profile.ProfileLocator;
31  import org.apache.jetspeed.om.profile.QueryLocator;
32  import org.apache.jetspeed.services.Profiler;
33  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
34  import org.apache.jetspeed.services.logging.JetspeedLogger;
35  
36  //Castor defined API
37  import org.apache.jetspeed.om.profile.Portlets;
38  import org.apache.jetspeed.om.profile.PSMLDocument;
39  import org.apache.jetspeed.om.profile.BasePSMLDocument;
40  
41  //turbine stuff
42  import org.apache.turbine.services.TurbineBaseService;
43  import org.apache.turbine.services.InitializationException;
44  import org.apache.turbine.services.TurbineServices;
45  import org.apache.turbine.services.resources.ResourceService;
46  import org.apache.turbine.services.servlet.TurbineServlet;
47  import org.apache.turbine.services.servlet.ServletService;
48  
49  // torque
50  import org.apache.torque.Torque;
51  
52  // jetspeed security
53  import org.apache.jetspeed.om.security.JetspeedUser;
54  import org.apache.jetspeed.om.security.JetspeedUserFactory;
55  import org.apache.jetspeed.om.security.Role;
56  import org.apache.jetspeed.om.security.JetspeedRoleFactory;
57  import org.apache.jetspeed.om.security.Group;
58  import org.apache.jetspeed.om.security.JetspeedGroupFactory;
59  import org.apache.jetspeed.services.security.JetspeedSecurityException;
60  
61  //Servlet API
62  import javax.servlet.ServletConfig;
63  
64  // Torque generated classes
65  import org.apache.jetspeed.om.dbpsml.JetspeedUserProfile;
66  import org.apache.jetspeed.om.dbpsml.JetspeedUserProfilePeer;
67  import org.apache.jetspeed.om.dbpsml.JetspeedRoleProfile;
68  import org.apache.jetspeed.om.dbpsml.JetspeedRoleProfilePeer;
69  import org.apache.jetspeed.om.dbpsml.JetspeedGroupProfile;
70  import org.apache.jetspeed.om.dbpsml.JetspeedGroupProfilePeer;
71  
72  //standard java stuff
73  import java.lang.Thread;
74  import java.util.List;
75  import java.util.Iterator;
76  import java.util.StringTokenizer;
77  import java.util.ArrayList;
78  import java.util.Map;
79  import java.util.HashMap;
80  import java.io.FileReader;
81  import java.io.File;
82  
83  import org.exolab.castor.mapping.Mapping;
84  import org.xml.sax.InputSource;
85  
86  
87  /***
88   * This service is responsible for loading and saving PSML documents. It uses
89   * database to persist the PSML documents.
90   *
91   * @author <a href="mailto:adambalk@cisco.com">Atul Dambalkar</a>
92   * @author <a href="mailto:mvaidya@cisco.com">Medha Vaidya</a>
93   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
94   * @version $Id: DatabasePsmlManagerService.java,v 1.35 2004/02/23 03:32:19 jford Exp $
95   */
96  public class DatabasePsmlManagerService extends TurbineBaseService
97                                       implements DatabasePsmlManager
98  {
99      /***
100      * Static initialization of the logger for this class
101      */    
102     private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(DatabasePsmlManagerService.class.getName());
103     
104     private Map psmlCache = new HashMap();
105 
106     /*** The watcher for the document locations */
107     private CacheRefresher refresher = null;
108 
109     /*** the base refresh rate for documents */
110     private long refreshRate;  // default will be 8 hours
111 
112     private final static String REFRESH_RATE = "refresh-rate";
113     private final static long DEFAULT_REFRESH_RATE = 60 * 60 * 8 * 1000; //8hrs
114 
115     /*** whether caching is allowed */
116     private boolean cachingOn;  // default will be false
117 
118     private final static String CACHING_ON = "caching-on";
119     private final static boolean DEFAULT_CACHING_ON = false;
120 
121     private final static String POOL_NAME = "database";
122 
123     /*** the import/export consumer service **/
124     private PsmlManagerService consumer = null;
125 
126     // castor mapping
127     public static final String DEFAULT_MAPPING = "${webappRoot}/WEB-INF/conf/psml-mapping.xml";
128     String mapFile = null;
129     /*** the Castor mapping file name */
130     private Mapping mapping = null;
131 
132     /*** The pool name to use for database requests. */
133     private String poolName = null;
134 
135     /***
136      * This is the early initialization method called by the
137      * Turbine <code>Service</code> framework
138      */
139     public void init(ServletConfig conf) throws InitializationException
140     {
141         if (getInit())
142         {
143             return;
144         }
145 
146         logger.info("Initializing DatabasePsmlManagerService...");
147         initConfiguration(conf);
148 
149         logger.info("Done initializing DatabasePsmlManagerService.");
150 
151     }
152 
153     /***
154      * Loads the configuration parameters for this service from the
155      * JetspeedResources.properties file.
156      *
157      * @exception throws a <code>InitializationException</code> if the service
158      * fails to initialize
159      */
160     private void initConfiguration(ServletConfig conf)
161                             throws InitializationException
162     {
163 
164         //Ensure that the servlet service is initialized
165         TurbineServices.getInstance().initService(ServletService.SERVICE_NAME, conf);
166 
167         ResourceService serviceConf =
168                       ((TurbineServices)TurbineServices.getInstance())
169                               .getResources(PsmlManagerService.SERVICE_NAME);
170         try
171         {
172             // get configuration parameters from Turbine Resources
173             // we'll use only string accessors so the values can be multiply
174             // specified in the properties files (the first one wins).
175             String value = serviceConf.getString(REFRESH_RATE);
176             refreshRate = DEFAULT_REFRESH_RATE;
177             try
178             {
179                 refreshRate = Long.parseLong(value);
180             }
181             catch (Exception e)
182             {
183                 logger.warn("DatabasePsmlManagerService: error in refresh-rate configuration: using default");
184             }
185 
186             // get the name of the torque database pool to use
187             poolName = serviceConf.getString(POOL_NAME);
188 
189             //find out if caching allowed
190             value = serviceConf.getString(CACHING_ON);
191             cachingOn = DEFAULT_CACHING_ON;
192             try
193             {
194                 cachingOn = value.equals("true");
195             }
196             catch (Exception e)
197             {
198                 logger.warn("DatabasePsmlManagerService: error in caching-on configuration: using default");
199             }
200 
201             // psml castor mapping file
202             mapFile = serviceConf.getString("mapping",DEFAULT_MAPPING);
203             mapFile = TurbineServlet.getRealPath( mapFile );
204             loadMapping();
205         }
206         catch (Throwable t)
207         {
208             logger.error(this + ".init:" , t);
209             throw new InitializationException("Exception initializing DatabasePsmlManagerService" + t);
210         }
211 
212         if (cachingOn)
213         {
214             this.refresher = new CacheRefresher();
215             refresher.start();
216         }
217     }
218 
219     /*** Late init method from Turbine Service model */
220     public void init() throws InitializationException
221     {
222         //Mark that we are done
223         setInit(true);
224 /*
225         try
226         {
227             PsmlImporter importer = new PsmlImporter();
228             importer.run(null);
229         }
230         catch (Exception e)
231         {
232             logger.warn("DatabasePsmlManagerService.init: exception while importing:" , e);
233         }
234   */
235     }
236 
237     protected void loadMapping()
238         throws InitializationException
239     {
240         // test the mapping file and create the mapping object
241 
242         if (mapFile != null)
243         {
244             File map = new File(mapFile);
245             if (logger.isDebugEnabled())
246                 logger.debug("Loading psml mapping file " + mapFile);
247             if (map.exists() && map.isFile() && map.canRead())
248             {
249                 try
250                 {
251                     mapping = new Mapping();
252                     InputSource is = new InputSource( new FileReader(map) );
253                     is.setSystemId( mapFile );
254                     mapping.loadMapping( is );
255                 }
256                 catch (Exception e)
257                 {
258                     logger.error("Error in psml mapping creation",e);
259                     throw new InitializationException("Error in mapping",e);
260                 }
261             }
262             else
263             {
264                 throw new InitializationException("PSML Mapping not found or not a file or unreadable: "+mapFile);
265             }
266         }
267     }
268 
269     /***
270      * This is the shutdown method called by the
271      * Turbine <code>Service</code> framework
272      */
273     public void shutdown()
274     {
275         if (this.refresher != null)
276         {
277             this.refresher.setDone(true);
278         }
279     }
280 
281 
282     /***
283      * A thread implementation of cache refreshing mechanism for database
284      * persisted PSMLs. We have to refresh the cache after specific intervals
285      * if someone manually updates the PSML database.
286      *
287      * @author <a href="mailto:adambalk@cisco.com">Atul Dambalkar</a>
288      */
289     class CacheRefresher extends Thread
290     {
291         private boolean done = false;
292 
293         /***
294          * Constructor to to set the priority.
295          */
296         CacheRefresher()
297         {
298             setDaemon(true);
299             setPriority(Thread.MIN_PRIORITY+1);
300         }
301 
302         /***
303          * We are all done, system is shutting down.
304          */
305         void setDone(boolean done)
306         {
307             this.done = done;
308         }
309 
310         /***
311          * Method as needed for a Thread to run
312          */
313         public void run()
314         {
315             try
316             {
317                 while (!done)
318                 {
319                     if (logger.isDebugEnabled())
320                         logger.debug("Cache Refresher thread sleeping now!");
321                     sleep (refreshRate);
322                     if (logger.isDebugEnabled())
323                         logger.debug("Cache Refresher thread working now!");
324 
325                     try
326                     {
327                         synchronized (this)
328                        {
329                             Iterator i = psmlCache.keySet().iterator();
330 
331                             while(i.hasNext())
332                             {
333                                 String locator = (String)i.next();
334 
335                                 // do refresh for the locator
336                                 PSMLDocument doc =
337                                             refresh(stringToLocator(locator));
338 
339                                 // over write the existing document in cache
340                                 psmlCache.put(locator, doc);
341                             }
342                         }
343                     }
344                     catch (Exception e)
345                     {
346                         logger.warn("DatabasePsmlManagerService.CacheRefresher: Error in cache refresher...", e);
347                     }
348                 }
349             }
350             catch (InterruptedException e)
351             {
352                 if (logger.isDebugEnabled())
353                     logger.debug("DatabasePsmlManagerService.CacheRefresher: recieved interruption, aborting.");
354             }
355         }
356     }
357 
358     /***
359      * Return a unique string identifying this object.
360      */
361     private String locatorToString(ProfileLocator locator)
362     {
363         StringBuffer keybuf = new StringBuffer();
364 
365         JetspeedUser user = locator.getUser();
366         Role role = locator.getRole();
367         Group group = locator.getGroup();
368         String name = locator.getName();
369         String mediaType = locator.getMediaType();
370         String country = locator.getCountry();
371         String language = locator.getLanguage();
372 
373        synchronized (this)
374        {
375             if (user != null)
376             {
377                 keybuf.append("User:").append(user.getUserName());
378            }
379             else if (group != null)
380             {
381                 keybuf.append("Group:").append(group.getName());
382             }
383             else if (role != null)
384             {
385                 keybuf.append("Role:").append(role.getName());
386             }
387 
388             if (name != null)
389             {
390                 keybuf.append('$').append("Page:").append(name);
391             }
392 
393             if (mediaType != null)
394             {
395                 keybuf.append('$').append("MediaType:").append(mediaType);
396             }
397             if (country != null && (! country.equals("-1")))
398             {
399                 keybuf.append('$').append("Country:").append(country);
400             }
401             if (language != null && (! language.equals("-1")))
402             {
403                 keybuf.append('$').append("Language:").append(language);
404             }
405         }
406         if (logger.isDebugEnabled())
407             logger.debug("DatabasePsmlManagerService: Returning locator string: " + keybuf.toString());
408 
409         return keybuf.toString();
410     }
411 
412     private ProfileLocator stringToLocator(String locstr) throws Exception
413     {
414         ProfileLocator locator = Profiler.createLocator();
415         String entity = null;
416 
417         if (logger.isDebugEnabled())
418             logger.debug("DatabasePsmlManagerService: Creating locator for string: " + locstr);
419 
420         StringTokenizer dollarTokens = new StringTokenizer(locstr, "$");
421         while (dollarTokens.hasMoreTokens())
422         {
423             String dollarToken = dollarTokens.nextToken().trim();
424 
425             StringTokenizer colonTokens = new StringTokenizer(dollarToken, ":");
426             String colonToken = colonTokens.nextToken();
427             if (colonToken.equals("User"))
428             {
429                 entity = colonTokens.nextToken().trim();
430                 locator.setUser(JetspeedSecurity.getUser(entity));
431             }
432             else if (colonToken.equals("Group"))
433             {
434                 entity = colonTokens.nextToken().trim();
435                 locator.setGroup(JetspeedSecurity.getGroup(entity));
436             }
437             else if (colonToken.equals("Role"))
438             {
439                 entity = colonTokens.nextToken().trim();
440                 locator.setRole(JetspeedSecurity.getRole(entity));
441             }
442             else if (colonToken.equals("Page"))
443             {
444                 entity = colonTokens.nextToken().trim();
445                 locator.setName(entity);
446             }
447             else if (colonToken.equals("MediaType"))
448             {
449                 entity = colonTokens.nextToken().trim();
450                 locator.setMediaType(entity);
451             }
452             else if (colonToken.equals("Country"))
453             {
454                 entity = colonTokens.nextToken().trim();
455                 locator.setCountry(entity);
456             }
457             else if (colonToken.equals("Language"))
458             {
459                 entity = colonTokens.nextToken().trim();
460                 locator.setLanguage(entity);
461             }
462         }
463         if (logger.isDebugEnabled())
464             logger.debug("DatabasePsmlManagerService: Returning locator for string: " + locatorToString(locator));
465 
466         return locator;
467 
468     }
469 
470     public PSMLDocument getDocument(String name)
471     {
472         // do nothing, deprecated
473         logger.warn("*** NOT SUPPORTED: GETDOC FROM DATABASE PSML MANAGER!!!");
474         return null;
475     }
476 
477     public boolean saveDocument(String fileOrUrl, PSMLDocument doc)
478     {
479         // do nothing, deprecated
480         logger.warn("*** NOT SUPPORTED: SAVING DOC FROM DATABASE PSML MANAGER!!!");
481         return false;
482     }
483 
484     public boolean saveDocument(PSMLDocument doc)
485     {
486         // do nothing, will be deprecated
487         logger.warn("*** NOT SUPPORTED: SAVING DOC FROM DATABASE PSML MANAGER!!!");
488         return false;
489     }
490 
491 
492     /***
493      * Returns a PSML document for the given locator
494      *
495      * @param locator The locator descriptor(ProfileLocator object) of the
496      * document to be retrieved.
497      * @return psmldoc The PSMLDocument object
498      */
499     public PSMLDocument getDocument(ProfileLocator locator)
500     {
501         // check the cache for the req'e document if not available in cache
502         // get the document from database
503 
504         if (locator == null)
505         {
506             String message = "PSMLManager: Must specify a locator";
507             logger.warn("DatabasePsmlManagerService.getDocument: " + message);
508             throw new IllegalArgumentException(message);
509         }
510 
511         PSMLDocument psmldoc = null;
512         String locStr = locatorToString(locator);
513         boolean inCache = false;
514 
515         if (cachingOn)
516         {
517             synchronized (psmlCache)
518             {
519                 // psmldoc = (PSMLDocument)psmlCache.get(locatorToString(locator));
520                 // if we have seached and found nothing, this is cached as a null value
521                 // so check to see if the key is there
522                 inCache = psmlCache.containsKey(locStr);
523                 if (inCache)
524                 {
525                     psmldoc = (PSMLDocument)psmlCache.get(locStr);
526                 }
527              }
528 //        if (Log.getLogger().isDebugEnabled())
529 //            Log.info("DatabasePsmlManagerService.getDocument(): psmlcache: " +
530 //                (inCache ? ((psmldoc == null) ? "null present" : "doc present") : "not in cache") + " : " + locStr);
531 
532             // if in the cache, doc or null, return what's in the cache
533             if (inCache)
534             {
535                 return psmldoc;
536             }
537         }
538 
539 
540         try
541         {
542             return refresh(locator);
543         }
544         catch (Exception e)
545         {
546             logger.warn("DatabasePSMLManagerService.getDocument: exception:", e);
547             throw new RuntimeException("Could not get profile from DB");
548         }
549     }
550 
551     /***
552      * Stores the PSML document in DB for the given profile
553      *
554      * @param profile The profile that holds the PSMLDocument.
555      * @return PSMLDocument The PSMLDocument that got created in DB.
556      */
557     public PSMLDocument createDocument(Profile profile)
558     {
559         return createOrSaveDocument(profile, INSERT);
560     }
561 
562     /***
563      * Update the PSML document in DB for the given profile
564      *
565      * @param profile The profile that holds the PSMLDocument.
566      * @return PSMLDocument The PSMLDocument that got created in DB.
567      */
568     public boolean store(Profile profile)
569     {
570         return createOrSaveDocument(profile, UPDATE) != null;
571     }
572 
573     private PSMLDocument createOrSaveDocument(Profile profile, int operation)
574     {
575         // create record in the database for Portlets for the given
576         // profile/PSMLDocuemnt,use marsheller to create Portlets
577         // object and then put it in database, update the cache
578         if (profile == null)
579         {
580             String message = "PSMLManager: Must specify a profile";
581             logger.warn("DatabasePsmlManagerService.createOrSaveDocument: " + message);
582             throw new IllegalArgumentException(message);
583         }
584 
585         JetspeedUser user = profile.getUser();
586         Role role = profile.getRole();
587         Group group = profile.getGroup();
588         String tableName = null;
589 
590         Connection dbCon = getDbConnection();
591 
592         try
593         {
594             if (user != null)
595             {
596                 tableName = "JETSPEED_USER_PROFILE";
597                 if (operation == INSERT)
598                 {
599                     new JetspeedUserProfilePeer().insert(profile, dbCon);
600                 }
601                 else if (operation == UPDATE)
602                 {
603                     new JetspeedUserProfilePeer().update(profile, dbCon);
604                 }
605             }
606             else if (role != null)
607             {
608                 tableName = "JETSPEED_ROLE_PROFILE";
609                 if (operation == INSERT)
610                 {
611                     new JetspeedRoleProfilePeer().insert(profile, dbCon);
612                 }
613                 else if (operation == UPDATE)
614                 {
615                     new JetspeedRoleProfilePeer().update(profile, dbCon);
616                 }
617             }
618             else if (group != null)
619             {
620                 tableName = "JETSPEED_GROUP_PROFILE";
621                 if (operation == INSERT)
622                 {
623                     new JetspeedGroupProfilePeer().insert(profile, dbCon);
624                 }
625                 else if (operation == UPDATE)
626                 {
627                     new JetspeedGroupProfilePeer().update(profile, dbCon);
628                 }
629             }
630 
631             if (cachingOn)
632             {
633                 // insert successful
634                 synchronized (psmlCache)
635                 {
636                     if (logger.isDebugEnabled())
637                         logger.debug("DatabasePsmlManagerService.createOrSaveDocument: caching document: profile: " + locatorToString(profile));
638                     psmlCache.put(locatorToString(profile), profile.getDocument());
639                 }
640             }
641 
642             return profile.getDocument();
643         }
644         catch (Exception e) // insert failed
645         {
646             logger.warn("DatabasePsmlManagerService.createOrSaveDocument: profile: "
647                         + profile + " tableName: " + tableName, e);
648             throw new RuntimeException("Could not create new profile in DB");
649         }
650         finally
651         {
652             // make sure to release the database connection
653             releaseDbConnection(dbCon);
654         }
655 
656     }
657 
658     /***
659      * Remove the PSMLDocument/profile for given locator object.
660      *
661      * @param locator The profile locator criteria for profile to be removed.
662      */
663     public void removeDocument(ProfileLocator locator)
664     {
665         if (locator == null)
666         {
667             String message = "PSMLManager: Must specify a locator";
668             logger.warn("DatabasePsmlManagerService.removeDocument: " + message);
669             throw new IllegalArgumentException(message);
670         }
671 
672         JetspeedUser user = locator.getUser();
673         Role role = locator.getRole();
674         Group group = locator.getGroup();
675         String tableName = null;
676 
677         // get a database connection
678         Connection dbCon = getDbConnection();
679 
680         try
681         {
682             if (user != null)
683             {
684                 new JetspeedUserProfilePeer().delete(locator, dbCon);
685                 tableName = "JETSPEED_USER_PROFILE";
686             }
687             else if (role != null)
688             {
689                 new JetspeedRoleProfilePeer().delete(locator, dbCon);
690                 tableName = "JETSPEED_ROLE_PROFILE";
691             }
692             else if (group != null)
693             {
694                 new JetspeedGroupProfilePeer().delete(locator, dbCon);
695                 tableName = "JETSPEED_GROUP_PROFILE";
696             }
697 
698             if (cachingOn)
699             {
700                 // Delete successful
701                 synchronized (psmlCache)
702                 {
703                     psmlCache.remove(locatorToString(locator));
704                 }
705             }
706         }
707         catch (Exception e) // insert failed
708         {
709             logger.warn("DatabasePsmlManagerService.removeDocument: profile: "
710                         + locatorToString(locator) + " tableName: " + tableName, e);
711             throw new RuntimeException("Could not delete profile for given locator from DB");
712         }
713         finally
714         {
715             // make sure to release the database connection
716             releaseDbConnection(dbCon);
717         }
718     }
719 
720     /***
721      * Query for a collection of profiles given a profile locator criteria.
722      * Use SQL engine to get the required profiles.
723      *
724      * @param locator The profile locator criteria.
725      * @return Iterator object with the PSMLDocuments satisfying query
726      */
727     public Iterator query(QueryLocator locator)
728     {
729         if (locator == null)
730         {
731             String message = "PSMLManager: Must specify a locator";
732             logger.warn("DatabasePsmlManagerService.query: " + message);
733             throw new IllegalArgumentException(message);
734         }
735 
736         Connection dbCon = getDbConnection();
737 
738         try
739         {
740             List userData = null;
741             List groupData = null;
742             List roleData = null;
743 
744             int queryMode = locator.getQueryMode();
745 
746             List list = new ArrayList();
747 
748             switch (queryMode)
749             {
750                 case QueryLocator.QUERY_USER:
751                     userData = new JetspeedUserProfilePeer().selectOrdered(locator, dbCon);
752                     if (userData != null)
753                     {
754                         list = getProfiles(userData);
755                     }
756                     break;
757 
758                 case QueryLocator.QUERY_GROUP:
759                     groupData = new JetspeedGroupProfilePeer().selectOrdered(locator, dbCon);
760                     if (groupData != null)
761                     {
762                         list = getProfiles(groupData);
763                     }
764                     break;
765 
766                 case QueryLocator.QUERY_ROLE:
767                     roleData = new JetspeedRoleProfilePeer().selectOrdered(locator, dbCon);
768                     if (roleData != null)
769                     {
770                         list = getProfiles(roleData);
771                     }
772                     break;
773 
774 
775                 default:  //QUERY_ALL
776                     userData = new JetspeedUserProfilePeer().selectOrdered(locator, dbCon);
777                     if (userData != null)
778                     {
779                         list.addAll(getProfiles(userData));
780                     }
781 
782                     groupData = new JetspeedGroupProfilePeer().selectOrdered(locator, dbCon);
783                     if (groupData != null)
784                     {
785                         list.addAll(getProfiles(groupData));
786                     }
787 
788                     roleData = new JetspeedRoleProfilePeer().selectOrdered(locator, dbCon);
789                     if (roleData != null)
790                     {
791                         list.addAll(getProfiles(roleData));
792                     }
793 
794                    break;
795             }
796 
797             return list.iterator();
798         }
799         catch (Exception e)
800         {
801             logger.warn("DatabasePsmlManagerService.query: exception" , e);
802         }
803         finally
804         {
805             // make sure to release the databased connection
806             releaseDbConnection(dbCon);
807         }
808 
809         return new ArrayList().iterator();  // return empty non-null iterator
810     }
811 
812     /***
813      * Get profile iterator from given list of objects.
814      *
815      * @param data List of JetspeedUserProfile, JetspeedGroupProfile,
816      * JetspeedRoleProfile, objects
817      * @return List of profiles
818      */
819     private List getProfiles(List data)
820     {
821         List list = new ArrayList();
822 
823         for (int i = 0; i < data.size(); i++)
824         {
825             Object obj = data.get(i);
826             Portlets portlets = null;
827 
828             if (obj instanceof JetspeedUserProfile)
829             {
830                 portlets = DBUtils.bytesToPortlets(((JetspeedUserProfile)obj).getProfile(), this.mapping);
831                 list.add(createUserProfile((JetspeedUserProfile)obj, portlets));
832             }
833             else if (obj instanceof JetspeedGroupProfile)
834             {
835                 portlets = DBUtils.bytesToPortlets(((JetspeedGroupProfile)obj).getProfile(), this.mapping);
836                 list.add(createGroupProfile((JetspeedGroupProfile)obj, portlets));
837             }
838             else if (obj instanceof JetspeedRoleProfile)
839             {
840                 portlets = DBUtils.bytesToPortlets(((JetspeedRoleProfile)obj).getProfile(), this.mapping);
841                 list.add(createRoleProfile((JetspeedRoleProfile)obj, portlets));
842             }
843 
844         }
845         return list;
846     }
847 
848 
849     /***
850      * Get PSMLDocument object for given pagename and portlets.
851      *
852      * @param portlets Portlets for the given page name
853      * @param page page name for this resource
854      * @return PSMLDocument object for given page and portlets
855      */
856     private PSMLDocument getPSMLDocument(String page, Portlets portlets)
857     {
858         PSMLDocument psmldoc = new BasePSMLDocument();
859         psmldoc.setName(page);
860         psmldoc.setPortlets(portlets);
861         return psmldoc;
862     }
863 
864 
865     /***
866      * Given ordered list of locators, find the first document matching
867      * a profile locator, starting from the beginning of the list and working
868      * to the end.
869      *
870      * @param locator The ordered list of profile locators.
871      * @return PSMLDocument object for the first document matching a locator
872      */
873     public PSMLDocument getDocument(List locators)
874     {
875         if (locators == null)
876         {
877             String message = "PSMLManager: Must specify a list of locators";
878             logger.warn("DatabasePsmlManagerService.getDocument: " + message);
879             throw new IllegalArgumentException(message);
880         }
881 
882         // iterate over the list and invoke getDocument(locator) method
883         for (int i = 0; i < locators.size(); i++)
884         {
885             PSMLDocument psmldoc = getDocument((ProfileLocator)locators.get(i));
886             if (psmldoc != null)
887             {
888                 return psmldoc;
889             }
890         }
891         return null;
892     }
893 
894     /***
895      * Returns a PSML document for the given locator, it is called by the cache
896      * refresher
897      *
898      * @param locator The locator descriptor(ProfileLocator object) of the
899      * document to be retrieved.
900      * @return psmldoc The PSMLDocument object
901      */
902     public PSMLDocument refresh(ProfileLocator locator)
903     {
904         // go to database and get the blob, and marshal the Portlets
905 
906         if (locator == null)
907         {
908             String message = "PSMLManager: Must specify a locator";
909             logger.warn("DatabasePsmlManagerService.refresh: " + message);
910             throw new IllegalArgumentException(message);
911         }
912 
913         JetspeedUser user = locator.getUser();
914         Role role = locator.getRole();
915         Group group = locator.getGroup();
916         String tableName = null;
917         List records = null;
918         Portlets portlets = null;
919         PSMLDocument psmldoc = null;
920         String page = null;
921 
922         Connection dbCon = getDbConnection();
923 
924         try
925         {
926             if (user != null)
927             {
928                 tableName = "JETSPEED_USER_PROFILE";
929                 records = new JetspeedUserProfilePeer().select(locator, dbCon);
930                 Iterator iterator = records.iterator();
931                 while (iterator.hasNext())
932                 {
933                     JetspeedUserProfile uprofile =
934                                        (JetspeedUserProfile)iterator.next();
935                     page = uprofile.getPage();
936                     portlets = DBUtils.bytesToPortlets(uprofile.getProfile(), this.mapping);
937                 }
938             }
939             else if (role != null)
940             {
941                 tableName = "JETSPEED_ROLE_PROFILE";
942                 records = new JetspeedRoleProfilePeer().select(locator, dbCon);
943                 Iterator iterator = records.iterator();
944                 while (iterator.hasNext())
945                 {
946                     JetspeedRoleProfile rprofile =
947                                        (JetspeedRoleProfile)iterator.next();
948                     page = rprofile.getPage();
949                     portlets = DBUtils.bytesToPortlets(rprofile.getProfile(), this.mapping);
950                 }
951             }
952             else if (group != null)
953             {
954                 tableName = "JETSPEED_GROUP_PROFILE";
955                 records = new JetspeedGroupProfilePeer().select(locator, dbCon);
956                 Iterator iterator = records.iterator();
957                 while (iterator.hasNext())
958                 {
959                     JetspeedGroupProfile gprofile =
960                                        (JetspeedGroupProfile)iterator.next();
961                     page = gprofile.getPage();
962                     portlets = DBUtils.bytesToPortlets(gprofile.getProfile(), this.mapping);
963                 }
964             }
965 
966             if (page != null && portlets != null)
967             {
968                 psmldoc = getPSMLDocument(page, portlets);
969                 if (cachingOn)
970                 {
971                     synchronized (psmlCache)
972                     {
973                         if (logger.isDebugEnabled())
974                             logger.debug("DatabasePsmlManagerService.refresh: caching document: profile: " + locatorToString(locator));
975                         psmlCache.put(locatorToString(locator), psmldoc);
976                     }
977                 }
978                 return psmldoc;
979             }
980             else
981             {
982                 if (cachingOn)
983                 {
984                     // cache the fact that there is NO document matching this profile
985                     psmlCache.put(locatorToString(locator), null);
986                     if (logger.isDebugEnabled())
987                         logger.debug("DatabasePsmlManagerService.refresh: caching 'document not found': profile: " + locatorToString(locator));
988                 }
989             }
990         }
991         catch (Exception e)
992         {
993             logger.warn("DatabasePsmlManagerService.refresh: profile: " + locatorToString(locator)
994                 + " tableName: " + tableName, e);
995             throw new RuntimeException("Could not refresh profile from DB");
996         }
997         finally
998         {
999             // make sure to release the database connection
1000             releaseDbConnection(dbCon);
1001         }
1002 
1003         if (logger.isDebugEnabled())
1004             logger.debug("DatabasePsmlManagerService.refresh: no document found: profile: "
1005                     + locatorToString(locator));
1006         return null;
1007     }
1008 
1009     /*** Removes all documents for a given user.
1010      *
1011      * @param user The user object.
1012      */
1013     public void removeUserDocuments(JetspeedUser user)
1014     {
1015         Connection dbCon = getDbConnection();
1016 
1017         try
1018         {
1019             if (user != null)
1020             {
1021                 new JetspeedUserProfilePeer().delete(user, dbCon);
1022             }
1023         }
1024         catch (Exception e) // delete failed
1025         {
1026             logger.warn("DatabasePsmlManagerService.removeUserDocuments: exception:", e);
1027             throw new RuntimeException("Could not delete documents for given user from DB");
1028         }
1029         finally
1030         {
1031             // make sure to release the database connection
1032             releaseDbConnection(dbCon);
1033         }
1034 
1035     }
1036 
1037     /*** Removes all documents for a given role.
1038      *
1039      * @param role The role object.
1040      */
1041     public void removeRoleDocuments(Role role)
1042     {
1043         Connection dbCon = getDbConnection();
1044 
1045         try
1046         {
1047             if (role != null)
1048             {
1049                 new JetspeedRoleProfilePeer().delete(role, dbCon);
1050             }
1051         }
1052         catch (Exception e) // delete failed
1053         {
1054             logger.warn("DatabasePsmlManagerService.removeRoleDocuments: exception:", e);
1055             throw new RuntimeException("Could not delete documents for given role from DB");
1056         }
1057         finally
1058         {
1059             // make sure to release the database connection
1060             releaseDbConnection(dbCon);
1061         }
1062     }
1063 
1064     /*** Removes all documents for a given group.
1065      *
1066      * @param group The group object.
1067      */
1068     public void removeGroupDocuments(Group group)
1069     {
1070         Connection dbCon = getDbConnection();
1071 
1072         try
1073         {
1074             if (group != null)
1075             {
1076                 new JetspeedGroupProfilePeer().delete(group, dbCon);
1077             }
1078         }
1079         catch (Exception e) // delete failed
1080         {
1081             logger.warn("DatabasePsmlManagerService.removeGroupDocuments: exception:", e);
1082             throw new RuntimeException("Could not delete documents for given group from DB");
1083         }
1084         finally
1085         {
1086             // make sure to release the database connection
1087             releaseDbConnection(dbCon);
1088         }
1089     }
1090 
1091     /*** Query for a collection of profiles given a profile locator criteria.
1092      *  This method should be used when importing or exporting profiles between services.
1093      *
1094      * @param locator The profile locator criteria.
1095      * @return The count of profiles exported.
1096      */
1097     public int export(PsmlManagerService consumer, QueryLocator locator)
1098     {
1099         Iterator profiles = null;
1100         int count = 0;
1101         try
1102         {
1103             this.consumer = consumer;
1104             profiles = query(locator);
1105 
1106             while (profiles.hasNext() )
1107             {
1108                 Profile profile = (Profile)profiles.next();
1109                 //dumpProfile(profile);
1110                 try
1111                 {
1112                     consumer.createDocument(profile);
1113                     count++;
1114                 }
1115                 catch (Exception ex)
1116                 {
1117                     try
1118                     {
1119                         consumer.store(profile);
1120                         count++;
1121                     }
1122                     catch (Exception e)
1123                     {
1124                         logger.warn("DatabasePsmlManagerService.export: profile: "
1125                                 + profile, ex);
1126                     }
1127                 }
1128             }
1129         }
1130         catch(Exception e)
1131         {
1132             logger.warn("DatabasePsmlManagerService.export: exception:", e);
1133 
1134         }
1135         finally
1136         {
1137         }
1138         return count;
1139     }
1140 
1141 
1142     public Mapping getMapping()
1143     {
1144         return this.mapping;
1145     }
1146 
1147     /***
1148      * Creates a user profile from a JetspeedUserProfile database object.
1149      *
1150      * @param entity The user profile entity in the database.
1151      * @param portlets The PSML blob.
1152      * @return A new profile object representing the locator and PSML blob.
1153      */
1154     public Profile createUserProfile(JetspeedUserProfile entity, Portlets portlets)
1155     {
1156         Profile profile = Profiler.createProfile();
1157         try
1158         {
1159             JetspeedUser user = JetspeedSecurity.getUser(entity.getUserName());
1160             if (null == user)
1161             {
1162                 user = JetspeedUserFactory.getInstance();
1163                 user.setUserName(entity.getUserName());
1164             }
1165             profile.setUser(user);
1166 
1167             profile.setMediaType(entity.getMediaType());
1168             profile.setLanguage(entity.getLanguage());
1169             profile.setCountry(entity.getCountry());
1170             profile.setName(entity.getPage());
1171             profile.setDocument(getPSMLDocument(entity.getPage(), portlets));
1172         }
1173         catch (JetspeedSecurityException e)
1174         {
1175         }
1176         return profile;
1177     }
1178 
1179     /***
1180      * Creates a group profile from a JetspeedGroupProfile database object.
1181      *
1182      * @param entity The group profile entity in the database.
1183      * @param portlets The PSML blob.
1184      * @return A new profile object representing the locator and PSML blob.
1185      */
1186     public Profile createGroupProfile(JetspeedGroupProfile entity, Portlets portlets)
1187     {
1188         Profile profile = Profiler.createProfile();
1189         try
1190         {
1191             Group group = JetspeedSecurity.getGroup(entity.getGroupName());
1192             if (null == group)
1193             {
1194                 group = JetspeedGroupFactory.getInstance();
1195                 group.setName(entity.getGroupName());
1196             }
1197             profile.setGroup(group);
1198             profile.setMediaType(entity.getMediaType());
1199             profile.setLanguage(entity.getLanguage());
1200             profile.setCountry(entity.getCountry());
1201             profile.setName(entity.getPage());
1202             profile.setDocument(getPSMLDocument(entity.getPage(), portlets));
1203         }
1204         catch (JetspeedSecurityException e)
1205         {
1206         }
1207         return profile;
1208     }
1209 
1210     /***
1211      * Creates a role profile from a JetspeedRoleProfile database object.
1212      *
1213      * @param entity The group profile entity in the database.
1214      * @param portlets The PSML blob.
1215      * @return A new profile object representing the locator and PSML blob.
1216      */
1217     public Profile createRoleProfile(JetspeedRoleProfile entity, Portlets portlets)
1218     {
1219         Profile profile = Profiler.createProfile();
1220         try
1221         {
1222             Role role = JetspeedSecurity.getRole(entity.getRoleName());
1223             if (null == role)
1224             {
1225                 role = JetspeedRoleFactory.getInstance();
1226                 role.setName(entity.getRoleName());
1227             }
1228             profile.setRole(role);
1229             profile.setMediaType(entity.getMediaType());
1230             profile.setLanguage(entity.getLanguage());
1231             profile.setCountry(entity.getCountry());
1232             profile.setName(entity.getPage());
1233             profile.setDocument(getPSMLDocument(entity.getPage(), portlets));
1234         }
1235         catch (JetspeedSecurityException e)
1236         {
1237         }
1238         return profile;
1239     }
1240 
1241 
1242     /***
1243     * Get a database connection to the default or specifed torque database pool
1244     */
1245     private Connection getDbConnection()
1246     {
1247         try
1248         {
1249             // use the default pool if not specified
1250             if (poolName == null)
1251             {
1252                 return Torque.getConnection();
1253             }
1254 
1255             // otherwise use the specified pool name
1256             else
1257             {
1258                 return Torque.getConnection(poolName);
1259             }
1260         }
1261         catch (Exception e)
1262         {
1263             logger.warn("DatabasePsmlManagerService.getDbConnection: exception: " + e);
1264             return null;
1265         }
1266     }
1267 
1268     /***
1269     * Release a previously gotten database connection back to the torque pool
1270     */
1271     private void releaseDbConnection(Connection connection)
1272     {
1273         Torque.closeConnection(connection);
1274     }
1275 }
1276