View Javadoc

1   /*
2    * Copyright 2003,2004 The Apache Software Foundation.
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  
18   */
19  
20  package org.apache.pluto.portalImpl.services;
21  
22  import java.io.*;
23  import java.util.*;
24  
25  import javax.servlet.ServletConfig;
26  import javax.servlet.ServletContext;
27  
28  import org.apache.pluto.util.StringUtils;
29  import org.apache.pluto.portalImpl.util.Properties;
30  
31  /***
32   * Manages the life-time of services registered during servlet startup.
33   * A service has to derive from {@link org.apache.pluto.services.ContainerService} and implement the
34   * <CODE>init()</CODE> and <CODE>destroy()</CODE> methods as appropriate.
35   * 
36   * <P>
37   * By registering the service and its implementation in the file
38   * <CODE>/config/services.properties</CODE>, the service will become
39   * available to the portal engine. The format of the file is simple:
40   * 
41   * <PRE>
42   *   org.apache.pluto.portalImpl.services.log.Logger = org.apache.pluto.portalImpl.services.log.LogServicesImpl
43   * </PRE>
44   * 
45   * Each entry represents one service. The left-hand side is the abstract
46   * service class, the right-hand side is the implementation of this service.
47   * The services are initialized in the order of appearance.
48   * 
49   * <P>
50   * Each service can have its own configuration file, located in
51   * <CODE>/config/services</CODE>. It has to have the name of either
52   * implementation or abstract class of the service, without the
53   * leading package name. For example, the service manager looks
54   * for <CODE>LoggerImpl
55  .properties</CODE>. This allows a special
56   * implementation to provide different configuration than the
57   * general (abstract) service requires.
58   * 
59   * <P>
60   * If present, one of the services configuration files is loaded
61   * and passed to the service as {@link org.apache.pluto.portalImpl.util.Properties}
62   * object. Not providing a service configuration file is okay too,
63   * in that case the properties are empty.
64   * 
65   * @see org.apache.pluto.services.ContainerService
66   */
67  public class ServiceManager
68  {
69  
70      private final static String SERVICES_CONFIG_FILE = "/WEB-INF/config/services.properties";
71      private final static String SERVICES_CONFIG_DIR  = "/WEB-INF/config/services/";
72  
73      /***
74       ** Initializes all services specified in <CODE>services.properties</CODE>.
75       ** By specifying a different implementation of the service the behaviour
76       ** of the portal can be modified.
77       **
78       ** @param   aConfig
79       **          the servlet configuration
80       **
81       ** @exception    Exception
82       **               if loading <CODE>services.properties</CODE>
83       **               or initializing any of its contained services fails
84       **/
85  
86      public static void init (ServletConfig aConfig) throws Exception
87      {
88          init (aConfig, SERVICES_CONFIG_FILE, SERVICES_CONFIG_DIR);
89      }
90  
91      /***
92       ** Initializes all services specified in <CODE>services.properties</CODE>.
93       ** By specifying a different implementation of the service the behaviour
94       ** of the portal can be modified.
95       **
96       ** @param   aConfig
97       **          the servlet configuration
98       ** @param   aServiceConfigFile
99       **          The location of <CODE>services.properties</CODE> (relative to classpath)
100      ** @param   aServiceConfigDir
101      **          The direcory with the services' properties files (relative to classpath)
102      **
103      ** @exception    Exception
104      **               if loading <CODE>services.properties</CODE>
105      **               or initializing any of its contained services fails
106      **/
107 
108     public static void init (ServletConfig aConfig,
109                              String aServiceConfigFile,
110                              String aServiceConfigDir) throws Exception
111     {
112         // avoid duplicate initialization of services
113 
114         if (! cInitialized)
115         {
116             synchronized (ServiceManager.class)
117             {
118                 if (! cInitialized)
119                 {
120                     cInitialized = true;
121                 }
122                 else
123                 {
124                     return;
125                 }
126             }
127         }
128         else
129         {
130             return;
131         }
132 
133         ServletContext context = null;
134 
135         if (aConfig != null)
136             context = aConfig.getServletContext ();
137 
138         if (context != null)
139             context.log ("ServiceManager: Loading services...");
140 
141         Properties props = new Properties ();
142 
143         try
144         {
145             props.load (context.getResourceAsStream (aServiceConfigFile));
146         }
147         catch (IOException exc)
148         {
149             if (context != null)
150                 context.log ("ServiceManager: File \"" + aServiceConfigFile + "\" cannot be found or read.");
151             throw new Exception("ServiceManager: File \"" + aServiceConfigFile + "\" cannot be found or read.");
152         }
153 
154         int numAll = 0;
155         int numSuccessful = 0;
156 
157         for (Iterator iter = props.names (); iter.hasNext (); )
158         {
159             String serviceBaseName = (String) iter.next ();
160 
161             numAll++;
162 
163             // ty to get hold of the base service
164 
165             Class serviceBase;
166 
167             try
168             {
169                 serviceBase = Class.forName (serviceBaseName);
170             }
171             catch (ClassNotFoundException exc)
172             {
173                 if (context != null)
174                     context.log ("ServiceManager: A service with name " + serviceBaseName + " cannot be found.");
175 
176                 continue;
177             }
178 
179             String serviceImplName = props.getString (serviceBaseName);
180 
181             Class serviceImpl = null;
182 
183             Service service = null;
184 
185             try
186             {
187                 serviceImpl = Class.forName (serviceImplName);
188 
189                 service = (Service) serviceImpl.newInstance ();
190 
191                 Properties serviceProps = new Properties ();
192 
193                 try
194                 {
195                     InputStream is = null;
196 
197                     is = context.getResourceAsStream (aServiceConfigDir + StringUtils.nameOf (serviceImpl) + ".properties");
198 
199                     if (is == null)
200                         is = context.getResourceAsStream (aServiceConfigDir + StringUtils.nameOf (serviceBase) + ".properties");
201 
202                     if (is != null)
203                         serviceProps.load (is);
204                 }
205                 catch (IOException exc)
206                 {
207                     // ignore -- we go without properties then
208                 }
209 
210                 if (context != null)
211                     context.log (StringUtils.nameOf (serviceBase) + " initializing...");
212 
213                 service.init (aConfig, serviceProps);
214 
215                 if (context != null)
216                     context.log (StringUtils.nameOf (serviceBase) + " done.");
217             }
218             catch (ClassNotFoundException exc)
219             {
220                 if (context != null)
221                     context.log ("ServiceManager: A service implementation with name " + serviceImplName + " cannot be found.", exc);
222 
223                 continue;
224             }
225             catch (ClassCastException exc)
226             {
227                 if (context != null)
228                     context.log ("ServiceManager: Service implementation " + serviceImplName + " is not a service of the required type.", exc);
229 
230                 continue;
231             }
232             catch (InstantiationException exc)
233             {
234                 if (context != null)
235                     context.log ("ServiceManager: Service implementation " + serviceImplName + " cannot be instantiated.", exc);
236 
237                 continue;
238             }
239             catch (Exception exc)
240             {
241                 if (context != null)
242                     context.log ("ServiceManager: An unidentified error occurred", exc);
243 
244                 service = null;
245             }
246 
247             if (service != null)
248             {
249                 cServicesMap.put (serviceBase, service);
250 
251                 // build up list in reverse order for later destruction
252 
253                 cServicesList.add (0, service);
254 
255                 numSuccessful++;
256             }
257         }
258 
259         if (context != null)
260             context.log ("ServiceManager: Services initialized (" + numSuccessful + "/" + numAll + " successful).");
261         if (numSuccessful!=numAll)
262         {
263             throw new Exception("ServiceManager: Services initialized (" + numSuccessful + "/" + numAll + " successful).");
264         }
265     }
266 
267     /***
268      * Calls post init for all services
269      * 
270      * @param   aConfig
271      *         the servlet configuration
272      **/
273      public static void postInit(ServletConfig aConfig) {
274           // avoid duplicate destruction of services
275 
276         if (cInitialized)
277         {
278             synchronized (ServiceManager.class)
279             {
280                 if (cInitialized)
281                 {
282                     cInitialized = false;
283                 }
284                 else
285                 {
286                     return;
287                 }
288             }
289         }
290         else
291         {
292             return;
293         }
294 
295         ServletContext context = null;
296 
297         if (aConfig != null)
298             context = aConfig.getServletContext ();
299 
300         // post init all services
301         for (Iterator iterator = cServicesList.iterator (); iterator.hasNext (); )
302        {
303            Service service = (Service) iterator.next ();
304 
305            try
306            {
307                service.postInit(aConfig);
308            }
309            catch (Exception exc)
310            {
311                if (context != null)
312                    context.log ("ServiceManager: Service couldn't be started (postInit) after init..", exc);
313            }
314        }
315 
316      }
317 
318     /***
319      ** Destroys all services.
320      **
321      ** @param   aConfig
322      **          the servlet configuration
323      **/
324 
325     public static void destroy (ServletConfig aConfig)
326     {
327         // avoid duplicate destruction of services
328 
329         if (cInitialized)
330         {
331             synchronized (ServiceManager.class)
332             {
333                 if (cInitialized)
334                 {
335                     cInitialized = false;
336                 }
337                 else
338                 {
339                     return;
340                 }
341             }
342         }
343         else
344         {
345             return;
346         }
347 
348         ServletContext context = null;
349 
350         if (aConfig != null)
351             context = aConfig.getServletContext ();
352 
353         // destroy the services in reverse order
354 
355         for (Iterator iterator = cServicesList.iterator (); iterator.hasNext (); )
356         {
357             Service service = (Service) iterator.next ();
358 
359             try
360             {
361                 service.destroy (aConfig);
362             }
363             catch (Exception exc)
364             {
365                 if (context != null)
366                     context.log ("ServiceManager: Service couldn't be destroyed.", exc);
367             }
368         }
369 
370         cServicesList.clear();
371         cServicesMap.clear();
372 
373     }
374 
375     /***
376      ** Returns the service implementation for the given service class, or
377      ** <CODE>null</CODE> if no such service is registered.
378      **
379      ** @param   aClass
380      **          the service class
381      **
382      ** @return   the service implementation
383      **/
384 
385     public static Service getService (Class aClass)
386     {
387         // at this state the services map is read-only,
388         // therefore we can go without synchronization
389 
390         return ((Service) cServicesMap.get (aClass));
391     }
392 
393     // --- PRIVATE MEMBERS --- //
394 
395     private static volatile boolean cInitialized = false;
396 
397     private static Map  cServicesMap  = new HashMap ();
398     private static List cServicesList = new LinkedList ();
399 }
400 
401 
402