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;
18  
19  //Jetspeed stuff
20  import org.apache.jetspeed.om.profile.ProfileLocator;
21  import org.apache.jetspeed.om.profile.QueryLocator;
22  import org.apache.jetspeed.util.FileCopy;
23  import org.apache.jetspeed.util.DirectoryUtils;
24  import org.apache.jetspeed.services.Profiler;
25  import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
26  import org.apache.jetspeed.services.logging.JetspeedLogger;
27  import org.apache.jetspeed.services.JetspeedSecurity;
28  import org.apache.jetspeed.services.resources.JetspeedResources;
29  
30  //Castor defined API
31  import org.apache.jetspeed.om.profile.Portlets;
32  import org.apache.jetspeed.om.profile.*;
33  
34  //turbine stuff
35  import org.apache.turbine.services.TurbineBaseService;
36  import org.apache.turbine.services.InitializationException;
37  import org.apache.turbine.services.TurbineServices;
38  import org.apache.turbine.services.servlet.TurbineServlet;
39  import org.apache.turbine.services.resources.ResourceService;
40  import org.apache.turbine.services.servlet.ServletService;
41  
42  import org.apache.jetspeed.om.security.JetspeedUser;
43  import org.apache.jetspeed.om.security.Role;
44  import org.apache.jetspeed.om.security.JetspeedRoleFactory;
45  import org.apache.jetspeed.om.security.Group;
46  import org.apache.jetspeed.om.security.JetspeedGroupFactory;
47  import org.apache.jetspeed.om.security.JetspeedUserFactory;
48  
49  //castor support
50  import org.exolab.castor.xml.MarshalException;
51  import org.exolab.castor.xml.Unmarshaller;
52  import org.exolab.castor.xml.Marshaller;
53  import org.exolab.castor.xml.ValidationException;
54  import org.exolab.castor.mapping.Mapping;
55  import org.exolab.castor.mapping.MappingException;
56  import org.xml.sax.InputSource;
57  import org.xml.sax.SAXException;
58  import org.w3c.dom.Document;
59  import org.w3c.dom.Node;
60  
61  // serialization support
62  import org.apache.xml.serialize.Serializer;
63  import org.apache.xml.serialize.XMLSerializer;
64  import org.apache.xml.serialize.OutputFormat;
65  
66  //standard java stuff
67  import java.io.File;
68  import java.io.Reader;
69  import java.io.FileReader;
70  import java.io.Writer;
71  import java.io.FileOutputStream;
72  import java.io.OutputStreamWriter;
73  import java.io.IOException;
74  import java.util.Iterator;
75  import java.util.List;
76  import java.util.LinkedList;
77  import javax.servlet.ServletConfig;
78  import javax.xml.parsers.DocumentBuilder;
79  import javax.xml.parsers.DocumentBuilderFactory;
80  import javax.xml.parsers.ParserConfigurationException;
81  
82  import org.apache.jetspeed.cache.FileCache;
83  import org.apache.jetspeed.cache.FileCacheEventListener;
84  import org.apache.jetspeed.cache.FileCacheEntry;
85  
86  
87  /***
88   * This service is responsible for loading and saving PSML documents.
89   *
90   * @author <a href="mailto:raphael@apache.org">Raphaël Luta</a>
91   * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
92   * @author <a href="mailto:sgala@apache.org">Santiago Gala</a>
93   * @version $Id: CastorPsmlManagerService.java,v 1.44 2004/03/31 00:23:02 jford Exp $
94   */
95  public class CastorPsmlManagerService extends TurbineBaseService
96                                        implements FileCacheEventListener,
97                                                   PsmlManagerService
98  {
99      /***
100      * Static initialization of the logger for this class
101      */    
102     private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(CastorPsmlManagerService.class.getName());
103     
104     // resource path constants
105     protected static final String PATH_GROUP              = "group";
106     protected static final String PATH_ROLE               = "role";
107     protected static final String PATH_USER               = "user";
108 
109     // configuration keys
110     protected final static String CONFIG_ROOT             = "root";
111     protected final static String CONFIG_EXT              = "ext";
112     protected final static String CONFIG_SCAN_RATE        = "scanRate";
113     protected final static String CONFIG_CACHE_SIZE       = "cacheSize";
114 
115     // default configuration values
116     public final static String DEFAULT_ROOT             = "/WEB-INF/psml";
117     public final static String DEFAULT_EXT              = ".psml";
118 
119     // default resource
120     public final static String DEFAULT_RESOURCE         = "default.psml";
121 
122     // the root psml resource directory
123     protected String root;
124     // base store directory
125     protected File rootDir = null;
126     // file extension
127     protected String ext;
128 
129     /*** The documents loaded by this manager */
130     protected FileCache documents = null;
131 
132     /*** the output format for pretty printing when saving registries */
133     protected OutputFormat format = null;
134 
135     /*** the base refresh rate for documents */
136     protected long scanRate = 1000 * 60; // every minute
137 
138     /*** the default cache size */
139     protected int cacheSize = 100;
140 
141     /*** the import/export consumer service **/
142     protected PsmlManagerService consumer = null;
143     protected boolean importFlag = false;
144 
145     // castor mapping
146     public static final String DEFAULT_MAPPING = "${webappRoot}/WEB-INF/conf/psml-mapping.xml";
147     protected String mapFile = null;
148 
149     /*** the Castor mapping file name */
150     protected Mapping mapping = null;
151 
152     /*** The default encoding used to serialize PSML files to disk */
153     protected String defaultEncoding = JetspeedResources.getString(JetspeedResources.CONTENT_ENCODING_KEY, "utf-8");
154 
155     /***
156      * This is the early initialization method called by the
157      * Turbine <code>Service</code> framework
158      */
159     public void init( ServletConfig conf ) throws InitializationException
160     {
161         if (getInit())
162         {
163             return;
164         }
165 
166         //Ensure that the servlet service is initialized
167         TurbineServices.getInstance().initService(ServletService.SERVICE_NAME, conf);
168 
169         // get configuration parameters from Jetspeed Resources
170         ResourceService serviceConf = ((TurbineServices)TurbineServices.getInstance())
171                                                      .getResources(PsmlManagerService.SERVICE_NAME);
172         // get the PSML Root Directory
173         this.root = serviceConf.getString( CONFIG_ROOT, DEFAULT_ROOT );
174         this.rootDir = new File(root);
175 
176         //If the rootDir does not exist, treat it as context relative
177         if ( !rootDir.exists() )
178         {
179             try
180             {
181                 this.rootDir = new File(conf.getServletContext().getRealPath(root));
182             }
183             catch (Exception e)
184             {
185                 // this.rootDir = new File("./webapp" + this.rootDir.toString());
186             }
187         }
188         //If it is still missing, try to create it
189         if (!rootDir.exists())
190         {
191             try
192             {
193                 rootDir.mkdirs();
194             }
195             catch (Exception e)
196             {
197             }
198         }
199 
200         // get default extension
201         this.ext = serviceConf.getString( CONFIG_EXT, DEFAULT_EXT );
202 
203         // create the serializer output format
204         this.format = new OutputFormat();
205         format.setIndenting(true);
206         format.setIndent(4);
207         format.setLineWidth(0);
208 
209         // psml castor mapping file
210         mapFile = serviceConf.getString("mapping",DEFAULT_MAPPING);
211         mapFile = TurbineServlet.getRealPath( mapFile );
212         loadMapping();
213 
214         this.scanRate = serviceConf.getLong(CONFIG_SCAN_RATE, this.scanRate);
215         this.cacheSize= serviceConf.getInt(CONFIG_CACHE_SIZE, this.cacheSize);
216 
217         documents = new FileCache(this.scanRate, this.cacheSize);
218         documents.addListener(this);
219         documents.startFileScanner();
220 
221 
222         //Mark that we are done
223         setInit(true);
224 
225         // Test
226         //testCases();
227 
228     }
229 
230 
231     /*** Late init method from Turbine Service model */
232     public void init() throws InitializationException
233     {
234         while( !getInit() )
235         {
236             //Not yet...
237             try
238             {
239                 Thread.sleep( 500 );
240             }
241             catch (InterruptedException ie )
242             {
243                 logger.error("Exception", ie);
244             }
245         }
246     }
247 
248 
249     /***
250      * This is the shutdown method called by the
251      * Turbine <code>Service</code> framework
252      */
253     public void shutdown()
254     {
255         documents.stopFileScanner();
256     }
257 
258     /***
259      * Returns a PSML document of the given name.
260      * For this implementation, the name must be the document
261      * URL or absolute filepath
262      *
263      * @deprecated
264      * @param name the name of the document to retrieve
265      */
266     public PSMLDocument getDocument( String name )
267     {
268         if (name == null)
269         {
270             String message = "PSMLManager: Must specify a name";
271             logger.error( message );
272             throw new IllegalArgumentException( message );
273         }
274 
275         if (logger.isDebugEnabled())
276         {
277             logger.debug( "PSMLManager: asked for " + name );
278         }
279 
280         PSMLDocument doc = null;
281 
282         doc = (PSMLDocument)documents.getDocument(name);
283 
284         if (doc == null)
285         {
286             doc = loadDocument(name);
287 
288             synchronized (documents)
289             {
290                 // store the document in the hash and reference it to the watcher
291                 try
292                 {
293                     documents.put(name, doc);
294                 }
295                 catch (java.io.IOException e)
296                 {
297                     logger.error("Error putting document", e);
298                 }
299             }
300         }
301 
302         return doc;
303     }
304 
305     /***
306      * Returns a cached PSML document for the given locator
307      * 
308      * @param locator The locator descriptor of the document to be retrieved.
309      * @return PSML document  from cache (or disk if not yet cached)
310      */
311     public PSMLDocument getDocument( ProfileLocator locator)
312     {
313         return getDocument(locator, true);
314     }
315 
316     /***
317      * Returns a PSML document for the given locator
318      * 
319      * @param locator   The locator descriptor of the document to be retrieved.
320      * @param getCached Look in the cache (true) or umarshall a fresh copy from disk (false)
321      * @return 
322      */
323     protected PSMLDocument getDocument( ProfileLocator locator, boolean getCached )
324     {
325         if (locator == null)
326         {
327             String message = "PSMLManager: Must specify a name";
328             logger.error( message );
329             throw new IllegalArgumentException( message );
330         }
331         File base = this.rootDir;
332         String path = mapLocatorToFile(locator);
333         File file = new File(base, path);
334         String name = null;
335 
336         try
337         {
338             name = file.getCanonicalPath();
339         }
340         catch (IOException e)
341         {
342             logger.error("PSMLManager: unable to resolve file path for "+ file);
343         }
344 
345         if (logger.isDebugEnabled())
346         {
347             logger.debug("PSMLManager: calculated resource:" + path + ". Base: " + base + " File: " + name);
348         }
349 
350         PSMLDocument doc = null;
351         Profile profile = null;
352 
353         if (getCached == true)
354         {
355             profile = (Profile)documents.getDocument(name);
356         }
357 
358         if (profile == null)
359         {
360             doc = loadDocument(name);
361             if (null == doc)
362             {
363                 if (logger.isWarnEnabled())
364                 {
365                     logger.warn( "PSMLManager: " + name + " not found, returning null document" );
366                 }
367                 return null;
368             }
369 
370             synchronized (documents)
371             {
372                 // store the document in the hash and reference it to the watcher
373                 Profile newProfile = createProfile(locator);
374                 newProfile.setDocument(doc);
375                 try
376                 {
377                     documents.put(name, newProfile);
378                 }
379                 catch (IOException e)
380                 {
381                     logger.error("Error putting document", e);
382                 }
383             }
384         }
385         else
386         {
387             doc = profile.getDocument();
388         }
389 
390         return doc;
391     }
392 
393     /***
394      * Loads a PSML document from disk bypassing the cache
395      * 
396      * @param locator
397      * @return PSML document from disk
398      */
399     public PSMLDocument refresh(ProfileLocator locator)
400     {
401         if (logger.isDebugEnabled())
402         {
403             logger.debug("CastorPsmlManagerService: psml document refreshed from disk: " + locator.getPath());
404         }
405         return getDocument(locator, false);
406     }
407 
408     /***
409      * Load a PSMLDOcument from disk
410      *
411      * @param fileOrUrl a String representing either an absolute URL or an
412      * absolute filepath
413      */
414     protected PSMLDocument loadDocument(String fileOrUrl)
415     {
416         PSMLDocument doc = null;
417 
418         if (fileOrUrl!=null)
419         {
420             if (!fileOrUrl.endsWith(DEFAULT_EXT))
421             {
422                 fileOrUrl = fileOrUrl.concat(DEFAULT_EXT);
423             }
424 
425             // load the document and add it to the watcher
426             // we'll assume the name is the the location of the file
427 
428             File f = getFile(fileOrUrl);
429             if (null == f)
430                 return null;
431 
432             doc = new BasePSMLDocument();
433             doc.setName(fileOrUrl);
434 
435             // now that we have a file reference, try to load the serialized PSML
436             Portlets portlets = null;
437             try
438             {
439                 DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
440                 DocumentBuilder builder = dbfactory.newDocumentBuilder();
441 
442                 Document d = builder.parse(f);
443 
444                 Unmarshaller unmarshaller = new Unmarshaller(this.mapping);
445                 portlets = (Portlets)unmarshaller.unmarshal((Node) d);
446 
447                 doc.setPortlets(portlets);
448 
449             }
450             catch (IOException e)
451             {
452                 logger.error("PSMLManager: Could not load the file "+f.getAbsolutePath(), e);
453                 doc = null;
454             }
455             catch (MarshalException e)
456             {
457                 logger.error("PSMLManager: Could not unmarshal the file "+f.getAbsolutePath(), e);
458                 doc = null;
459             }
460             catch (MappingException e)
461             {
462                 logger.error("PSMLManager: Could not unmarshal the file "+f.getAbsolutePath(), e);
463                 doc = null;
464             }
465             catch (ValidationException e)
466             {
467                 logger.error("PSMLManager: document "+f.getAbsolutePath()+" is not valid", e);
468                 doc = null;
469             }
470             catch (ParserConfigurationException e)
471             {
472                 logger.error("PSMLManager: Could not load the file "+f.getAbsolutePath(), e);
473                 doc = null;
474             }
475             catch (SAXException e)
476             {
477                 logger.error("PSMLManager: Could not load the file "+f.getAbsolutePath(), e);
478                 doc = null;
479             }
480         }
481 
482         return doc;
483     }
484 
485     /*** Store the PSML document on disk, using its locator
486      *
487      * @param profile the profile locator description.
488      * @return true if the operation succeeded
489      */
490     public boolean store(Profile profile)
491     {
492         PSMLDocument doc = profile.getDocument();
493 
494         File base = this.rootDir;
495         String path = mapLocatorToFile(profile);
496 
497         File file = new File(base, path);
498         String fullpath = null;
499 
500         try
501         {
502             fullpath = file.getCanonicalPath();
503         }
504         catch (IOException e)
505         {
506             logger.error("PSMLManager: unable to resolve file path for "+ file);
507         }
508 
509         boolean ok = saveDocument(fullpath, doc);
510 
511         // update it in cache
512         synchronized (documents)
513         {
514             try
515             {
516                 documents.put(fullpath, profile);
517             }
518             catch (IOException e)
519             {
520                 logger.error("Error storing document", e);
521             }
522         }
523 
524         return ok;
525     }
526 
527     /*** Save the PSML document on disk, using its name as filepath
528      * @deprecated
529      * @param doc the document to save
530      */
531     public boolean saveDocument(PSMLDocument doc)
532     {
533         return saveDocument(doc.getName(), doc);
534     }
535 
536     /*** Save the PSML document on disk to the specififed fileOrUrl
537      *
538      * @param fileOrUrl a String representing either an absolute URL
539      * or an absolute filepath
540      * @param doc the document to save
541      */
542     public boolean saveDocument(String fileOrUrl, PSMLDocument doc)
543     {
544         boolean success = false;
545 
546         if (doc == null) return false;
547         File f = getFile(fileOrUrl);
548         if (f == null)
549         {
550             f = new File(fileOrUrl);
551         }
552 
553         OutputStreamWriter writer = null;
554         FileOutputStream fos = null;
555         try
556         {
557             String encoding = this.defaultEncoding;
558             fos = new FileOutputStream(f);
559             writer = new OutputStreamWriter(fos, encoding);
560 
561             save(writer, doc.getPortlets());
562             success = true;
563         }
564         catch (MarshalException e)
565         {
566             logger.error("PSMLManager: Could not marshal the file "+f.getAbsolutePath(), e);
567         }
568         catch (MappingException e)
569         {
570             logger.error("PSMLManager: Could not marshal the file "+f.getAbsolutePath(), e);
571         }
572         catch (ValidationException e)
573         {
574             logger.error("PSMLManager: document "+f.getAbsolutePath()+" is not valid", e);
575         }
576         catch (IOException e)
577         {
578             logger.error("PSMLManager: Could not save the file "+f.getAbsolutePath(), e);
579         }
580         catch (Exception e)
581         {
582             logger.error("PSMLManager: Error while saving  "+f.getAbsolutePath(), e);
583         }
584         finally
585         {
586             try { writer.close(); } catch (IOException e) {}
587             try { if(fos != null) { fos.close(); } } catch (IOException e) {}
588         }
589 
590         return success;
591     }
592 
593     /*** Deserializes a PSML structure read from the reader using Castor
594      *  XML unmarshaller
595      *
596      * @param reader the reader to load the PSML from
597      * @param the loaded portlets structure or null
598      */
599     protected Portlets load(Reader reader)
600         throws IOException, MarshalException, ValidationException, MappingException
601     {
602         Unmarshaller unmarshaller = new Unmarshaller(this.mapping);
603         Portlets portlets = (Portlets)unmarshaller.unmarshal(reader);
604         return portlets;
605     }
606 
607     protected void loadMapping()
608         throws InitializationException
609     {
610         // test the mapping file and create the mapping object
611 
612         if (mapFile != null)
613         {
614             File map = new File(mapFile);
615             if (logger.isDebugEnabled())
616             {
617                 logger.debug("PSMLManager: Loading psml mapping file "+mapFile);
618             }
619             if (map.exists() && map.isFile() && map.canRead())
620             {
621                 try
622                 {
623                     mapping = new Mapping();
624                     InputSource is = new InputSource( new FileReader(map) );
625                     is.setSystemId( mapFile );
626                     mapping.loadMapping( is );
627                 }
628                 catch (Exception e)
629                 {
630                     logger.error("PSMLManager: Error in psml mapping creation", e);
631                     throw new InitializationException("Error in mapping",e);
632                 }
633             }
634             else
635             {
636                 throw new InitializationException("PSML Mapping not found or not a file or unreadable: "+mapFile);
637             }
638         }
639     }
640 
641     /*** Serializes a PSML structure using the specified writer with Castor
642      *  XML marshaller and a Xerces serializer for pretty printing
643      *
644      * @param writer the writer to use for serialization
645      * @param portlets the structure to save
646      */
647     protected void save(Writer writer, Portlets portlets)
648         throws IOException, MarshalException, ValidationException, MappingException
649     {
650         String encoding = this.defaultEncoding;
651 
652         if (portlets != null)
653         {
654             format.setEncoding(encoding);
655             Serializer serializer = new XMLSerializer(writer, format);
656             Marshaller marshaller = new Marshaller(serializer.asDocumentHandler());
657             marshaller.setMapping(this.mapping);
658             marshaller.marshal(portlets);
659         }
660     }
661 
662     /*** Tests wether the passed argument is an URL string or a file name
663      *  and returns the corresponding file object, using diskcache for
664      *  remote URLs
665      *
666      *  @param fileOrUrl the URL string or file path
667      *  @return a File object. This file may not exist on disk.
668      */
669     protected File getFile(String fileOrUrl)
670     {
671         File f = null;
672 
673         f = new File(fileOrUrl);
674 
675         if (f.exists())
676         {
677             return f;
678         }
679 
680         return null;
681     }
682 
683     /*** Create a new document.
684      *
685      * @param profile The description and default value for the new document.
686      * @return The newly created document;
687      */
688     public PSMLDocument createDocument( Profile profile )
689     {
690         File base = this.rootDir;
691         String path = mapLocatorToFile((ProfileLocator)profile);
692 
693         if (logger.isDebugEnabled())
694         {
695             logger.debug("PSMLManager: Create document for profile " + profile +", calculated path: " + path);
696         }
697 
698         File file = new File(base, path);
699         String name = null;
700 
701         try
702         {
703             name = file.getCanonicalPath();
704         }
705         catch (IOException e)
706         {
707             logger.error("PSMLManager: unable to resolve file path for "+ file);
708         }
709 
710         PSMLDocument template = profile.getDocument();
711         PSMLDocument doc = new BasePSMLDocument( name, template.getPortlets() );
712         try
713         {
714             String parent = file.getParent();
715             File filePath = new File(parent);
716             filePath.mkdirs();
717             if (template.getName() != null)
718             {
719                 try
720                 {
721                     File source = new File(template.getName());
722                     if (source.exists())
723                     {
724                         FileCopy.copy( template.getName(), name );
725                     }
726                 }
727                 catch (Exception e)
728                 {}
729             }
730             else
731             {
732                 doc.setName(name);
733             }
734             saveDocument(doc);
735         }
736         catch (Exception e)
737         {
738             logger.error("PSMLManager: Failed to save document: " , e);
739         }
740         return doc;
741     }
742 
743     /*** Given a ordered list of locators, find the first document matching
744      *  a profile locator, starting from the beginning of the list and working
745      *  to the end.
746      *
747      * @param locator The ordered list of profile locators.
748      */
749     public PSMLDocument getDocument( List locators )
750     {
751         PSMLDocument doc=null;
752 
753         Iterator i = locators.iterator();
754         while ((doc==null)&&(i.hasNext()))
755         {
756             doc=getDocument((ProfileLocator)i.next());
757         }
758 
759         return doc;
760     }
761 
762     /*** Removes a document.
763      *
764      * @param locator The description of the profile resource to be removed.
765      */
766     public void removeDocument( ProfileLocator locator )
767     {
768         // remove a single document
769         String fileName = mapLocatorToFile(locator);
770 
771         File base = this.rootDir;
772         File file = new File(base, fileName);
773         String name = null;
774 
775         try
776         {
777             name = file.getCanonicalPath();
778         }
779         catch (IOException e)
780         {
781             logger.error("PSMLManager: unable to resolve file path for "+ file);
782         }
783 
784 
785         synchronized (documents)
786         {
787             documents.remove(name);
788         }
789 
790         file.delete();
791 
792     }
793 
794     /*** Removes all documents for a given user.
795      *
796      * @param user The user object.
797      */
798     public void removeUserDocuments( JetspeedUser user )
799     {
800         ProfileLocator locator = Profiler.createLocator();
801         locator.setUser(user);
802         StringBuffer buffer = new StringBuffer();
803         buffer.append(PATH_USER);
804         String name = user.getUserName();
805         if (null != name && name.length() > 0)
806         {
807             buffer.append(File.separator)
808                 .append(name);
809         }
810         else
811             return; // don't delete the entire user directories
812 
813         String path = buffer.toString();
814         File base = this.rootDir;
815         File file = new File(base, path);
816 
817         try
818         {
819             name = file.getCanonicalPath();
820         }
821         catch (IOException e)
822         {
823             logger.error("PSMLManager: unable to resolve file path for "+ file);
824         }
825 
826 
827         synchronized (documents)
828         {
829             DirectoryUtils.rmdir(name);
830             Iterator it = documents.getIterator();
831             while (it.hasNext())
832             {
833                 FileCacheEntry entry = (FileCacheEntry)it.next();
834                 if (null == entry)
835                 {
836                     continue;
837                 }
838                 Profile profile = (Profile)entry.getDocument();
839                 if (null == profile)
840                 {
841                     continue;
842                 }
843                 JetspeedUser pUser = profile.getUser();
844                 if (null != pUser && pUser.getUserName().equals(user.getUserName()))
845                 {
846                     documents.remove(profile.getDocument().getName());
847                 }
848             }
849         }
850 
851     }
852 
853     /*** Removes all documents for a given role.
854      *
855      * @param role The role object.
856      */
857     public void removeRoleDocuments( Role role )
858     {
859         ProfileLocator locator = Profiler.createLocator();
860         locator.setRole(role);
861         StringBuffer buffer = new StringBuffer();
862         buffer.append(PATH_ROLE);
863         String name = role.getName();
864         if (null != name && name.length() > 0)
865         {
866             buffer.append(File.separator)
867                 .append(name);
868         }
869         else
870             return; // don't delete the entire role directories
871 
872         String path = buffer.toString();
873         File base = this.rootDir;
874         File file = new File(base, path);
875 
876         try
877         {
878             name = file.getCanonicalPath();
879         }
880         catch (IOException e)
881         {
882             logger.error("PSMLManager: unable to resolve file path for "+ file);
883         }
884 
885 
886         synchronized (documents)
887         {
888             DirectoryUtils.rmdir(name);
889             Iterator it = documents.getIterator();
890             while (it.hasNext())
891             {
892                 FileCacheEntry entry = (FileCacheEntry)it.next();
893                 if (null == entry)
894                 {
895                     continue;
896                 }
897                 Profile profile = (Profile)entry.getDocument();
898                 if (null == profile)
899                 {
900                     continue;
901                 }
902                 Role pRole = profile.getRole();
903                 if (null != pRole && pRole.getName().equals(role.getName()))
904                 {
905                     documents.remove(profile.getDocument().getName());
906                 }
907             }
908         }
909     }
910 
911     /*** Removes all documents for a given group.
912      *
913      * @param group The group object.
914      */
915     public void removeGroupDocuments( Group group )
916     {
917         ProfileLocator locator = Profiler.createLocator();
918         locator.setGroup(group);
919         StringBuffer buffer = new StringBuffer();
920         buffer.append(PATH_GROUP);
921         String name = group.getName();
922         if (null != name && name.length() > 0)
923         {
924             buffer.append(File.separator)
925                 .append(name);
926         }
927         else
928             return; // don't delete the entire group directories
929 
930         String path = buffer.toString();
931         File base = this.rootDir;
932         File file = new File(base, path);
933 
934         try
935         {
936             name = file.getCanonicalPath();
937         }
938         catch (IOException e)
939         {
940             logger.error("PSMLManager: unable to resolve file path for "+ file);
941         }
942 
943 
944         synchronized (documents)
945         {
946             DirectoryUtils.rmdir(name);
947             Iterator it = documents.getIterator();
948             while (it.hasNext())
949             {
950                 FileCacheEntry entry = (FileCacheEntry)it.next();
951                 if (null == entry)
952                 {
953                     continue;
954                 }
955                 Profile profile = (Profile)entry.getDocument();
956                 if (null == profile)
957                 {
958                     continue;
959                 }
960                 Group pGroup = profile.getGroup();
961                 if (null != pGroup && pGroup.getName().equals(group.getName()))
962                 {
963                     documents.remove(profile.getDocument().getName());
964                 }
965             }
966         }
967 
968     }
969 
970 
971     /***
972      * Maps a ProfileLocator to a file.
973      *
974      * @param locator The profile locator describing the PSML resource to be found.
975      * @return the String path of the file.
976      */
977     protected String mapLocatorToFile(ProfileLocator locator)
978     {
979         StringBuffer path = new StringBuffer();
980 
981         // move the base dir is either user or role is specified
982         Role role = locator.getRole();
983         Group group = locator.getGroup();
984         JetspeedUser user = locator.getUser();
985 
986         if (user != null)
987         {
988             path.append(PATH_USER);
989             String name = user.getUserName();
990             if (null != name && name.length() > 0)
991             {
992                 path.append(File.separator)
993                     .append(name);
994             }
995         }
996         else if (group != null)
997         {
998             path.append(PATH_GROUP);
999             String name = group.getName();
1000             if (null != name && name.length() > 0)
1001             {
1002                 path.append(File.separator)
1003                     .append(name);
1004             }
1005         }
1006         else if (null != role)
1007         {
1008             path.append(PATH_ROLE);
1009             String name = role.getName();
1010             if (null != name && name.length() > 0)
1011             {
1012                 path.append(File.separator)
1013                     .append(name);
1014             }
1015         }
1016 
1017         // Media
1018         if (null != locator.getMediaType())
1019         {
1020             path.append(File.separator)
1021                 .append(locator.getMediaType());
1022         }
1023         // Language
1024         if (null != locator.getLanguage() && (! locator.getLanguage().equals("-1")))
1025         {
1026             path.append(File.separator)
1027                 .append(locator.getLanguage());
1028         }
1029         // Country
1030         if (null != locator.getCountry() && (! locator.getCountry().equals("-1")))
1031         {
1032             path.append(File.separator)
1033                 .append(locator.getCountry());
1034         }
1035         // Resource Name
1036         if (null != locator.getName())
1037         {
1038             if (!(locator.getName().endsWith(CastorPsmlManagerService.DEFAULT_EXT)))
1039             {
1040                 path.append(File.separator)
1041                     .append(locator.getName()).append(CastorPsmlManagerService.DEFAULT_EXT);
1042             }
1043             else
1044             {
1045                 path.append(File.separator)
1046                     .append(locator.getName());
1047             }
1048         }
1049         else
1050         {
1051             path.append(File.separator)
1052                 .append(DEFAULT_RESOURCE);
1053         }
1054 
1055         return  path.toString();
1056     }
1057 
1058     protected static int STATE_INIT = 0;
1059     protected static int STATE_BASE = 1;
1060     protected static int STATE_NAME = 2;
1061     protected static int STATE_MEDIA = 3;
1062     protected static int STATE_LANGUAGE = 4;
1063     protected static int STATE_COUNTRY = 5;
1064 
1065     /*** Query for a collection of profiles given a profile locator criteria.
1066      *
1067      * @param locator The profile locator criteria.
1068      */
1069     public Iterator query( QueryLocator locator )
1070     {
1071         List list = new LinkedList();
1072 
1073         Role role = locator.getRole();
1074         Group group = locator.getGroup();
1075         JetspeedUser user = locator.getUser();
1076 
1077         // search thru anonymous directories?
1078         int qm = locator.getQueryMode();
1079         if ((qm & QueryLocator.QUERY_USER) == QueryLocator.QUERY_USER)
1080         {
1081             Profile profile = createProfile();
1082             StringBuffer path = new StringBuffer();
1083             path.append(PATH_USER);
1084             String name = null;
1085             int state = STATE_INIT;
1086             if (null != user)
1087             {
1088                 name = user.getUserName();
1089                 profile.setUser( user );
1090                 if (null != name)
1091                 {
1092                     path.append(File.separator).append(name);
1093                     state = STATE_BASE;
1094                 }
1095             }
1096             File base = this.rootDir;
1097             File file = new File(base, path.toString());
1098             String absPath = file.getAbsolutePath();
1099             QueryState qs = new QueryState( QUERY_BY_USER,
1100                                              profile,
1101                                              locator,
1102                                              list,
1103                                              name,
1104                                              state);
1105             subQuery(qs, absPath);
1106         }
1107         if ((qm & QueryLocator.QUERY_ROLE) == QueryLocator.QUERY_ROLE)
1108         {
1109             Profile profile = createProfile();
1110             StringBuffer path = new StringBuffer();
1111             path.append(PATH_ROLE);
1112             String name = null;
1113             int state = STATE_INIT;
1114             if (null != role)
1115             {
1116                 name = role.getName();
1117                 profile.setRole( role );
1118                 if (null != name)
1119                 {
1120                     path.append(File.separator).append(name);
1121                     state = STATE_BASE;
1122                 }
1123             }
1124             File base = this.rootDir;
1125             File file = new File(base, path.toString());
1126             String absPath = null;
1127 
1128             try
1129             {
1130                 absPath = file.getCanonicalPath();
1131             }
1132             catch (IOException e)
1133             {
1134                 logger.error("PSMLManager: unable to resolve file path for "+ file);
1135             }
1136 
1137             QueryState qs = new QueryState( QUERY_BY_ROLE,
1138                                              profile,
1139                                              locator,
1140                                              list,
1141                                              name,
1142                                              state);
1143             subQuery(qs, absPath);
1144         }
1145         if ((qm & QueryLocator.QUERY_GROUP) == QueryLocator.QUERY_GROUP)
1146         {
1147             Profile profile = createProfile();
1148             StringBuffer path = new StringBuffer();
1149             path.append(PATH_GROUP);
1150             String name = null;
1151             int state = STATE_INIT;
1152             if (null != group)
1153             {
1154                 name = group.getName();
1155                 profile.setGroup( group );
1156                 if (null != name)
1157                 {
1158                     path.append(File.separator).append(name);
1159                     state = STATE_BASE;
1160                 }
1161             }
1162             File base = this.rootDir;
1163             File file = new File(base, path.toString());
1164             String absPath = null;
1165 
1166             try
1167             {
1168                 absPath = file.getCanonicalPath();
1169             }
1170             catch (IOException e)
1171             {
1172                 logger.error("PSMLManager: unable to resolve file path for "+ file);
1173             }
1174 
1175             QueryState qs = new QueryState( QUERY_BY_GROUP,
1176                                              profile,
1177                                              locator,
1178                                              list,
1179                                              name,
1180                                              state);
1181             subQuery(qs, absPath);
1182         }
1183 
1184         return list.iterator();
1185     }
1186 
1187     /*** Create a profile based on import flag.
1188      *
1189      */
1190     protected Profile createProfile()
1191     {
1192         if (importFlag)
1193             return new ImportProfile(this, this.consumer);
1194         else
1195             return Profiler.createProfile();
1196     }
1197 
1198     protected Profile createProfile(ProfileLocator locator)
1199     {
1200         if (importFlag)
1201             return new ImportProfile(this, this.consumer, locator);
1202         else
1203             return Profiler.createProfile(locator);
1204     }
1205 
1206     /*** Query for a collection of profiles given a profile locator criteria.
1207      *  This method should be used when importing or exporting profiles between services.
1208      *
1209      * @param locator The profile locator criteria.
1210      * @return The count of profiles exported.
1211      */
1212     public int export(PsmlManagerService consumer, QueryLocator locator)
1213     {
1214         importFlag = true;
1215         Iterator profiles = null;
1216         int count = 0;
1217         try
1218         {
1219             this.consumer = consumer;
1220             profiles = query(locator);
1221 
1222             while (profiles.hasNext() )
1223             {
1224                 Profile profile = (Profile)profiles.next();
1225                 //dumpProfile(profile);
1226                 try
1227                 {
1228                     consumer.createDocument(profile);
1229                     count++;
1230                 }
1231                 catch (Exception ex)
1232                 {
1233                     try
1234                     {
1235                         consumer.store(profile);
1236                         count++;
1237                     }
1238                     catch (Exception e)
1239                     {
1240                         logger.error("PSMLManager: Failed to export profiles to DB: " + profile, ex );
1241                     }
1242                 }
1243             }
1244         }
1245         catch(Exception e)
1246         {
1247             e.printStackTrace();
1248             logger.error("PSMLManager: Failed to export profiles to DB: " , e );
1249 
1250         }
1251         finally
1252         {
1253             importFlag = false;
1254         }
1255         return count;
1256     }
1257 
1258 
1259     /*** Query for a collection of profiles given a profile locator criteria.
1260      *  To specify 'all' - use '*' in the criteria
1261      *
1262      * @param locator The profile locator criteria.
1263      */
1264     protected void subQuery(QueryState qs, String path)
1265     {
1266         File file = new File(path);
1267         if (file.isFile())
1268         {
1269             try
1270             {
1271                 String filename = file.getName();
1272                 if (!filename.endsWith(this.ext))
1273                     return;
1274 
1275                 Profile clone = (Profile)qs.profile.clone();
1276                 clone.setName(filename);
1277                 qs.list.add( clone );
1278             }
1279             catch (Exception e)
1280             {
1281                 logger.error("PSMLManager: Failed to clone profile: " + path + " : " + e, e);
1282             }
1283         }
1284         else if (file.isDirectory())
1285         {
1286             String dirName = file.getName();
1287             qs.state++;
1288 
1289             // filter out based on name, mediatype, language, country
1290             if (qs.state == STATE_NAME)
1291             {
1292                 if (null != qs.name)
1293                 {
1294                     if (!dirName.equals(qs.name))
1295                         return;
1296                 }
1297                 try
1298                 {
1299                     if (QUERY_BY_USER == qs.queryBy)
1300                     {
1301                         JetspeedUser user = (JetspeedUser)qs.profile.getUser();
1302                         if (null == user)
1303                         {
1304                             user = JetspeedUserFactory.getInstance();
1305                             user.setUserName(file.getName());
1306                             qs.profile.setUser(user);
1307                             qs.clearName = true;
1308                         }
1309                     }
1310                     else if (QUERY_BY_ROLE == qs.queryBy)
1311                     {
1312                         Role role = qs.profile.getRole();
1313                         if (null == role)
1314                         {
1315                             role = JetspeedRoleFactory.getInstance();
1316                             role.setName(file.getName());
1317                             qs.profile.setRole(role);
1318                             qs.clearName = true;
1319                         }
1320                     }
1321                     else if (QUERY_BY_GROUP == qs.queryBy)
1322                     {
1323                         Group group = qs.profile.getGroup();
1324                         if (null == group)
1325                         {
1326                             group = JetspeedGroupFactory.getInstance();
1327                             group.setName(file.getName());
1328                             qs.profile.setGroup(group);
1329                             qs.clearName = true;
1330                         }
1331                     }
1332                 }
1333                 catch (Exception e)
1334                 {}
1335 
1336 
1337             }
1338             else if (qs.state == STATE_MEDIA)
1339             {
1340                 String media = qs.locator.getMediaType();
1341                 if (null != media)
1342                 {
1343                     if (!dirName.equals(media))
1344                         return;
1345                 }
1346                 else
1347                 {
1348                     qs.profile.setMediaType(dirName);
1349                     qs.clearMedia = true;
1350                 }
1351             }
1352             else if (qs.state == STATE_LANGUAGE)
1353             {
1354                 String language = qs.locator.getLanguage();
1355                 if (null != language)
1356                 {
1357                     if (!dirName.equals(language))
1358                         return;
1359                 }
1360                 else
1361                 {
1362                     qs.profile.setLanguage(dirName);
1363                     qs.clearLanguage = true;
1364                 }
1365             }
1366             else if (qs.state == STATE_COUNTRY)
1367             {
1368                 String country = qs.locator.getCountry();
1369                 if (null != country)
1370                 {
1371                     if (!dirName.equals(country))
1372                         return;
1373                 }
1374                 else
1375                 {
1376                     qs.profile.setCountry(dirName);
1377                     qs.clearCountry = true;
1378                 }
1379             }
1380 
1381             if (!path.endsWith(File.separator))
1382                 path += File.separator;
1383 
1384             String files[] = file.list();
1385 
1386 
1387             // Process all files recursivly
1388             for(int ix = 0; files != null && ix < files.length; ix++)
1389             {
1390                 subQuery(qs, path + files[ix]);
1391             }
1392 
1393             // clear state
1394             if (qs.state == STATE_NAME && true == qs.clearName)
1395             {
1396                 if (QUERY_BY_USER == qs.queryBy)
1397                     qs.profile.setUser(null);
1398                 else if (QUERY_BY_ROLE == qs.queryBy)
1399                     qs.profile.setRole(null);
1400                 else if (QUERY_BY_GROUP == qs.queryBy)
1401                     qs.profile.setGroup(null);
1402                 qs.clearName = false;
1403             }
1404             else if (qs.state == STATE_MEDIA && true == qs.clearMedia)
1405             {
1406                 qs.profile.setMediaType(null);
1407                 qs.clearMedia = false;
1408             }
1409             else if (qs.state == STATE_LANGUAGE && true == qs.clearLanguage)
1410             {
1411                 qs.profile.setLanguage(null);
1412                 qs.clearLanguage = false;
1413             }
1414             else if (qs.state == STATE_COUNTRY && true == qs.clearCountry)
1415             {
1416                 qs.profile.setCountry(null);
1417                 qs.clearCountry = false;
1418             }
1419 
1420             qs.state--;
1421 
1422         }
1423 
1424     }
1425 
1426      static int QUERY_BY_USER = 0;
1427      static int QUERY_BY_ROLE = 1;
1428      static int QUERY_BY_GROUP = 2;
1429 
1430     protected class QueryState
1431     {
1432 
1433         QueryState( int queryBy,
1434                     Profile profile,
1435                     ProfileLocator locator,
1436                     List list,
1437                     String name,
1438                     int state)
1439         {
1440             this.queryBy = queryBy;
1441             this.profile = profile;
1442             this.locator = locator;
1443             this.list = list;
1444             this.name = name;
1445             this.state = state;
1446         }
1447 
1448         protected int queryBy;
1449         protected Profile profile;
1450         protected ProfileLocator locator;
1451         protected List list;
1452         protected String name;
1453         protected int state;
1454 
1455         protected boolean clearName = false;
1456         protected boolean clearMedia = false;
1457         protected boolean clearLanguage = false;
1458         protected boolean clearCountry = false;
1459 
1460     }
1461 
1462     protected void testCases()
1463     {
1464         try
1465         {
1466             QueryLocator locator = new QueryLocator( QueryLocator.QUERY_USER );
1467             Iterator x1 = query( locator );
1468             dump( x1 );
1469 
1470             QueryLocator locator2 = new QueryLocator( QueryLocator.QUERY_USER );
1471             locator2.setUser( JetspeedSecurity.getUser("turbine") );
1472             Iterator x2 = query( locator2 );
1473             dump( x2 );
1474 
1475 
1476             QueryLocator locator4 = new QueryLocator( QueryLocator.QUERY_GROUP );
1477 //            locator4.setGroup( JetspeedSecurity.getGroup("apache") );
1478             Iterator x4 = query( locator4 );
1479             dump( x4 );
1480           }
1481         catch (Exception e)
1482         {
1483             System.out.println( "Exception in Debug:" + e);
1484         }
1485     }
1486 
1487     protected void dump( Iterator it )
1488     {
1489         System.out.println("===============================================");
1490         while (it.hasNext() )
1491         {
1492             Profile profile = (Profile)it.next();
1493             dumpProfile(profile);
1494         }
1495         System.out.println("===============================================");
1496     }
1497 
1498     protected void dumpProfile(Profile profile)
1499     {
1500         JetspeedUser user = profile.getUser();
1501         Group group = profile.getGroup();
1502         Role role = profile.getRole();
1503         if (profile.getAnonymous() == true)
1504             System.out.println("ANON USER");
1505         System.out.println("RESOURCE = " + profile.getName());
1506         if (null != user)
1507             System.out.println("USER = " + user.getUserName() );
1508         if (null != group)
1509             System.out.println("GROUP = " + group.getName() );
1510         if (null != role)
1511             System.out.println("ROLE = " + role.getName() );
1512         System.out.println("MEDIA TYPE = " + profile.getMediaType());
1513         System.out.println("LANGUAGE = " + profile.getLanguage());
1514         System.out.println("COUNTRY = " + profile.getCountry());
1515         PSMLDocument doc = profile.getDocument();
1516         if (null == doc)
1517             System.out.println("Document is null");
1518         else
1519         {
1520             if (null == profile.getName())
1521                 System.out.println("profile name is null");
1522             else
1523                 System.out.println("Doc.name=" + profile.getName());
1524         }
1525 
1526         System.out.println("----------------------");
1527     }
1528 
1529     /***
1530      * Refresh event, called when the entry is being refreshed from file system.
1531      *
1532      * @param entry the entry being refreshed.
1533      */
1534     public void refresh(FileCacheEntry entry)
1535     {
1536         if (logger.isInfoEnabled())
1537         {
1538             logger.info("CastorPsmlManager: Entry is refreshing: " + entry.getFile().getPath());
1539         }
1540 
1541         Profile profile = (Profile) entry.getDocument();
1542         String path = null;
1543 
1544         if (profile != null)
1545         {
1546             try
1547             {
1548                 path = entry.getFile().getCanonicalPath();
1549                 profile.setDocument(loadDocument(path));
1550             }
1551             catch(java.io.IOException e)
1552             {
1553                 logger.error("CastorPsmlManager: Failed to refresh document "+path);
1554             }
1555         }
1556     }
1557 
1558     /***
1559      * Evict event, called when the entry is being evicted out of the cache
1560      *
1561      * @param entry the entry being refreshed.
1562      */
1563     public void evict(FileCacheEntry entry)
1564     {
1565         System.out.println("entry is evicting: " + entry.getFile().getName());
1566     }
1567 
1568 }
1569