View Javadoc
1   package org.apache.fulcrum.intake;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.beans.IntrospectionException;
23  import java.beans.PropertyDescriptor;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileOutputStream;
27  import java.io.IOException;
28  import java.io.ObjectInputStream;
29  import java.io.ObjectOutputStream;
30  import java.lang.reflect.Method;
31  import java.net.URL;
32  import java.util.ArrayList;
33  import java.util.HashMap;
34  import java.util.HashSet;
35  import java.util.List;
36  import java.util.ListIterator;
37  import java.util.Map;
38  import java.util.Map.Entry;
39  import java.util.Set;
40  
41  import javax.xml.XMLConstants;
42  import javax.xml.bind.JAXBContext;
43  import javax.xml.bind.Unmarshaller;
44  import javax.xml.bind.Unmarshaller.Listener;
45  import javax.xml.bind.helpers.DefaultValidationEventHandler;
46  import javax.xml.validation.SchemaFactory;
47  
48  import org.apache.avalon.framework.activity.Initializable;
49  import org.apache.avalon.framework.configuration.Configurable;
50  import org.apache.avalon.framework.configuration.Configuration;
51  import org.apache.avalon.framework.configuration.ConfigurationException;
52  import org.apache.avalon.framework.context.Context;
53  import org.apache.avalon.framework.context.ContextException;
54  import org.apache.avalon.framework.context.Contextualizable;
55  import org.apache.avalon.framework.logger.AbstractLogEnabled;
56  import org.apache.avalon.framework.logger.LogEnabled;
57  import org.apache.avalon.framework.service.ServiceException;
58  import org.apache.avalon.framework.service.ServiceManager;
59  import org.apache.avalon.framework.service.Serviceable;
60  import org.apache.commons.pool2.KeyedObjectPool;
61  import org.apache.commons.pool2.KeyedPooledObjectFactory;
62  import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
63  import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
64  import org.apache.fulcrum.intake.model.AppData;
65  import org.apache.fulcrum.intake.model.Field;
66  import org.apache.fulcrum.intake.model.Group;
67  
68  /**
69   * This service provides access to input processing objects based on an XML
70   * specification.
71   *
72   * avalon.component name="intake"
73   * avalon.service type="org.apache.fulcrum.intake.IntakeService"
74   *
75   * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
76   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
77   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
78   * @version $Id$
79   */
80  public class IntakeServiceImpl extends AbstractLogEnabled implements
81          IntakeService, Configurable, Initializable, Contextualizable,
82          Serviceable
83  {
84      /** Map of groupNames -> appData elements */
85      private Map<String, AppData> groupNames;
86  
87      /** The cache of group names. */
88      private Map<String, String> groupNameMap;
89  
90      /** The cache of group keys. */
91      private Map<String, String> groupKeyMap;
92  
93      /** The cache of property getters. */
94      private Map<String, Map<String, Method>> getterMap;
95  
96      /** The cache of property setters. */
97      private Map<String, Map<String, Method>> setterMap;
98  
99      /** AppData -> keyed Pools Map */
100     private Map<AppData, KeyedObjectPool<String, Group>> keyedPools;
101 
102     /** The Avalon Container root directory */
103     private String applicationRoot;
104 
105     /** List of configured xml specification files */
106     private List<String> xmlPathes = null;
107 
108     /** Configured location of the serialization file */
109     private String serialDataPath = null;
110 
111     /**
112      * Local Class to enable Avalon logging on the model classes
113      *
114      */
115     private class AvalonLogEnabledListener extends Listener
116     {
117 		/**
118 		 * @see javax.xml.bind.Unmarshaller.Listener#beforeUnmarshal(java.lang.Object, java.lang.Object)
119 		 */
120 		@Override
121 		public void beforeUnmarshal(Object target, Object parent)
122 		{
123 			super.beforeUnmarshal(target, parent);
124 
125 			if (target instanceof LogEnabled)
126 			{
127 				((LogEnabled)target).enableLogging(getLogger());
128 			}
129 		}
130 
131     }
132 
133     /**
134      * Registers a given group name in the system
135      *
136      * @param groupName
137      *            The name to register the group under
138      * @param group
139      *            The Group to register in
140      * @param appData
141      *            The app Data object where the group can be found
142      * @param checkKey
143      *            Whether to check if the key also exists.
144      *
145      * @return true if successful, false if not
146      */
147     private boolean registerGroup(String groupName, Group group,
148             AppData appData, boolean checkKey)
149     {
150         if (groupNames.containsKey(groupName))
151         {
152             // This name already exists.
153             return false;
154         }
155 
156         boolean keyExists = groupNameMap.containsKey(group.getGID());
157 
158         if (checkKey && keyExists)
159         {
160             // The key for this package is already registered for another group
161             return false;
162         }
163 
164         groupNames.put(groupName, appData);
165         groupKeyMap.put(groupName, group.getGID());
166 
167         if (!keyExists)
168         {
169             // This key does not exist. Add it to the hash.
170             groupNameMap.put(group.getGID(), groupName);
171         }
172 
173         List<Field<?>> fields = group.getFields();
174         for (Field<?> field : fields)
175         {
176             String className = field.getMapToObject();
177             if (!getterMap.containsKey(className))
178             {
179                 getterMap.put(className, new HashMap<String, Method>());
180                 setterMap.put(className, new HashMap<String, Method>());
181             }
182         }
183         return true;
184     }
185 
186     /**
187      * Tries to load a serialized Intake Group file. This can reduce the startup
188      * time of Turbine.
189      *
190      * @param serialDataPath
191      *            The path of the File to load.
192      *
193      * @return A map with appData objects loaded from the file or null if the
194      *         map could not be loaded.
195      */
196     private Map<AppData, File> loadSerialized(String serialDataPath, long timeStamp)
197     {
198         getLogger().debug(
199                 "Entered loadSerialized(" + serialDataPath + ", " + timeStamp
200                         + ")");
201 
202         long timer = System.currentTimeMillis();
203 
204         if (serialDataPath == null)
205         {
206             return null;
207         }
208 
209         File serialDataFile = new File(serialDataPath);
210 
211         if (!serialDataFile.exists())
212         {
213             getLogger().info("No serialized file found, parsing XML");
214             return null;
215         }
216 
217         if (serialDataFile.lastModified() <= timeStamp)
218         {
219             getLogger().info("serialized file too old, parsing XML");
220             return null;
221         }
222 
223         ObjectInputStream in = null;
224         Map<AppData, File> serialData = null;
225 
226         try
227         {
228         	FileInputStream fin = new FileInputStream(serialDataFile);
229             in = new ObjectInputStream(fin);
230             Object o = in.readObject();
231 
232             if (o instanceof Map)
233             {
234                 @SuppressWarnings("unchecked") // checked with instanceof
235 				Map<AppData, File> map = (Map<AppData, File>) o;
236 				serialData = map;
237             }
238             else
239             {
240                 // This could be old file from intake. Try to delete it
241                 getLogger().info("serialized object is not an intake map, ignoring");
242                 in.close();
243                 in = null;
244                 
245                 // Try to delete the file
246                 boolean result = serialDataFile.delete();
247                 if ( result == false )
248                 {
249                 	getLogger().error("Unknown serialized file could not be removed");
250                 }
251             }
252         }
253         catch (IOException e)
254         {
255             getLogger().error("Serialized File could not be read.", e);
256 
257             // We got a corrupt file for some reason.
258             // Null out serialData to be sure
259             serialData = null;
260         }
261         catch (ClassNotFoundException e)
262         {
263             getLogger().error("Objects could not be read from serialized file.", e);
264 
265             // This should not happen
266             // Null out serialData to be sure
267             serialData = null;
268         }
269         finally
270         {
271             // Could be null if we opened a file, didn't find it to be a
272             // Map object and then nuked it away.
273             try
274             {
275                 if (in != null)
276                 {
277                     in.close();
278                 }
279             }
280             catch (IOException e)
281             {
282                 getLogger().error("Exception while closing file", e);
283             }
284         }
285 
286         // Recreate transient loggers
287         if (serialData != null)
288         {
289         	for (AppData appData : serialData.keySet())
290         	{
291         		for (Group group : appData.getGroups())
292         		{
293         			if (group instanceof LogEnabled)
294         			{
295         				((LogEnabled)group).enableLogging(getLogger());
296         			}
297 
298     				for (Field<?> field : group.getFields())
299     				{
300             			if (field instanceof LogEnabled)
301             			{
302             				((LogEnabled)field).enableLogging(getLogger());
303             			}
304     				}
305         		}
306         	}
307         }
308         getLogger().info("Loaded serialized map object, ignoring XML");
309         getLogger().debug("Loading took " + (System.currentTimeMillis() - timer));
310         return serialData;
311     }
312 
313     /**
314      * Writes a parsed XML map with all the appData groups into a file. This
315      * will speed up loading time when you restart the Intake Service because it
316      * will only unserialize this file instead of reloading all of the XML files
317      *
318      * @param serialDataPath
319      *            The path of the file to write to
320      * @param appDataElements
321      *            A Map containing all of the XML parsed appdata elements
322      */
323     private void saveSerialized(String serialDataPath, Map<AppData, File> appDataElements)
324     {
325 
326         getLogger().debug(
327                 "Entered saveSerialized(" + serialDataPath
328                         + ", appDataElements)");
329 
330         long timer = System.currentTimeMillis();
331 
332         if (serialDataPath == null)
333         {
334             return;
335         }
336 
337         File serialData = new File(serialDataPath);
338 
339         try
340         {
341             boolean result = serialData.createNewFile();
342             if ( result == false )
343             {
344             	getLogger().error("Could not create new serialized file");
345             }
346 
347             // Try to delete the file
348             result = serialData.delete();
349             if ( result == false )
350             {
351             	getLogger().error("Serialized file could not be removed");
352             }
353             
354         }
355         catch (IOException e)
356         {
357             getLogger().info(
358                     "Could not create serialized file " + serialDataPath
359                             + ", not serializing the XML data", e);
360             return;
361         }
362 
363         ObjectOutputStream out = null;
364         ObjectInputStream in = null;
365 
366         try
367         {
368             // write the appData file out
369         	FileOutputStream fout = new FileOutputStream(serialDataPath);
370             out = new ObjectOutputStream(fout);
371             out.writeObject(appDataElements);
372             out.flush();
373 
374             // read the file back in. for some reason on OSX 10.1
375             // this is necessary.
376             FileInputStream fin = new FileInputStream(serialDataPath);
377             in = new ObjectInputStream(fin);
378             /* Map dummy = (Map) */ in.readObject();
379 
380             getLogger().debug("Serializing successful");
381         }
382         catch (IOException e)
383         {
384             getLogger().info(
385                     "Could not write serialized file to " + serialDataPath
386                             + ", not serializing the XML data", e);
387         }
388         catch (ClassNotFoundException e)
389         {
390             getLogger().info(
391                     "Could not re-read serialized file from " + serialDataPath, e);
392         }
393         finally
394         {
395             try
396             {
397                 if (out != null)
398                 {
399                     out.close();
400                 }
401             }
402             catch (IOException e)
403             {
404                 getLogger().error("Exception while closing file", e);
405             }
406             try
407             {
408                 if (in != null)
409                 {
410                     in.close();
411                 }
412             }
413             catch (IOException e)
414             {
415                 getLogger().error("Exception while closing file", e);
416             }
417         }
418 
419         getLogger().debug("Saving took " + (System.currentTimeMillis() - timer));
420     }
421 
422     /**
423      * Gets an instance of a named group either from the pool or by calling the
424      * Factory Service if the pool is empty.
425      *
426      * @param groupName
427      *            the name of the group.
428      * @return a Group instance.
429      * @throws IntakeException
430      *             if recycling fails.
431      */
432     @Override
433     public Group getGroup(String groupName) throws IntakeException
434     {
435         Group group = null;
436 
437         AppData appData = groupNames.get(groupName);
438 
439         if (groupName == null)
440         {
441             throw new IntakeException(
442                     "Intake IntakeServiceImpl.getGroup(groupName) is null");
443         }
444 
445         if (appData == null)
446         {
447             throw new IntakeException(
448                     "Intake IntakeServiceImpl.getGroup(groupName): No XML definition for Group "
449                             + groupName + " found");
450         }
451         try
452         {
453             group = keyedPools.get(appData).borrowObject(groupName);
454         }
455         catch (Exception e)
456         {
457             throw new IntakeException("Could not get group " + groupName, e);
458         }
459         return group;
460     }
461 
462     /**
463      * Puts a Group back to the pool.
464      *
465      * @param instance
466      *            the object instance to recycle.
467      *
468      * @throws IntakeException
469      *             The passed group name does not exist.
470      */
471     @Override
472     public void releaseGroup(Group instance) throws IntakeException
473     {
474         if (instance != null)
475         {
476             String groupName = instance.getIntakeGroupName();
477             AppData appData = groupNames.get(groupName);
478 
479             if (appData == null)
480             {
481                 throw new IntakeException(
482                         "Intake IntakeServiceImpl.releaseGroup(groupName): "
483                                 + "No XML definition for Group " + groupName
484                                 + " found");
485             }
486 
487             try
488             {
489                 keyedPools.get(appData).returnObject(groupName, instance);
490             }
491             catch (Exception e)
492             {
493                 throw new IntakeException("Could not get group " + groupName, e);
494             }
495         }
496     }
497 
498     /**
499      * Gets the current size of the pool for a group.
500      *
501      * @param groupName
502      *            the name of the group.
503      *
504      * @throws IntakeException
505      *             The passed group name does not exist.
506      */
507     @Override
508     public int getSize(String groupName) throws IntakeException
509     {
510         AppData appData = groupNames.get(groupName);
511         if (appData == null)
512         {
513             throw new IntakeException(
514                     "Intake IntakeServiceImpl.Size(groupName): No XML definition for Group "
515                             + groupName + " found");
516         }
517 
518         KeyedObjectPool<String, Group> kop = keyedPools.get(appData);
519 
520         return kop.getNumActive(groupName) + kop.getNumIdle(groupName);
521     }
522 
523     /**
524      * Names of all the defined groups.
525      *
526      * @return array of names.
527      */
528     @Override
529     public String[] getGroupNames()
530     {
531         return groupNames.keySet().toArray(new String[0]);
532     }
533 
534     /**
535      * Gets the key (usually a short identifier) for a group.
536      *
537      * @param groupName
538      *            the name of the group.
539      * @return the the key.
540      */
541     @Override
542     public String getGroupKey(String groupName)
543     {
544         return groupKeyMap.get(groupName);
545     }
546 
547     /**
548      * Gets the group name given its key.
549      *
550      * @param groupKey
551      *            the key.
552      * @return groupName the name of the group.
553      */
554     @Override
555     public String getGroupName(String groupKey)
556     {
557         return groupNameMap.get(groupKey);
558     }
559 
560     /**
561      * Gets the Method that can be used to set a property.
562      *
563      * @param className
564      *            the name of the object.
565      * @param propName
566      *            the name of the property.
567      * @return the setter.
568      * @throws ClassNotFoundException if the class specified could not be loaded
569      * @throws IntrospectionException if the property setter could not be called
570      */
571     @Override
572     public Method getFieldSetter(String className, String propName)
573             throws ClassNotFoundException, IntrospectionException
574     {
575         Map<String, Method> settersForClassName = setterMap.get(className);
576 
577         if (settersForClassName == null)
578         {
579             throw new IntrospectionException("No setter Map for " + className
580                     + " available!");
581         }
582 
583         Method setter = settersForClassName.get(propName);
584 
585         if (setter == null)
586         {
587             PropertyDescriptor pd = new PropertyDescriptor(propName, Class
588                     .forName(className));
589             synchronized (setterMap)
590             {
591                 setter = pd.getWriteMethod();
592                 settersForClassName.put(propName, setter);
593                 if (setter == null)
594                 {
595                     getLogger().error(
596                             "Intake: setter for '" + propName + "' in class '"
597                                     + className + "' could not be found.");
598                 }
599             }
600             // we have already completed the reflection on the getter, so
601             // save it so we do not have to repeat
602             synchronized (getterMap)
603             {
604                 Map<String, Method> gettersForClassName = getterMap.get(className);
605 
606                 if (gettersForClassName != null)
607                 {
608                     Method getter = pd.getReadMethod();
609                     if (getter != null)
610                     {
611                         gettersForClassName.put(propName, getter);
612                     }
613                 }
614             }
615         }
616         return setter;
617     }
618 
619     /**
620      * Gets the Method that can be used to get a property value.
621      *
622      * @param className
623      *            the name of the object.
624      * @param propName
625      *            the name of the property.
626      * @return the getter.
627      * @throws ClassNotFoundException if the class specified could not be loaded
628      * @throws IntrospectionException if the property getter could not be called
629      */
630     @Override
631     public Method getFieldGetter(String className, String propName)
632             throws ClassNotFoundException, IntrospectionException
633     {
634         Map<String, Method> gettersForClassName = getterMap.get(className);
635 
636         if (gettersForClassName == null)
637         {
638             throw new IntrospectionException("No getter Map for " + className
639                     + " available!");
640         }
641 
642         Method getter = gettersForClassName.get(propName);
643 
644         if (getter == null)
645         {
646             PropertyDescriptor pd = null;
647             synchronized (getterMap)
648             {
649                 pd = new PropertyDescriptor(propName, Class.forName(className));
650                 getter = pd.getReadMethod();
651                 gettersForClassName.put(propName, getter);
652                 if (getter == null)
653                 {
654                     getLogger().error(
655                             "Intake: getter for '" + propName + "' in class '"
656                                     + className + "' could not be found.");
657                 }
658             }
659             // we have already completed the reflection on the setter, so
660             // save it so we do not have to repeat
661             synchronized (setterMap)
662             {
663                 Map<String, Method> settersForClassName = getterMap.get(className);
664 
665                 if (settersForClassName != null)
666                 {
667                     Method setter = pd.getWriteMethod();
668                     if (setter != null)
669                     {
670                         settersForClassName.put(propName, setter);
671                     }
672                 }
673             }
674         }
675         return getter;
676     }
677 
678     // ---------------- Avalon Lifecycle Methods ---------------------
679     /**
680      * Avalon component lifecycle method
681      */
682     @Override
683     public void configure(Configuration conf) throws ConfigurationException
684     {
685         final Configuration xmlPaths = conf.getChild(XML_PATHS, false);
686 
687         xmlPathes = new ArrayList<String>();
688 
689         if (xmlPaths == null)
690         {
691             xmlPathes.add(XML_PATH_DEFAULT);
692         }
693         else
694         {
695             Configuration[] nameVal = xmlPaths.getChildren();
696             for (int i = 0; i < nameVal.length; i++)
697             {
698                 String val = nameVal[i].getValue();
699                 xmlPathes.add(val);
700             }
701         }
702 
703         serialDataPath = conf.getChild(SERIAL_XML, false).getValue(SERIAL_XML_DEFAULT);
704 
705         if (!serialDataPath.equalsIgnoreCase("none"))
706         {
707             serialDataPath = new File(applicationRoot, serialDataPath).getAbsolutePath();
708         }
709         else
710         {
711             serialDataPath = null;
712         }
713 
714         getLogger().debug("Path for serializing: " + serialDataPath);
715     }
716 
717     /**
718      * Avalon component lifecycle method Initializes the service by loading
719      * xml rule files and creating the Intake groups.
720      *
721      * @throws Exception
722      *             if initialization fails.
723      */
724     @Override
725     public void initialize() throws Exception
726     {
727         Map<AppData, File> appDataElements = null;
728 
729         groupNames = new HashMap<String, AppData>();
730         groupKeyMap = new HashMap<String, String>();
731         groupNameMap = new HashMap<String, String>();
732         getterMap = new HashMap<String, Map<String,Method>>();
733         setterMap = new HashMap<String, Map<String,Method>>();
734         keyedPools = new HashMap<AppData, KeyedObjectPool<String, Group>>();
735 
736         Set<File> xmlFiles = new HashSet<File>();
737 
738         long timeStamp = 0;
739         
740         getLogger().debug("logger is " + getLogger().getClass().getSimpleName());
741 
742         for (String xmlPath : xmlPathes)
743         {
744             // Files are webapp.root relative
745             File xmlFile = new File(applicationRoot, xmlPath).getAbsoluteFile();
746 
747             getLogger().debug("Path for XML File: " + xmlFile);
748 
749             if (!xmlFile.canRead())
750             {
751                 String READ_ERR = "Could not read input file with path "
752                         + xmlPath + ".  Looking for file " + xmlFile;
753 
754                 getLogger().error(READ_ERR);
755                 throw new Exception(READ_ERR);
756             }
757 
758             xmlFiles.add(xmlFile);
759 
760             getLogger().debug("Added " + xmlPath + " as File to parse");
761 
762             // Get the timestamp of the youngest file to be compared with
763             // a serialized file. If it is younger than the serialized file,
764             // then we have to parse the XML anyway.
765             timeStamp = xmlFile.lastModified() > timeStamp ? xmlFile
766                     .lastModified() : timeStamp;
767         }
768 
769         Map<AppData, File> serializedMap = loadSerialized(serialDataPath, timeStamp);
770 
771         if (serializedMap != null)
772         {
773             // Use the serialized data as XML groups. Don't parse.
774             appDataElements = serializedMap;
775             getLogger().debug("Using the serialized map");
776         }
777         else
778         {
779         	long timer = System.currentTimeMillis();
780 
781             // Parse all the given XML files
782             JAXBContext jaxb = JAXBContext.newInstance(AppData.class);
783             Unmarshaller um = jaxb.createUnmarshaller();
784 
785             // Debug mapping
786             um.setEventHandler(new DefaultValidationEventHandler());
787 
788             // Enable logging
789             Listener logEnabledListener = new AvalonLogEnabledListener();
790             um.setListener(logEnabledListener);
791 
792             URL schemaURL = getClass().getResource("/intake.xsd");
793             SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
794             um.setSchema(schemaFactory.newSchema(schemaURL));
795             appDataElements = new HashMap<AppData, File>();
796 
797             for (File xmlFile : xmlFiles)
798             {
799                 getLogger().debug("Now parsing: " + xmlFile);
800                 FileInputStream fis = null;
801                 try
802                 {
803                     fis = new FileInputStream(xmlFile);
804                     AppData/../../../org/apache/fulcrum/intake/model/AppData.html#AppData">AppData appData = (AppData) um.unmarshal(fis);
805 
806                     appDataElements.put(appData, xmlFile);
807                     getLogger().debug("Saving AppData for " + xmlFile);
808                 }
809                 finally
810                 {
811                     if (fis != null)
812                     {
813                         try
814                         {
815                             fis.close();
816                         }
817                         catch (IOException e)
818                         {
819                             getLogger().warn("Could not close file " + xmlFile);
820                         }
821                     }
822                 }
823             }
824 
825             getLogger().debug("Parsing took " + (System.currentTimeMillis() - timer));
826             saveSerialized(serialDataPath, appDataElements);
827         }
828 
829         int counter = 0;
830         AppData appData;
831         File dataFile;
832         for ( Entry<AppData, File> entry : appDataElements.entrySet() )
833         {
834         	// Set the entry pair
835         	appData = entry.getKey();
836         	dataFile = entry.getValue();
837         	
838             int maxPooledGroups = 0;
839             List<Group> glist = appData.getGroups();
840 
841             String groupPrefix = appData.getGroupPrefix();
842 
843             for (ListIterator<Group> i = glist.listIterator(glist.size()); i.hasPrevious();)
844             {
845                 Group g = i.previous();
846                 String groupName = g.getIntakeGroupName();
847 
848                 boolean registerUnqualified = registerGroup(groupName, g, appData, true);
849 
850                 if (!registerUnqualified)
851                 {
852                     getLogger().info(
853                             "Ignored redefinition of Group " + groupName
854                                     + " or Key " + g.getGID() + " from "
855                                     + dataFile);
856                 }
857 
858                 if (groupPrefix != null)
859                 {
860                     StringBuilder qualifiedName = new StringBuilder();
861                     qualifiedName.append(groupPrefix).append(':').append(groupName);
862 
863                     // Add the fully qualified group name. Do _not_ check
864                     // for
865                     // the existence of the key if the unqualified
866                     // registration succeeded
867                     // (because then it was added by the registerGroup
868                     // above).
869                     if (!registerGroup(qualifiedName.toString(), g,
870                             appData, !registerUnqualified))
871                     {
872                         getLogger().error(
873                             "Could not register fully qualified name "
874                                     + qualifiedName
875                                     + ", maybe two XML files have the same prefix. Ignoring it.");
876                     }
877                 }
878 
879                 // Init fields
880                 for (Field<?> f : g.getFields())
881                 {
882                     f.initGetterAndSetter();
883                 }
884 
885                 maxPooledGroups = Math.max(maxPooledGroups, g.getPoolCapacity());
886             }
887 
888             KeyedPooledObjectFactory<String, Group> factory =
889                 new Group.GroupFactory(appData);
890 
891             GenericKeyedObjectPoolConfig<Group> poolConfig = new GenericKeyedObjectPoolConfig<Group>();
892             poolConfig.setMaxTotalPerKey(maxPooledGroups);
893             poolConfig.setJmxEnabled(true);
894             poolConfig.setJmxNamePrefix("fulcrum-intake-pool-" + counter++);
895 
896             keyedPools.put(appData,
897                 new GenericKeyedObjectPool<String, Group>(factory, poolConfig));
898         }
899 
900         if (getLogger().isInfoEnabled())
901         {
902             getLogger().info("Intake Service is initialized now.");
903         }
904     }
905 
906     /**
907      * Note that the avalon.entry key="urn:avalon:home" 
908      * and the type is {@link java.io.File}
909      * 
910      * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
911      * 
912      * @param context the Context to use
913      * @throws ContextException if the context is not found
914      */
915     @Override
916     public void contextualize(Context context) throws ContextException
917     {
918         this.applicationRoot = context.get("urn:avalon:home").toString();
919     }
920 
921     /**
922      * Avalon component lifecycle method
923      *
924      * avalon.dependency type="org.apache.fulcrum.localization.LocalizationService"
925      * 
926      * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
927      * 
928      * @param manager the service manager
929      * @throws ServiceException generic exception
930      */
931     @Override
932     public void service(ServiceManager manager) throws ServiceException
933     {
934         IntakeServiceFacade.setIntakeService(this);
935     }
936 }