KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > turbine > services > intake > TurbineIntakeService


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

18
19 import java.beans.IntrospectionException JavaDoc;
20 import java.beans.PropertyDescriptor JavaDoc;
21
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.ObjectInputStream JavaDoc;
27 import java.io.ObjectOutputStream JavaDoc;
28 import java.io.OutputStream JavaDoc;
29
30 import java.lang.reflect.Method JavaDoc;
31
32 import java.util.HashMap JavaDoc;
33 import java.util.HashSet JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.Set JavaDoc;
38 import java.util.Vector JavaDoc;
39
40 import javax.servlet.ServletConfig JavaDoc;
41
42 import org.apache.commons.lang.StringUtils;
43
44 import org.apache.commons.logging.Log;
45 import org.apache.commons.logging.LogFactory;
46
47 import org.apache.commons.pool.KeyedObjectPool;
48 import org.apache.commons.pool.KeyedPoolableObjectFactory;
49 import org.apache.commons.pool.impl.StackKeyedObjectPool;
50
51 import org.apache.turbine.Turbine;
52 import org.apache.turbine.services.InitializationException;
53 import org.apache.turbine.services.TurbineBaseService;
54 import org.apache.turbine.services.intake.model.Group;
55 import org.apache.turbine.services.intake.transform.XmlToAppData;
56 import org.apache.turbine.services.intake.xmlmodel.AppData;
57 import org.apache.turbine.services.intake.xmlmodel.XmlGroup;
58
59 /**
60  * This service provides access to input processing objects based
61  * on an XML specification.
62  *
63  * @author <a HREF="mailto:jmcnally@collab.net">John McNally</a>
64  * @author <a HREF="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
65  * @author <a HREF="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
66  * @version $Id: TurbineIntakeService.java,v 1.14.2.4 2004/09/20 17:00:03 henning Exp $
67  */

