View Javadoc

1   /*
2    * JaspertReports JSF Plugin Copyright (C) 2011 A. Alonso Dominguez
3    *
4    * This library is free software; you can redistribute it and/or modify it
5    * under the terms of the GNU Lesser General Public License as published by
6    * the Free Software Foundation; either version 2.1 of the License, or (at
7    * your option) any later version. This library is distributed in the hope
8    * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9    * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   *
11   * See the GNU Lesser General Public License for more details. You should have
12   * received a copy of the GNU Lesser General Public License along with this
13   * library; if not, write to the Free Software Foundation, Inc., 59 Temple
14   * Place, Suite 330, Boston, MA 02111-1307 USA A.
15   *
16   * Alonso Dominguez
17   * alonsoft@users.sf.net
18   */
19  package net.sf.jasperreports.jsf.util;
20  
21  import java.io.BufferedReader;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.lang.reflect.Constructor;
25  import java.net.URL;
26  import java.util.Collections;
27  import java.util.Enumeration;
28  import java.util.HashSet;
29  import java.util.LinkedHashMap;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.logging.Level;
33  import java.util.logging.LogRecord;
34  import java.util.logging.Logger;
35  
36  import net.sf.jasperreports.jsf.Constants;
37  
38  /**
39   * Utility class to handle the different kind of pluggable services required
40   * by the implementation.
41   *
42   * @author A. Alonso Dominguez
43   */
44  public final class Services {
45  
46      /** The logger. */
47      private static final Logger logger = Logger.getLogger(
48              Services.class.getPackage().getName(),
49              Constants.LOG_MESSAGES_BUNDLE);
50  
51      /** The root folder from where load service resource files. */
52      private static final String SERVICES_ROOT = "META-INF/services/";
53  
54      /**
55       * Loads a chain of services from the classpath.
56       * <p>
57       * Services are looked up following convention
58       * <tt>META-INF/services/[serviceClassName]</tt>. Service chains must be
59       * configured the same way that service sets but implementation classes
60       * must have a public constructor accepting another instance of the same
61       * service that may used as a delegator when implementing class doesn't know
62       * how to handle the request.
63       *
64       * @param <T>             Service type to be obtained.
65       * @param clazz           Service interface class object.
66       * @param defaultInstance Last service in the chain. May be
67       *        <code>null</code>.
68       *
69       * @return Chained service implementations offered as a single instance.
70       * @throws ServiceException If any error happens when loading the
71       *         service chain.
72       */
73      @SuppressWarnings("unchecked")
74  	public static <T> T chain(final Class<T> clazz, final T defaultInstance)
75              throws ServiceException {
76          final ClassLoader loader = Util.getClassLoader(null);
77          final Enumeration<URL> resources = getServiceResources(clazz, loader);
78  
79          T current = defaultInstance;
80          while (resources.hasMoreElements()) {
81              final URL url = resources.nextElement();
82              BufferedReader reader = null;
83              try {
84                  String line;
85                  reader = new BufferedReader(new InputStreamReader(
86                          url.openStream()));
87                  while (null != (line = reader.readLine())) {
88                      // skip line comments
89                      if (line.startsWith("#")) {
90                          continue;
91                      }
92  
93                      Class<T> serviceClass;
94                      try {
95                          serviceClass = (Class<T>) loader.loadClass(line);
96                      } catch (final ClassNotFoundException e) {
97                          final LogRecord logRecord = new LogRecord(Level.SEVERE,
98                                  "JRJSF_0014");
99                          logRecord.setParameters(new Object[]{line});
100                         logRecord.setThrown(e);
101                         logRecord.setResourceBundleName(logger.getResourceBundleName());
102                         logRecord.setResourceBundle(logger.getResourceBundle());
103                         logger.log(logRecord);
104                         continue;
105                     }
106 
107                     Constructor<T> constructor;
108                     try {
109                         if (current == null) {
110                             constructor = serviceClass.getConstructor();
111                         } else {
112                             constructor = serviceClass.getConstructor(clazz);
113                         }
114                     } catch (Exception e) {
115                         final LogRecord logRecord = new LogRecord(Level.SEVERE,
116                                 "JRJSF_0027");
117                         logRecord.setParameters(new Object[]{line});
118                         logRecord.setThrown(e);
119                         logRecord.setResourceBundleName(logger.getResourceBundleName());
120                         logRecord.setResourceBundle(logger.getResourceBundle());
121                         logger.log(logRecord);
122                         continue;
123                     }
124 
125                     T instance;
126                     try {
127                         if (current == null) {
128                             instance = constructor.newInstance();
129                         } else {
130                             instance = constructor.newInstance(current);
131                         }
132                     } catch (Exception e) {
133                         final LogRecord logRecord = new LogRecord(Level.SEVERE,
134                                 "JRJSF_0015");
135                         logRecord.setParameters(new Object[]{line});
136                         logRecord.setThrown(e);
137                         logRecord.setResourceBundleName(logger.getResourceBundleName());
138                         logRecord.setResourceBundle(logger.getResourceBundle());
139                         logger.log(logRecord);
140                         continue;
141                     }
142 
143                     current = instance;
144                 }
145             } catch(IOException e) {
146                 final LogRecord logRecord = new LogRecord(Level.SEVERE,
147                         "JRJSF_0012");
148                 logRecord.setParameters(new Object[]{url});
149                 logRecord.setThrown(e);
150                 logRecord.setResourceBundleName(logger.getResourceBundleName());
151                 logRecord.setResourceBundle(logger.getResourceBundle());
152                 logger.log(logRecord);
153             } finally {
154                 if (reader != null) {
155                     try {
156                         reader.close();
157                     } catch(IOException e) { ; }
158                     reader = null;
159                 }
160             }
161         }
162         return current;
163     }
164 
165     /**
166      * Loads a service set from the classpath.
167      * <p>
168      * Services are looked up following convention
169      * <tt>META-INF/services/[serviceClassName]</tt>. Services set configuration
170      * files must contain a list of service implementation classes.
171      *
172      * @param <T>   Service type to be obtained.
173      * @param clazz Service interface class object.
174      * @return a set of services of the requested type.
175      */
176     @SuppressWarnings("unchecked")
177     public static <T> Set<T> set(final Class<T> clazz) throws ServiceException {
178         final ClassLoader loader = Util.getClassLoader(null);
179         final Enumeration<URL> resources = getServiceResources(clazz, loader);
180 
181         final Set<T> serviceSet = new HashSet<T>();
182         while (resources.hasMoreElements()) {
183             final URL url = resources.nextElement();
184             BufferedReader reader = null;
185             try {
186                 String line;
187                 reader = new BufferedReader(new InputStreamReader(
188                         url.openStream()));
189                 while (null != (line = reader.readLine())) {
190                     // skip line comments
191                     if (line.startsWith("#")) {
192                         continue;
193                     }
194 
195                     Class<T> serviceClass;
196                     try {
197                         serviceClass = (Class<T>) loader.loadClass(line);
198                     } catch (final ClassNotFoundException e) {
199                         final LogRecord logRecord = new LogRecord(Level.SEVERE,
200                                 "JRJSF_0014");
201                         logRecord.setParameters(new Object[]{line});
202                         logRecord.setThrown(e);
203                         logger.log(logRecord);
204                         continue;
205                     }
206 
207                     T instance;
208                     try {
209                         instance = serviceClass.newInstance();
210                     } catch (final Exception e) {
211                         final LogRecord logRecord = new LogRecord(Level.SEVERE,
212                                 "JRJSF_0015");
213                         logRecord.setParameters(new Object[]{line});
214                         logRecord.setThrown(e);
215                         logger.log(logRecord);
216                         continue;
217                     }
218 
219                     serviceSet.add(instance);
220                 }
221             } catch (final IOException e) {
222                 final LogRecord logRecord = new LogRecord(Level.SEVERE,
223                         "JRJSF_0012");
224                 logRecord.setParameters(new Object[]{url});
225                 logRecord.setThrown(e);
226                 logger.log(logRecord);
227             } finally {
228                 if (reader != null) {
229                     try {
230                         reader.close();
231                     } catch (final IOException e) { ; }
232                 }
233             }
234         }
235         return Collections.unmodifiableSet(serviceSet);
236     }
237 
238     /**
239      * Loads a service map from the classpath.
240      * <p>
241      * Services are looked up following convention
242      * <tt>META-INF/services/[serviceClassName]</tt>. When loading service maps,
243      * service configuration file must contain entries as follows: <tt>
244      * [key]:[implementation class]
245      * </tt>.
246      *
247      * @param <T>      Service type to be obtained.
248      * @param clazz    Service interface class object.
249      * @return the map of services.
250      */
251     @SuppressWarnings("unchecked")
252     public static <T> Map<String, T> map(final Class<T> clazz)
253             throws ServiceException {
254         final ClassLoader loader = Util.getClassLoader(null);
255         final Enumeration<URL> resources = getServiceResources(clazz, loader);
256 
257         final Map<String, T> serviceMap = new LinkedHashMap<String, T>();
258         while (resources.hasMoreElements()) {
259             final URL url = resources.nextElement();
260             BufferedReader reader = null;
261             try {
262                 String line;
263                 reader = new BufferedReader(new InputStreamReader(
264                         url.openStream()));
265                 while (null != (line = reader.readLine())) {
266                     // skip line comments
267                     if (line.startsWith("#")) {
268                         continue;
269                     }
270 
271                     Class<T> serviceClass;
272                     final String[] record = line.split(":");
273                     try {
274                         serviceClass = (Class<T>) loader.loadClass(record[1]);
275                     } catch (final ClassNotFoundException e) {
276                         final LogRecord logRecord = new LogRecord(Level.SEVERE,
277                                 "JRJSF_0011");
278                         logRecord.setParameters(new Object[]{record[1],
279                                     record[0]});
280                         logRecord.setThrown(e);
281                         logger.log(logRecord);
282                         continue;
283                     }
284 
285                     T instance;
286                     try {
287                         instance = serviceClass.newInstance();
288                     } catch (final Exception e) {
289                         final LogRecord logRecord = new LogRecord(Level.SEVERE,
290                                 "JRJSF_0015");
291                         logRecord.setParameters(new Object[]{record[1]});
292                         logRecord.setThrown(e);
293                         logger.log(logRecord);
294                         continue;
295                     }
296 
297                     serviceMap.put(("".equals(record[0]) ? null : record[0]),
298                             instance);
299                 }
300             } catch (final IOException e) {
301                 final LogRecord logRecord = new LogRecord(Level.SEVERE,
302                         "JRJSF_0012");
303                 logRecord.setParameters(new Object[]{url});
304                 logRecord.setThrown(e);
305                 logger.log(logRecord);
306                 continue;
307             } finally {
308                 if (reader != null) {
309                     try {
310                         reader.close();
311                     } catch (final IOException e) { ; }
312                 }
313             }
314         }
315         return Collections.unmodifiableMap(serviceMap);
316     }
317 
318     /** Utility method to obtain an enumeration of service config files. */
319     private static Enumeration<URL> getServiceResources(
320             final Class<?> serviceClass, final ClassLoader classLoader)
321             throws ServiceException {
322         Enumeration<URL> resources;
323         final String serviceConf = SERVICES_ROOT + serviceClass.getName();
324         try {
325             resources = classLoader.getResources(serviceConf);
326         } catch (final IOException e) {
327             throw new ServiceException(e);
328         }
329         return resources;
330     }
331 
332     /** Private constructor to prevent instantiation */
333     private Services() { }
334 
335 }