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