Coverage Report - org.apache.fulcrum.intake.IntakeServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
IntakeServiceImpl
73%
213/290
57%
63/110
6.125
IntakeServiceImpl$1
N/A
N/A
6.125
IntakeServiceImpl$AvalonLogEnabledListener
100%
5/5
100%
2/2
6.125
 
 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: IntakeServiceImpl.java 1855912 2019-03-20 14:50:06Z painter $
 79  
  */
 80  54
 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  20
     private List<String> xmlPathes = null;
 107  
 
 108  
     /** Configured location of the serialization file */
 109  20
     private String serialDataPath = null;
 110  
 
 111  
     /**
 112  
      * Local Class to enable Avalon logging on the model classes
 113  
      *
 114  
      */
 115  2
     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  80
                         super.beforeUnmarshal(target, parent);
 124  
 
 125  80
                         if (target instanceof LogEnabled)
 126  
                         {
 127  34
                                 ((LogEnabled)target).enableLogging(getLogger());
 128  
                         }
 129  80
                 }
 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  120
         if (groupNames.containsKey(groupName))
 151  
         {
 152  
             // This name already exists.
 153  0
             return false;
 154  
         }
 155  
 
 156  120
         boolean keyExists = groupNameMap.containsKey(group.getGID());
 157  
 
 158  120
         if (checkKey && keyExists)
 159  
         {
 160  
             // The key for this package is already registered for another group
 161  0
             return false;
 162  
         }
 163  
 
 164  120
         groupNames.put(groupName, appData);
 165  120
         groupKeyMap.put(groupName, group.getGID());
 166  
 
 167  120
         if (!keyExists)
 168  
         {
 169  
             // This key does not exist. Add it to the hash.
 170  120
             groupNameMap.put(group.getGID(), groupName);
 171  
         }
 172  
 
 173  120
         List<Field<?>> fields = group.getFields();
 174  120
         for (Field<?> field : fields)
 175  
         {
 176  560
             String className = field.getMapToObject();
 177  560
             if (!getterMap.containsKey(className))
 178  
             {
 179  80
                 getterMap.put(className, new HashMap<String, Method>());
 180  80
                 setterMap.put(className, new HashMap<String, Method>());
 181  
             }
 182  560
         }
 183  120
         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  20
         getLogger().debug(
 199  
                 "Entered loadSerialized(" + serialDataPath + ", " + timeStamp
 200  
                         + ")");
 201  
 
 202  20
         long timer = System.currentTimeMillis();
 203  
 
 204  20
         if (serialDataPath == null)
 205  
         {
 206  0
             return null;
 207  
         }
 208  
 
 209  20
         File serialDataFile = new File(serialDataPath);
 210  
 
 211  20
         if (!serialDataFile.exists())
 212  
         {
 213  1
             getLogger().info("No serialized file found, parsing XML");
 214  1
             return null;
 215  
         }
 216  
 
 217  19
         if (serialDataFile.lastModified() <= timeStamp)
 218  
         {
 219  0
             getLogger().info("serialized file too old, parsing XML");
 220  0
             return null;
 221  
         }
 222  
 
 223  19
         ObjectInputStream in = null;
 224  19
         Map<AppData, File> serialData = null;
 225  
 
 226  
         try
 227  
         {
 228  19
                 FileInputStream fin = new FileInputStream(serialDataFile);
 229  19
             in = new ObjectInputStream(fin);
 230  19
             Object o = in.readObject();
 231  
 
 232  19
             if (o instanceof Map)
 233  
             {
 234  
                 @SuppressWarnings("unchecked") // checked with instanceof
 235  19
                                 Map<AppData, File> map = (Map<AppData, File>) o;
 236  19
                                 serialData = map;
 237  19
             }
 238  
             else
 239  
             {
 240  
                 // This could be old file from intake. Try to delete it
 241  0
                 getLogger().info("serialized object is not an intake map, ignoring");
 242  0
                 in.close();
 243  0
                 in = null;
 244  
                 
 245  
                 // Try to delete the file
 246  0
                 boolean result = serialDataFile.delete();
 247  0
                 if ( result == false )
 248  
                 {
 249  0
                         getLogger().error("Unknown serialized file could not be removed");
 250  
                 }
 251  
             }
 252  
         }
 253  0
         catch (IOException e)
 254  
         {
 255  0
             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  0
             serialData = null;
 260  
         }
 261  0
         catch (ClassNotFoundException e)
 262  
         {
 263  0
             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  0
             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  0
             try
 274  
             {
 275  19
                 if (in != null)
 276  
                 {
 277  19
                     in.close();
 278  
                 }
 279  
             }
 280  0
             catch (IOException e)
 281  
             {
 282  0
                 getLogger().error("Exception while closing file", e);
 283  19
             }
 284  0
         }
 285  
 
 286  
         // Recreate transient loggers
 287  19
         if (serialData != null)
 288  
         {
 289  19
                 for (AppData appData : serialData.keySet())
 290  
                 {
 291  38
                         for (Group group : appData.getGroups())
 292  
                         {
 293  114
                                 if (group instanceof LogEnabled)
 294  
                                 {
 295  114
                                         ((LogEnabled)group).enableLogging(getLogger());
 296  
                                 }
 297  
 
 298  114
                                     for (Field<?> field : group.getFields())
 299  
                                     {
 300  532
                                     if (field instanceof LogEnabled)
 301  
                                     {
 302  532
                                             ((LogEnabled)field).enableLogging(getLogger());
 303  
                                     }
 304  532
                                     }
 305  114
                         }
 306  38
                 }
 307  
         }
 308  19
         getLogger().info("Loaded serialized map object, ignoring XML");
 309  19
         getLogger().debug("Loading took " + (System.currentTimeMillis() - timer));
 310  19
         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  1
         getLogger().debug(
 327  
                 "Entered saveSerialized(" + serialDataPath
 328  
                         + ", appDataElements)");
 329  
 
 330  1
         long timer = System.currentTimeMillis();
 331  
 
 332  1
         if (serialDataPath == null)
 333  
         {
 334  0
             return;
 335  
         }
 336  
 
 337  1
         File serialData = new File(serialDataPath);
 338  
 
 339  
         try
 340  
         {
 341  1
             boolean result = serialData.createNewFile();
 342  1
             if ( result == false )
 343  
             {
 344  0
                     getLogger().error("Could not create new serialized file");
 345  
             }
 346  
 
 347  
             // Try to delete the file
 348  1
             result = serialData.delete();
 349  1
             if ( result == false )
 350  
             {
 351  0
                     getLogger().error("Serialized file could not be removed");
 352  
             }
 353  
             
 354  
         }
 355  0
         catch (IOException e)
 356  
         {
 357  0
             getLogger().info(
 358  
                     "Could not create serialized file " + serialDataPath
 359  
                             + ", not serializing the XML data", e);
 360  0
             return;
 361  1
         }
 362  
 
 363  1
         ObjectOutputStream out = null;
 364  1
         ObjectInputStream in = null;
 365  
 
 366  
         try
 367  
         {
 368  
             // write the appData file out
 369  1
                 FileOutputStream fout = new FileOutputStream(serialDataPath);
 370  1
             out = new ObjectOutputStream(fout);
 371  1
             out.writeObject(appDataElements);
 372  1
             out.flush();
 373  
 
 374  
             // read the file back in. for some reason on OSX 10.1
 375  
             // this is necessary.
 376  1
             FileInputStream fin = new FileInputStream(serialDataPath);
 377  1
             in = new ObjectInputStream(fin);
 378  1
             /* Map dummy = (Map) */ in.readObject();
 379  
 
 380  1
             getLogger().debug("Serializing successful");
 381  
         }
 382  0
         catch (IOException e)
 383  
         {
 384  0
             getLogger().info(
 385  
                     "Could not write serialized file to " + serialDataPath
 386  
                             + ", not serializing the XML data", e);
 387  
         }
 388  0
         catch (ClassNotFoundException e)
 389  
         {
 390  0
             getLogger().info(
 391  
                     "Could not re-read serialized file from " + serialDataPath, e);
 392  
         }
 393  
         finally
 394  
         {
 395  0
             try
 396  
             {
 397  1
                 if (out != null)
 398  
                 {
 399  1
                     out.close();
 400  
                 }
 401  
             }
 402  0
             catch (IOException e)
 403  
             {
 404  0
                 getLogger().error("Exception while closing file", e);
 405  1
             }
 406  
             try
 407  
             {
 408  1
                 if (in != null)
 409  
                 {
 410  1
                     in.close();
 411  
                 }
 412  
             }
 413  0
             catch (IOException e)
 414  
             {
 415  0
                 getLogger().error("Exception while closing file", e);
 416  1
             }
 417  0
         }
 418  
 
 419  1
         getLogger().debug("Saving took " + (System.currentTimeMillis() - timer));
 420  1
     }
 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  25
         Group group = null;
 436  
 
 437  25
         AppData appData = groupNames.get(groupName);
 438  
 
 439  25
         if (groupName == null)
 440  
         {
 441  0
             throw new IntakeException(
 442  
                     "Intake IntakeServiceImpl.getGroup(groupName) is null");
 443  
         }
 444  
 
 445  25
         if (appData == null)
 446  
         {
 447  0
             throw new IntakeException(
 448  
                     "Intake IntakeServiceImpl.getGroup(groupName): No XML definition for Group "
 449  
                             + groupName + " found");
 450  
         }
 451  
         try
 452  
         {
 453  25
             group = keyedPools.get(appData).borrowObject(groupName);
 454  
         }
 455  0
         catch (Exception e)
 456  
         {
 457  0
             throw new IntakeException("Could not get group " + groupName, e);
 458  25
         }
 459  25
         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  0
         if (instance != null)
 475  
         {
 476  0
             String groupName = instance.getIntakeGroupName();
 477  0
             AppData appData = groupNames.get(groupName);
 478  
 
 479  0
             if (appData == null)
 480  
             {
 481  0
                 throw new IntakeException(
 482  
                         "Intake IntakeServiceImpl.releaseGroup(groupName): "
 483  
                                 + "No XML definition for Group " + groupName
 484  
                                 + " found");
 485  
             }
 486  
 
 487  
             try
 488  
             {
 489  0
                 keyedPools.get(appData).returnObject(groupName, instance);
 490  
             }
 491  0
             catch (Exception e)
 492  
             {
 493  0
                 throw new IntakeException("Could not get group " + groupName, e);
 494  0
             }
 495  
         }
 496  0
     }
 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  0
         AppData appData = groupNames.get(groupName);
 511  0
         if (appData == null)
 512  
         {
 513  0
             throw new IntakeException(
 514  
                     "Intake IntakeServiceImpl.Size(groupName): No XML definition for Group "
 515  
                             + groupName + " found");
 516  
         }
 517  
 
 518  0
         KeyedObjectPool<String, Group> kop = keyedPools.get(appData);
 519  
 
 520  0
         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  0
         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  0
         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  0
         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  60
         Map<String, Method> settersForClassName = setterMap.get(className);
 576  
 
 577  60
         if (settersForClassName == null)
 578  
         {
 579  0
             throw new IntrospectionException("No setter Map for " + className
 580  
                     + " available!");
 581  
         }
 582  
 
 583  60
         Method setter = settersForClassName.get(propName);
 584  
 
 585  60
         if (setter == null)
 586  
         {
 587  60
             PropertyDescriptor pd = new PropertyDescriptor(propName, Class
 588  60
                     .forName(className));
 589  60
             synchronized (setterMap)
 590  
             {
 591  60
                 setter = pd.getWriteMethod();
 592  60
                 settersForClassName.put(propName, setter);
 593  60
                 if (setter == null)
 594  
                 {
 595  0
                     getLogger().error(
 596  
                             "Intake: setter for '" + propName + "' in class '"
 597  
                                     + className + "' could not be found.");
 598  
                 }
 599  60
             }
 600  
             // we have already completed the reflection on the getter, so
 601  
             // save it so we do not have to repeat
 602  60
             synchronized (getterMap)
 603  
             {
 604  60
                 Map<String, Method> gettersForClassName = getterMap.get(className);
 605  
 
 606  60
                 if (gettersForClassName != null)
 607  
                 {
 608  60
                     Method getter = pd.getReadMethod();
 609  60
                     if (getter != null)
 610  
                     {
 611  60
                         gettersForClassName.put(propName, getter);
 612  
                     }
 613  
                 }
 614  60
             }
 615  
         }
 616  60
         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  60
         Map<String, Method> gettersForClassName = getterMap.get(className);
 635  
 
 636  60
         if (gettersForClassName == null)
 637  
         {
 638  0
             throw new IntrospectionException("No getter Map for " + className
 639  
                     + " available!");
 640  
         }
 641  
 
 642  60
         Method getter = gettersForClassName.get(propName);
 643  
 
 644  60
         if (getter == null)
 645  
         {
 646  60
             PropertyDescriptor pd = null;
 647  60
             synchronized (getterMap)
 648  
             {
 649  60
                 pd = new PropertyDescriptor(propName, Class.forName(className));
 650  60
                 getter = pd.getReadMethod();
 651  60
                 gettersForClassName.put(propName, getter);
 652  60
                 if (getter == null)
 653  
                 {
 654  0
                     getLogger().error(
 655  
                             "Intake: getter for '" + propName + "' in class '"
 656  
                                     + className + "' could not be found.");
 657  
                 }
 658  60
             }
 659  
             // we have already completed the reflection on the setter, so
 660  
             // save it so we do not have to repeat
 661  60
             synchronized (setterMap)
 662  
             {
 663  60
                 Map<String, Method> settersForClassName = getterMap.get(className);
 664  
 
 665  60
                 if (settersForClassName != null)
 666  
                 {
 667  60
                     Method setter = pd.getWriteMethod();
 668  60
                     if (setter != null)
 669  
                     {
 670  60
                         settersForClassName.put(propName, setter);
 671  
                     }
 672  
                 }
 673  60
             }
 674  
         }
 675  60
         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  20
         final Configuration xmlPaths = conf.getChild(XML_PATHS, false);
 686  
 
 687  20
         xmlPathes = new ArrayList<String>();
 688  
 
 689  20
         if (xmlPaths == null)
 690  
         {
 691  0
             xmlPathes.add(XML_PATH_DEFAULT);
 692  
         }
 693  
         else
 694  
         {
 695  20
             Configuration[] nameVal = xmlPaths.getChildren();
 696  60
             for (int i = 0; i < nameVal.length; i++)
 697  
             {
 698  40
                 String val = nameVal[i].getValue();
 699  40
                 xmlPathes.add(val);
 700  
             }
 701  
         }
 702  
 
 703  20
         serialDataPath = conf.getChild(SERIAL_XML, false).getValue(SERIAL_XML_DEFAULT);
 704  
 
 705  20
         if (!serialDataPath.equalsIgnoreCase("none"))
 706  
         {
 707  20
             serialDataPath = new File(applicationRoot, serialDataPath).getAbsolutePath();
 708  
         }
 709  
         else
 710  
         {
 711  0
             serialDataPath = null;
 712  
         }
 713  
 
 714  20
         getLogger().debug("Path for serializing: " + serialDataPath);
 715  20
     }
 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  20
         Map<AppData, File> appDataElements = null;
 728  
 
 729  20
         groupNames = new HashMap<String, AppData>();
 730  20
         groupKeyMap = new HashMap<String, String>();
 731  20
         groupNameMap = new HashMap<String, String>();
 732  20
         getterMap = new HashMap<String, Map<String,Method>>();
 733  20
         setterMap = new HashMap<String, Map<String,Method>>();
 734  20
         keyedPools = new HashMap<AppData, KeyedObjectPool<String, Group>>();
 735  
 
 736  20
         Set<File> xmlFiles = new HashSet<File>();
 737  
 
 738  20
         long timeStamp = 0;
 739  
 
 740  20
         for (String xmlPath : xmlPathes)
 741  
         {
 742  
             // Files are webapp.root relative
 743  40
             File xmlFile = new File(applicationRoot, xmlPath).getAbsoluteFile();
 744  
 
 745  40
             getLogger().debug("Path for XML File: " + xmlFile);
 746  
 
 747  40
             if (!xmlFile.canRead())
 748  
             {
 749  0
                 String READ_ERR = "Could not read input file with path "
 750  
                         + xmlPath + ".  Looking for file " + xmlFile;
 751  
 
 752  0
                 getLogger().error(READ_ERR);
 753  0
                 throw new Exception(READ_ERR);
 754  
             }
 755  
 
 756  40
             xmlFiles.add(xmlFile);
 757  
 
 758  40
             getLogger().debug("Added " + xmlPath + " as File to parse");
 759  
 
 760  
             // Get the timestamp of the youngest file to be compared with
 761  
             // a serialized file. If it is younger than the serialized file,
 762  
             // then we have to parse the XML anyway.
 763  40
             timeStamp = xmlFile.lastModified() > timeStamp ? xmlFile
 764  20
                     .lastModified() : timeStamp;
 765  40
         }
 766  
 
 767  20
         Map<AppData, File> serializedMap = loadSerialized(serialDataPath, timeStamp);
 768  
 
 769  20
         if (serializedMap != null)
 770  
         {
 771  
             // Use the serialized data as XML groups. Don't parse.
 772  19
             appDataElements = serializedMap;
 773  19
             getLogger().debug("Using the serialized map");
 774  
         }
 775  
         else
 776  
         {
 777  1
                 long timer = System.currentTimeMillis();
 778  
 
 779  
             // Parse all the given XML files
 780  1
             JAXBContext jaxb = JAXBContext.newInstance(AppData.class);
 781  1
             Unmarshaller um = jaxb.createUnmarshaller();
 782  
 
 783  
             // Debug mapping
 784  1
             um.setEventHandler(new DefaultValidationEventHandler());
 785  
 
 786  
             // Enable logging
 787  1
             Listener logEnabledListener = new AvalonLogEnabledListener();
 788  1
             um.setListener(logEnabledListener);
 789  
 
 790  1
             URL schemaURL = getClass().getResource("/intake.xsd");
 791  1
             SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 792  1
             um.setSchema(schemaFactory.newSchema(schemaURL));
 793  1
             appDataElements = new HashMap<AppData, File>();
 794  
 
 795  1
             for (File xmlFile : xmlFiles)
 796  
             {
 797  2
                 getLogger().debug("Now parsing: " + xmlFile);
 798  2
                 FileInputStream fis = null;
 799  
                 try
 800  
                 {
 801  2
                     fis = new FileInputStream(xmlFile);
 802  2
                     AppData appData = (AppData) um.unmarshal(fis);
 803  
 
 804  2
                     appDataElements.put(appData, xmlFile);
 805  2
                     getLogger().debug("Saving AppData for " + xmlFile);
 806  
                 }
 807  
                 finally
 808  
                 {
 809  2
                     if (fis != null)
 810  
                     {
 811  
                         try
 812  
                         {
 813  2
                             fis.close();
 814  
                         }
 815  0
                         catch (IOException e)
 816  
                         {
 817  0
                             getLogger().warn("Could not close file " + xmlFile);
 818  2
                         }
 819  
                     }
 820  0
                 }
 821  2
             }
 822  
 
 823  1
             getLogger().debug("Parsing took " + (System.currentTimeMillis() - timer));
 824  1
             saveSerialized(serialDataPath, appDataElements);
 825  
         }
 826  
 
 827  20
         int counter = 0;
 828  
         AppData appData;
 829  
         File dataFile;
 830  20
         for ( Entry<AppData, File> entry : appDataElements.entrySet() )
 831  
         {
 832  
                 // Set the entry pair
 833  40
                 appData = entry.getKey();
 834  40
                 dataFile = entry.getValue();
 835  
                 
 836  40
             int maxPooledGroups = 0;
 837  40
             List<Group> glist = appData.getGroups();
 838  
 
 839  40
             String groupPrefix = appData.getGroupPrefix();
 840  
 
 841  40
             for (ListIterator<Group> i = glist.listIterator(glist.size()); i.hasPrevious();)
 842  
             {
 843  120
                 Group g = i.previous();
 844  120
                 String groupName = g.getIntakeGroupName();
 845  
 
 846  120
                 boolean registerUnqualified = registerGroup(groupName, g, appData, true);
 847  
 
 848  120
                 if (!registerUnqualified)
 849  
                 {
 850  0
                     getLogger().info(
 851  
                             "Ignored redefinition of Group " + groupName
 852  0
                                     + " or Key " + g.getGID() + " from "
 853  
                                     + dataFile);
 854  
                 }
 855  
 
 856  120
                 if (groupPrefix != null)
 857  
                 {
 858  0
                     StringBuilder qualifiedName = new StringBuilder();
 859  0
                     qualifiedName.append(groupPrefix).append(':').append(groupName);
 860  
 
 861  
                     // Add the fully qualified group name. Do _not_ check
 862  
                     // for
 863  
                     // the existence of the key if the unqualified
 864  
                     // registration succeeded
 865  
                     // (because then it was added by the registerGroup
 866  
                     // above).
 867  0
                     if (!registerGroup(qualifiedName.toString(), g,
 868  
                             appData, !registerUnqualified))
 869  
                     {
 870  0
                         getLogger().error(
 871  
                             "Could not register fully qualified name "
 872  
                                     + qualifiedName
 873  
                                     + ", maybe two XML files have the same prefix. Ignoring it.");
 874  
                     }
 875  
                 }
 876  
 
 877  
                 // Init fields
 878  120
                 for (Field<?> f : g.getFields())
 879  
                 {
 880  560
                     f.initGetterAndSetter();
 881  560
                 }
 882  
 
 883  120
                 maxPooledGroups = Math.max(maxPooledGroups, g.getPoolCapacity());
 884  120
             }
 885  
 
 886  40
             KeyedPooledObjectFactory<String, Group> factory =
 887  
                 new Group.GroupFactory(appData);
 888  
 
 889  40
             GenericKeyedObjectPoolConfig<Group> poolConfig = new GenericKeyedObjectPoolConfig<Group>();
 890  40
             poolConfig.setMaxTotalPerKey(maxPooledGroups);
 891  40
             poolConfig.setJmxEnabled(true);
 892  40
             poolConfig.setJmxNamePrefix("fulcrum-intake-pool-" + counter++);
 893  
 
 894  40
             keyedPools.put(appData,
 895  
                 new GenericKeyedObjectPool<String, Group>(factory, poolConfig));
 896  40
         }
 897  
 
 898  20
         if (getLogger().isInfoEnabled())
 899  
         {
 900  20
             getLogger().info("Intake Service is initialized now.");
 901  
         }
 902  20
     }
 903  
 
 904  
     /**
 905  
      * Note that the avalon.entry key="urn:avalon:home" 
 906  
      * and the type is {@link java.io.File}
 907  
      * 
 908  
      * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context)
 909  
      * 
 910  
      * @param context the Context to use
 911  
      * @throws ContextException if the context is not found
 912  
      */
 913  
     @Override
 914  
     public void contextualize(Context context) throws ContextException
 915  
     {
 916  20
         this.applicationRoot = context.get("urn:avalon:home").toString();
 917  20
     }
 918  
 
 919  
     /**
 920  
      * Avalon component lifecycle method
 921  
      *
 922  
      * avalon.dependency type="org.apache.fulcrum.localization.LocalizationService"
 923  
      * 
 924  
      * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
 925  
      * 
 926  
      * @param manager the service manager
 927  
      * @throws ServiceException generic exception
 928  
      */
 929  
     @Override
 930  
     public void service(ServiceManager manager) throws ServiceException
 931  
     {
 932  20
         IntakeServiceFacade.setIntakeService(this);
 933  20
     }
 934  
 }