68 public class TurbineIntakeService
69         extends TurbineBaseService
70         implements IntakeService
71 {
72     /** Map of groupNames -> appData elements */
73     private Map JavaDoc groupNames;
74
75     /** The cache of group names. */
76     private Map JavaDoc groupNameMap;
77
78     /** The cache of group keys. */
79     private Map JavaDoc groupKeyMap;
80
81     /** The cache of property getters. */
82     private Map JavaDoc getterMap;
83
84     /** The cache of property setters. */
85     private Map JavaDoc setterMap;
86
87     /** AppData -> keyed Pools Map */
88     private Map JavaDoc keyedPools;
89
90     /** Used for logging */
91     private static Log log = LogFactory.getLog(TurbineIntakeService.class);
92
93     /**
94      * Constructor. All Components need a public no argument constructor
95      * to be a legal Component.
96      */

97     public TurbineIntakeService()
98     {
99     }
100
101     /**
102      * Called the first time the Service is used.
103      *
104      * @throws InitializationException Something went wrong in the init
105      * stage
106      */

107     public void init()
108             throws InitializationException
109     {
110         Vector JavaDoc defaultXmlPathes = new Vector JavaDoc();
111         defaultXmlPathes.add(XML_PATH_DEFAULT);
112
113         List JavaDoc xmlPathes = getConfiguration()
114                 .getList(XML_PATH, defaultXmlPathes);
115
116         Map JavaDoc appDataElements = null;
117
118         String JavaDoc serialDataPath = getConfiguration()
119                 .getString(SERIAL_XML, SERIAL_XML_DEFAULT);
120
121         if (!serialDataPath.equalsIgnoreCase("none"))
122         {
123             serialDataPath = Turbine.getRealPath(serialDataPath);
124         }
125         else
126         {
127             serialDataPath = null;
128         }
129
130         log.debug("Path for serializing: " + serialDataPath);
131
132         groupNames = new HashMap JavaDoc();
133         groupKeyMap = new HashMap JavaDoc();
134         groupNameMap = new HashMap JavaDoc();
135         getterMap = new HashMap JavaDoc();
136         setterMap = new HashMap JavaDoc();
137         keyedPools = new HashMap JavaDoc();
138
139         if (xmlPathes == null)
140         {
141             String JavaDoc LOAD_ERROR = "No pathes for XML files were specified. " +
142                     "Check that the property exists in " +
143                     "TurbineResources.props and were loaded.";
144
145             log.error(LOAD_ERROR);
146             throw new InitializationException(LOAD_ERROR);
147         }
148
149         Set JavaDoc xmlFiles = new HashSet JavaDoc();
150
151         long timeStamp = 0;
152
153         for (Iterator JavaDoc it = xmlPathes.iterator(); it.hasNext();)
154         {
155             // Files are webapp.root relative
156
String JavaDoc xmlPath = Turbine.getRealPath((String JavaDoc) it.next());
157             File JavaDoc xmlFile = new File JavaDoc(xmlPath);
158
159             log.debug("Path for XML File: " + xmlFile);
160
161             if (!xmlFile.canRead())
162             {
163                 String JavaDoc READ_ERR = "Could not read input file " + xmlPath;
164
165                 log.error(READ_ERR);
166                 throw new InitializationException(READ_ERR);
167             }
168
169             xmlFiles.add(xmlPath);
170
171             log.debug("Added " + xmlPath + " as File to parse");
172
173             // Get the timestamp of the youngest file to be compared with
174
// a serialized file. If it is younger than the serialized file,
175
// then we have to parse the XML anyway.
176
timeStamp =
177                     (xmlFile.lastModified() > timeStamp) ? xmlFile.lastModified() : timeStamp;
178         }
179
180         Map JavaDoc serializedMap = loadSerialized(serialDataPath, timeStamp);
181
182         if (serializedMap != null)
183         {
184             // Use the serialized data as XML groups. Don't parse.
185
appDataElements = serializedMap;
186             log.debug("Using the serialized map");
187         }
188         else
189         {
190             // Parse all the given XML files
191
appDataElements = new HashMap JavaDoc();
192
193             for (Iterator JavaDoc it = xmlFiles.iterator(); it.hasNext();)
194             {
195                 String JavaDoc xmlPath = (String JavaDoc) it.next();
196                 AppData appData = null;
197
198                 log.debug("Now parsing: " + xmlPath);
199                 try
200                 {
201                     XmlToAppData xmlApp = new XmlToAppData();
202                     appData = xmlApp.parseFile(xmlPath);
203                 }
204                 catch (Exception JavaDoc e)
205                 {
206                     log.error("Could not parse XML file " + xmlPath, e);
207
208                     throw new InitializationException("Could not parse XML file " +
209                             xmlPath, e);
210                 }
211
212                 appDataElements.put(appData, xmlPath);
213                 log.debug("Saving appData for " + xmlPath);
214             }
215
216             saveSerialized(serialDataPath, appDataElements);
217         }
218
219         try
220         {
221             for (Iterator JavaDoc it = appDataElements.keySet().iterator(); it.hasNext();)
222             {
223                 AppData appData = (AppData) it.next();
224
225                 int maxPooledGroups = 0;
226                 List JavaDoc glist = appData.getGroups();
227
228                 String JavaDoc groupPrefix = appData.getGroupPrefix();
229
230                 for (int i = glist.size() - 1; i >= 0; i--)
231                 {
232                     XmlGroup g = (XmlGroup) glist.get(i);
233                     String JavaDoc groupName = g.getName();
234
235                     boolean registerUnqualified = registerGroup(groupName, g, appData, true);
236
237                     if (!registerUnqualified)
238                     {
239                         log.info("Ignored redefinition of Group " + groupName
240                                 + " or Key " + g.getKey()
241                                 + " from " + appDataElements.get(appData));
242                     }
243
244                     if (groupPrefix != null)
245                     {
246                         StringBuffer JavaDoc qualifiedName = new StringBuffer JavaDoc();
247                         qualifiedName.append(groupPrefix)
248                                 .append(':')
249                                 .append(groupName);
250
251                         // Add the fully qualified group name. Do _not_ check for
252
// the existence of the key if the unqualified registration succeeded
253
// (because then it was added by the registerGroup above).
254
if (!registerGroup(qualifiedName.toString(), g, appData, !registerUnqualified))
255                         {
256                             log.error("Could not register fully qualified name " + qualifiedName
257                                     + ", maybe two XML files have the same prefix. Ignoring it.");
258                         }
259                     }
260
261                     maxPooledGroups =
262                             Math.max(maxPooledGroups,
263                                     Integer.parseInt(g.getPoolCapacity()));
264
265                 }
266
267                 KeyedPoolableObjectFactory factory =
268                         new Group.GroupFactory(appData);
269                 keyedPools.put(appData, new StackKeyedObjectPool(factory, maxPooledGroups));
270             }
271
272             setInit(true);
273         }
274         catch (Exception JavaDoc e)
275         {
276             throw new InitializationException(
277                     "TurbineIntakeService failed to initialize", e);
278         }
279     }
280
281     /**
282      * Called the first time the Service is used.
283      *
284      * @param config A ServletConfig.
285      * @deprecated use init() instead.
286      */

287     public void init(ServletConfig JavaDoc config)
288             throws InitializationException
289     {
290         init();
291     }
292
293     /**
294      * Registers a given group name in the system
295      *
296      * @param groupName The name to register the group under
297      * @param group The XML Group to register in
298      * @param appData The app Data object where the group can be found
299      * @param checkKey Whether to check if the key also exists.
300      *
301      * @return true if successful, false if not
302      */

303     private boolean registerGroup(String JavaDoc groupName, XmlGroup group, AppData appData, boolean checkKey)
304     {
305         if (groupNames.keySet().contains(groupName))
306         {
307             // This name already exists.
308
return false;
309         }
310
311         boolean keyExists = groupNameMap.keySet().contains(group.getKey());
312
313         if (checkKey && keyExists)
314         {
315             // The key for this package is already registered for another group
316
return false;
317         }
318
319         groupNames.put(groupName, appData);
320
321         groupKeyMap.put(groupName, group.getKey());
322
323         if (!keyExists)
324         {
325             // This key does not exist. Add it to the hash.
326
groupNameMap.put(group.getKey(), groupName);
327         }
328
329         List JavaDoc classNames = group.getMapToObjects();
330         for (Iterator JavaDoc iter2 = classNames.iterator(); iter2.hasNext();)
331         {
332             String JavaDoc className = (String JavaDoc) iter2.next();
333             if (!getterMap.containsKey(className))
334             {
335                 getterMap.put(className, new HashMap JavaDoc());
336                 setterMap.put(className, new HashMap JavaDoc());
337             }
338         }
339         return true;
340     }
341
342     /**
343      * Tries to load a serialized Intake Group file. This
344      * can reduce the startup time of Turbine.
345      *
346      * @param serialDataPath The path of the File to load.
347      *
348      * @return A map with appData objects loaded from the file
349      * or null if the map could not be loaded.
350      */

351     private Map JavaDoc loadSerialized(String JavaDoc serialDataPath, long timeStamp)
352     {
353         log.debug("Entered loadSerialized("
354                 + serialDataPath + ", "
355                 + timeStamp + ")");
356
357         if (serialDataPath == null)
358         {
359             return null;
360         }
361
362         File JavaDoc serialDataFile = new File JavaDoc(serialDataPath);
363
364         if (!serialDataFile.exists())
365         {
366             log.info("No serialized file found, parsing XML");
367             return null;
368         }
369
370         if (serialDataFile.lastModified() <= timeStamp)
371         {
372             log.info("serialized file too old, parsing XML");
373             return null;
374         }
375
376         InputStream JavaDoc in = null;
377         Map JavaDoc serialData = null;
378
379         try
380         {
381             in = new FileInputStream JavaDoc(serialDataFile);
382             ObjectInputStream JavaDoc p = new ObjectInputStream JavaDoc(in);
383             Object JavaDoc o = p.readObject();
384
385             if (o instanceof Map JavaDoc)
386             {
387                 serialData = (Map JavaDoc) o;
388             }
389             else
390             {
391                 // Maybe an old file from intake. Ignore it and try to delete
392
log.info("serialized object is not an intake map, ignoring");
393                 in.close();
394                 in = null;
395                 serialDataFile.delete(); // Try to delete the file lying around
396
}
397         }
398         catch (Exception JavaDoc e)
399         {
400             log.error("Serialized File could not be read.", e);
401
402             // We got a corrupt file for some reason.
403
// Null out serialData to be sure
404
serialData = null;
405         }
406         finally
407         {
408             // Could be null if we opened a file, didn't find it to be a
409
// Map object and then nuked it away.
410
try
411             {
412                 if (in != null)
413                 {
414                     in.close();
415                 }
416             }
417             catch (Exception JavaDoc e)
418             {
419                 log.error("Exception while closing file", e);
420             }
421         }
422
423         log.info("Loaded serialized map object, ignoring XML");
424         return serialData;
425     }
426
427     /**
428      * Writes a parsed XML map with all the appData groups into a
429      * file. This will speed up loading time when you restart the
430      * Intake Service because it will only unserialize this file instead
431      * of reloading all of the XML files
432      *
433      * @param serialDataPath The path of the file to write to
434      * @param appDataElements A Map containing all of the XML parsed appdata elements
435      */

436     private void saveSerialized(String JavaDoc serialDataPath, Map JavaDoc appDataElements)
437     {
438
439         log.debug("Entered saveSerialized("
440                 + serialDataPath + ", appDataElements)");
441
442         if (serialDataPath == null)
443         {
444             return;
445         }
446
447         File JavaDoc serialData = new File JavaDoc(serialDataPath);
448
449         try
450         {
451             serialData.createNewFile();
452             serialData.delete();
453         }
454         catch (Exception JavaDoc e)
455         {
456             log.info("Could not create serialized file " + serialDataPath
457                     + ", not serializing the XML data");
458             return;
459         }
460
461         OutputStream JavaDoc out = null;
462         InputStream JavaDoc in = null;
463
464         try
465         {
466             // write the appData file out
467
out = new FileOutputStream JavaDoc(serialDataPath);
468             ObjectOutputStream JavaDoc pout = new ObjectOutputStream JavaDoc(out);
469             pout.writeObject(appDataElements);
470             pout.flush();
471
472             // read the file back in. for some reason on OSX 10.1
473
// this is necessary.
474
in = new FileInputStream JavaDoc(serialDataPath);
475             ObjectInputStream JavaDoc pin = new ObjectInputStream JavaDoc(in);
476             Map JavaDoc dummy = (Map JavaDoc) pin.readObject();
477
478             log.debug("Serializing successful");
479         }
480         catch (Exception JavaDoc e)
481         {
482             log.info("Could not write serialized file to " + serialDataPath
483                     + ", not serializing the XML data");
484         }
485         finally
486         {
487             try
488             {
489                 if (out != null)
490                 {
491                     out.close();
492                 }
493                 if (in != null)
494                 {
495                     in.close();
496                 }
497             }
498             catch (Exception JavaDoc e)
499             {
500                 log.error("Exception while closing file", e);
501             }
502         }
503     }
504
505     /**
506      * Gets an instance of a named group either from the pool
507      * or by calling the Factory Service if the pool is empty.
508      *
509      * @param groupName the name of the group.
510      * @return a Group instance.
511      * @throws IntakeException if recycling fails.
512      */

513     public Group getGroup(String JavaDoc groupName)
514             throws IntakeException
515     {
516         Group group = null;
517
518         AppData appData = (AppData) groupNames.get(groupName);
519
520         if (groupName == null)
521         {
522             throw new IntakeException(
523                     "Intake TurbineIntakeService.getGroup(groupName) is null");
524         }
525         if (appData == null)
526         {
527             throw new IntakeException(
528                     "Intake TurbineIntakeService.getGroup(groupName): No XML definition for Group "
529                     + groupName + " found");
530         }
531         try
532         {
533             group = (Group) ((KeyedObjectPool) keyedPools.get(appData)).borrowObject(groupName);
534         }
535         catch (Exception JavaDoc e)
536         {
537             throw new IntakeException("Could not get group " + groupName, e);
538         }
539         return group;
540     }
541
542     /**
543      * Puts a Group back to the pool.
544      *
545      * @param instance the object instance to recycle.
546      *
547      * @throws IntakeException The passed group name does not exist.
548      */

549     public void releaseGroup(Group instance)
550             throws IntakeException
551     {
552         if (instance != null)
553         {
554             String JavaDoc groupName = instance.getIntakeGroupName();
555             AppData appData = (AppData) groupNames.get(groupName);
556
557             if (appData == null)
558             {
559                 throw new IntakeException(
560                         "Intake TurbineIntakeService.releaseGroup(groupName): "
561                         + "No XML definition for Group " + groupName + " found");
562             }
563
564             try
565             {
566                 ((KeyedObjectPool) keyedPools.get(appData)).returnObject(groupName, instance);
567             }
568             catch (Exception JavaDoc e)
569             {
570                 new IntakeException("Could not get group " + groupName, e);
571             }
572         }
573     }
574
575     /**
576      * Gets the current size of the pool for a group.
577      *
578      * @param groupName the name of the group.
579      *
580      * @throws IntakeException The passed group name does not exist.
581      */

582     public int getSize(String JavaDoc groupName)
583             throws IntakeException
584     {
585         AppData appData = (AppData) groupNames.get(groupName);
586         if (appData == null)
587         {
588             throw new IntakeException(
589                     "Intake TurbineIntakeService.Size(groupName): No XML definition for Group "
590                     + groupName + " found");
591         }
592
593         KeyedObjectPool kop = (KeyedObjectPool) keyedPools.get(groupName);
594
595         return kop.getNumActive(groupName)
596                 + kop.getNumIdle(groupName);
597     }
598
599     /**
600      * Names of all the defined groups.
601      *
602      * @return array of names.
603      */

604     public String JavaDoc[] getGroupNames()
605     {
606         return (String JavaDoc[]) groupNames.keySet().toArray(new String JavaDoc[0]);
607     }
608
609     /**
610      * Gets the key (usually a short identifier) for a group.
611      *
612      * @param groupName the name of the group.
613      * @return the the key.
614      */

615     public String JavaDoc getGroupKey(String JavaDoc groupName)
616     {
617         return (String JavaDoc) groupKeyMap.get(groupName);
618     }
619
620     /**
621      * Gets the group name given its key.
622      *
623      * @param groupKey the key.
624      * @return groupName the name of the group.
625      */

626     public String JavaDoc getGroupName(String JavaDoc groupKey)
627     {
628         return (String JavaDoc) groupNameMap.get(groupKey);
629     }
630
631     /**
632      * Gets the Method that can be used to set a property.
633      *
634      * @param className the name of the object.
635      * @param propName the name of the property.
636      * @return the setter.
637      * @throws ClassNotFoundException
638      * @throws IntrospectionException
639      */

640     public Method JavaDoc getFieldSetter(String JavaDoc className, String JavaDoc propName)
641             throws ClassNotFoundException JavaDoc, IntrospectionException JavaDoc
642     {
643         Map JavaDoc settersForClassName = (Map JavaDoc) setterMap.get(className);
644
645         if (settersForClassName == null)
646         {
647             throw new IntrospectionException JavaDoc("No setter Map for " + className + " available!");
648         }
649
650         Method JavaDoc setter = (Method JavaDoc) settersForClassName.get(propName);
651
652         if (setter == null)
653         {
654             PropertyDescriptor JavaDoc pd = null;
655
656             synchronized (setterMap)
657             {
658                 try
659                 {
660                     pd = new PropertyDescriptor JavaDoc(propName,
661                             Class.forName(className));
662                 }
663                 catch (IntrospectionException JavaDoc ie)
664                 {
665                     if (log.isWarnEnabled())
666                     {
667                         log.warn("Trying to find only a setter for " + propName);
668                     }
669                     
670                     pd = new PropertyDescriptor JavaDoc(propName,
671                             Class.forName(className),
672                             "set" + StringUtils.capitalise(propName),
673                             null); // Java sucks.
674
}
675                 
676                 setter = pd.getWriteMethod();
677                 settersForClassName.put(propName, setter);
678
679                 if (setter == null)
680                 {
681                     log.error("Intake: setter for '" + propName
682                             + "' in class '" + className
683                             + "' could not be found.");
684                 }
685             }
686
687             if (pd.getReadMethod() != null)
688             {
689                 // we have already completed the reflection on the getter, so
690
// save it so we do not have to repeat
691
synchronized (getterMap)
692                 {
693                     Map JavaDoc gettersForClassName = (Map JavaDoc) getterMap.get(className);
694                     
695                     if (gettersForClassName != null)
696                     {
697                         try
698                         {
699                             Method JavaDoc getter = pd.getReadMethod();
700                             if (getter != null)
701                             {
702                                 gettersForClassName.put(propName, getter);
703                             }
704                         }
705                         catch (Exception JavaDoc e)
706                         {
707                             // Do nothing
708
}
709                     }
710                 }
711             }
712         }
713         return setter;
714     }
715
716     /**
717      * Gets the Method that can be used to get a property value.
718      *
719      * @param className the name of the object.
720      * @param propName the name of the property.
721      * @return the getter.
722      * @throws ClassNotFoundException
723      * @throws IntrospectionException
724      */

725     public Method JavaDoc getFieldGetter(String JavaDoc className, String JavaDoc propName)
726             throws ClassNotFoundException JavaDoc, IntrospectionException JavaDoc
727     {
728         Map JavaDoc gettersForClassName = (Map JavaDoc) getterMap.get(className);
729
730         if (gettersForClassName == null)
731         {
732             throw new IntrospectionException JavaDoc("No getter Map for " + className + " available!");
733         }
734
735         Method JavaDoc getter = (Method JavaDoc) gettersForClassName.get(propName);
736
737         if (getter == null)
738         {
739             PropertyDescriptor JavaDoc pd = null;
740
741             synchronized (getterMap)
742             {
743                 try
744                 {
745                     pd = new PropertyDescriptor JavaDoc(propName,
746                             Class.forName(className));
747                 }
748                 catch (IntrospectionException JavaDoc ie)
749                 {
750                     if (log.isWarnEnabled())
751                     {
752                         log.warn("Trying to find only a getter for " + propName);
753                     }
754                     
755                     pd = new PropertyDescriptor JavaDoc(propName,
756                             Class.forName(className),
757                             "get" + StringUtils.capitalise(propName),
758                             null); // Java sucks some more.
759
}
760                 
761                 getter = pd.getReadMethod();
762                 gettersForClassName.put(propName, getter);
763
764                 if (getter == null)
765                 {
766                     log.error("Intake: getter for '" + propName
767                             + "' in class '" + className
768                             + "' could not be found.");
769                 }
770             }
771
772             if (pd.getWriteMethod() != null)
773             {
774                 // we have already completed the reflection on the setter, so
775
// save it so we do not have to repeat
776
synchronized (setterMap)
777                 {
778                     Map JavaDoc settersForClassName = (Map JavaDoc) getterMap.get(className);
779                     
780                     if (settersForClassName != null)
781                     {
782                         try
783                         {
784                             Method JavaDoc setter = pd.getWriteMethod();
785                             if (setter != null)
786                             {
787                                 settersForClassName.put(propName, setter);
788                             }
789                         }
790                         catch (Exception JavaDoc e)
791                         {
792                             // Do nothing
793
}
794                     }
795                 }
796             }
797         }
798         return getter;
799     }
800 }
801
Popular Tags