View Javadoc

1   /*
2    * Copyright 2004 Sun Microsystems, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   */
17  package com.sun.syndication.io.impl;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Properties;
24  import java.util.StringTokenizer;
25  
26  /***
27   * Loads and instantiates classes indicated in property files.
28   * <p>
29   * The PlugableClasses is useful for handling collection of classes that are not known or fixed
30   * at development time.
31   * <p>
32   * It reads the class names from a default properties file and from an additional properties
33   * file indicated through a System property. The additional properties file does not override
34   * the default properties file, the classes are aggregated.
35   * <p>
36   * @author Alejandro Abdelnur
37   *
38   */
39  public class PlugableClasses {
40      private String _defaultFile;
41      private String _systemPropertyFile;
42      private String _propertyKey;
43      private ClassLoader _classLoader;
44      private boolean _hardFailure = true;
45      private Class[] _classes;
46  
47      /***
48       * Creates a PlugableClasses instance that will load the classes indicated in properties files.
49       * <p>
50       * It uses the same ClassLoader of the PlugableClasses and it is set for hard failure
51       * (a RuntimeException will be thrown if one or more of the classes cannot be loaded or instanciated).
52       * <p>
53       * @param defaultFile properties file that defines the classes to load
54       * @param systemPropertyFile system property that if present it should be indicate an additional
55       *        properties file to look for classes to load. The additional properties file does not
56       *        override the default properties file, the classes are aggregated.
57       * @param propertyKey property name that contains the list of classes to load. The class names
58       *        must be separated with white spaces.
59       *
60       */
61      public PlugableClasses(String defaultFile,String systemPropertyFile,String propertyKey) {
62          this(defaultFile,systemPropertyFile,propertyKey,true,PlugableClasses.class.getClassLoader());
63      }
64  
65      /***
66       * Creates a PlugableClasses instance that will load the classes indicated in properties files.
67       * <p>
68       * @param defaultFile properties file that defines the classes to load
69       * @param systemPropertyFile system property that if present it should be indicate an additional
70       *        properties file to look for classes to load. The additional properties file does not
71       *        override the default properties file, the classes are aggregated.
72       * @param propertyKey property name that contains the list of classes to load. The class names
73       *        must be separated with white spaces.
74       * @param hardFailure indicates if a RuntimeException will be thrown if one or more of the classes
75       *        cannot be loaded or instanciated. Otherwise it will ignore the failure and continue with
76       *        the other classes.
77       * @param classLoader ClassLoader to use for loading the properties files and classes.
78       *
79       */
80      public PlugableClasses(String defaultFile,String systemPropertyFile,String propertyKey,
81                             boolean hardFailure,ClassLoader classLoader) {
82          _defaultFile = defaultFile;
83          _systemPropertyFile = systemPropertyFile;
84          _propertyKey = propertyKey;
85          _hardFailure = hardFailure;
86          _classLoader = classLoader;
87      }
88  
89      /***
90       * Sets the hard failure mode for the PlugableClasses instance.
91       * <p>
92       * If a hard failure mode is OFF, a ClassNotFoundException will be thrown if one of the classes cannot
93       * be loaded or instanciated. Otherwise it will ignore the failure and continue with the other
94       * classes.
95       * <p>
96       * @param hardFailure <b>true</b> to set hard failure to ON, <b>false</b> to set it to OFF.
97       *
98       */
99      public void setHardFailure(boolean hardFailure) {
100         _hardFailure = hardFailure;
101     }
102 
103     /***
104      * Returns the hard failure mode for the PlugableClasses instance.
105      * <p>
106      * If a hard failure mode is OFF, a RuntimeException will be thrown if one of the classes cannot
107      * be loaded or instanciated. Otherwise it will ignore the failure and continue with the other
108      * classes.
109      * <p>
110      * @return <b>true</b> to set hard failure to ON, <b>false</b> to set it to OFF.
111      *
112      */
113     public boolean isHardFailure() {
114         return _hardFailure;
115     }
116 
117     /***
118      * Loads and returns the classes defined in the properties files.
119      * <p>
120      * @return array containing the classes defined in the properties files.
121      * @throws java.io.IOException thrown if the properties files cannot be found or read, and hard failure is ON.
122      * @throws java.lang.ClassNotFoundException thrown if one of the classes defined in the properties file cannot be loaded
123      *         and hard failure is ON.
124      *
125      */
126     public Class[] getClasses() throws IOException,ClassNotFoundException {
127         synchronized (this) {
128             if (_classes==null) {
129                 _classes = loadClasses();
130             }
131         }
132         return _classes;
133     }
134 
135     /***
136      * Loads, instanciates and returns the classes defined in the properties files.
137      * <p>
138      * @return array containing the classes defined in the properties files.
139      * @throws java.io.IOException thrown if the properties files cannot be found or read, and hard failure is ON.
140      * @throws java.lang.ClassNotFoundException thrown if one of the classes defined in the properties files cannot be loaded
141      *         and hard failure is ON.
142      * @throws java.lang.InstantiationException thrown if one of the classes defined in the properties files cannot be
143      *         instantiated and hard failure is ON.
144      *
145      */
146     public Object[] createInstances() throws IOException,ClassNotFoundException,InstantiationException {
147         List objects = new ArrayList();
148         Class[] classes = getClasses();
149         for (int i=0;i<classes.length;i++) {
150             try {
151                 objects.add(classes[i].newInstance());
152             }
153             catch (Exception ex) {
154                 if (isHardFailure()) {
155                     if (ex instanceof InstantiationException) {
156                         throw (InstantiationException)ex;
157                     }
158                     throw new InstantiationException("Could not create instance for ["+classes[i].getName()+"]");
159                 }
160             }
161         }
162         return objects.toArray();
163     }
164 
165     /***
166      * Loads classes defined in the default and System-properties-indicated properties files.
167      *
168      */
169     private Class[] loadClasses() throws IOException,ClassNotFoundException {
170         List classes = new ArrayList();
171         if (_defaultFile!=null) {
172             classes.addAll(loadClasses(_defaultFile));
173         }
174         if (_systemPropertyFile!=null) {
175             String extraFile = System.getProperty(_systemPropertyFile);
176             if (extraFile!=null) {
177                 classes.addAll(loadClasses(extraFile));
178             }
179         }
180         Class[] array = new Class[classes.size()];
181         classes.toArray(array);
182         return array;
183     }
184 
185     /***
186      * Loads classes defined in a properties file.
187      *
188      */
189     private List loadClasses(String fileName) throws IOException,ClassNotFoundException {
190         List classes = new ArrayList();
191         Properties props = new Properties();
192         InputStream is = _classLoader.getResourceAsStream(fileName);
193         if (is!=null) {
194             try {
195                 props.load(is);
196             }
197             catch (IOException ioEx) {
198                 if (isHardFailure()) {
199                     throw ioEx;
200                 }
201             }
202             String classNames = props.getProperty(_propertyKey);
203             if (classNames!=null) {
204                 classes.addAll(parseAndLoadClasses(classNames));
205             }
206         }
207         else {
208             if (isHardFailure()) {
209                 throw new IOException("Could not find properties file ["+fileName+"]");
210             }
211         }
212         return classes;
213 
214     }
215 
216     private List parseAndLoadClasses(String classNames) throws ClassNotFoundException {
217         List classes = new ArrayList();
218         StringTokenizer st = new StringTokenizer(classNames," ");
219         while (st.hasMoreTokens()) {
220             String className = st.nextToken();
221             Class mClass = null;
222             try {
223                 mClass = _classLoader.loadClass(className);
224                 classes.add(mClass);
225             }
226             catch (ClassNotFoundException ex) {
227                 if (isHardFailure()) {
228                     throw ex;
229                 }
230             }
231         }
232         return classes;
233     }
234 
235 }