View Javadoc
1   /*
2    * Copyright (c) 2002-2017, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  /*
35  
36   */
37  package org.apache.pluto.portalImpl.services;
38  
39  import fr.paris.lutece.plugins.jsr168.pluto.LutecePlutoConstant;
40  import fr.paris.lutece.plugins.jsr168.pluto.exception.ContainerInitLutecePlutoException;
41  import fr.paris.lutece.plugins.jsr168.pluto.xml.ServiceXML;
42  import fr.paris.lutece.plugins.jsr168.pluto.xml.ServicesXML;
43  import fr.paris.lutece.plugins.jsr168.pluto.xml.XMLFactory;
44  import fr.paris.lutece.plugins.jsr168.pluto.xml.XMLFactoryException;
45  import fr.paris.lutece.portal.service.util.AppLogService;
46  import fr.paris.lutece.portal.service.util.AppPropertiesService;
47  
48  import org.apache.pluto.portalImpl.util.Properties;
49  import org.apache.pluto.util.StringUtils;
50  
51  import java.util.HashMap;
52  import java.util.Iterator;
53  import java.util.LinkedList;
54  import java.util.List;
55  import java.util.Map;
56  
57  import javax.servlet.ServletConfig;
58  import javax.servlet.ServletContext;
59  
60  
61  /**
62   * Manages the life-time of services registered during servlet startup.
63   * A service has to derive from {@link ContainerService} and implement the
64   * <CODE>init()</CODE> and <CODE>destroy()</CODE> methods as appropriate.
65   *
66   * <P>
67   * By registering the service and its implementation in the file
68   * <CODE>/config/services.properties</CODE>, the service will become
69   * available to the portal engine. The format of the file is simple:
70   *
71   * <PRE>
72   *   org.apache.pluto.portalImpl.services.log.LogService = org.apache.pluto.portalImpl.services.log.LogServicesImpl
73   * </PRE>
74   *
75   * Each entry represents one service. The left-hand side is the abstract
76   * service class, the right-hand side is the implementation of this service.
77   * The services are initialized in the order of appearance.
78   *
79   * <P>
80   * Each service can have its own configuration file, located in
81   * <CODE>/config/services</CODE>. It has to have the name of either
82   * implementation or abstract class of the service, without the
83   * leading package name. For example, the service manager looks
84   * for <CODE>LogServiceImpl.properties</CODE>. This allows a special
85   * implementation to provide different configuration than the
86   * general (abstract) service requires.
87   *
88   * <P>
89   * If present, one of the services configuration files is loaded
90   * and passed to the service as {@link org.apache.pluto.portalImpl.util.Properties}
91   * object. Not providing a service configuration file is okay too,
92   * in that case the properties are empty.
93   *
94   * @see ContainerService
95   */
96  public class ServiceManager
97  {
98      private static volatile boolean _bInitialized = false;
99      final private static Map _mapServices = new HashMap(  );
100     final private static List _lstServices = new LinkedList(  );
101 
102     /**
103      ** Initializes all services specified in <CODE>services.properties</CODE>.
104      ** By specifying a different implementation of the service the behaviour
105      ** of the portal can be modified.
106      **
107      ** @param   aConfig
108      **          the servlet configuration
109      **
110      ** @exception    Exception
111      **               if loading <CODE>services.properties</CODE>
112      **               or initializing any of its contained services fails
113      **/
114     public static void init( ServletConfig aConfig ) throws Exception
115     {
116         init( aConfig, AppPropertiesService.getProperty( LutecePlutoConstant.PROPERTY_FILE_SERVICES ) );
117     }
118 
119     /**
120      ** Initializes all services specified in <CODE>services.properties</CODE>.
121      ** By specifying a different implementation of the service the behaviour
122      ** of the portal can be modified.
123      **
124      ** @param   config
125      **          the servlet configuration
126      ** @param   serviceConfigFile
127      **          The location of <CODE>services.properties</CODE> (relative to classpath)
128      **
129      ** @exception    Exception
130      **               if loading <CODE>services.properties</CODE>
131      **               or initializing any of its contained services fails
132      **/
133     public static void init( ServletConfig config, String serviceConfigFile )
134         throws ContainerInitLutecePlutoException
135     {
136         if ( config == null )
137         {
138             throw new NullPointerException( "Parameter 'config' cannot be null." );
139         }
140 
141         final ServletContext context = config.getServletContext(  );
142 
143         if ( context == null )
144         {
145             throw new NullPointerException( "config.getServletContext() cannot sennd a null value." );
146         }
147 
148         // avoid duplicate initialization of services
149         if ( _bInitialized )
150         {
151             return;
152         }
153 
154         synchronized ( ServiceManager.class )
155         {
156             if ( !_bInitialized )
157             {
158                 _bInitialized = true;
159             }
160             else
161             {
162                 return;
163             }
164         }
165 
166         AppLogService.info( "Lutece/Pluto[ServiceManager] Loading services..." );
167 
168         final ServicesXML servicesXML;
169 
170         try
171         {
172             servicesXML = XMLFactory.loadServicesXML( context, serviceConfigFile );
173         }
174         catch ( XMLFactoryException e )
175         {
176             throw new ContainerInitLutecePlutoException( "ServiceManager: can't read services configuration (file '" +
177                 serviceConfigFile + "').", e );
178         }
179 
180         int numAll = 0;
181         int numSuccessful = 0;
182 
183         for ( Iterator it = servicesXML.getServices(  ).iterator(  ); it.hasNext(  ); )
184         {
185             final ServiceXML serviceXML = (ServiceXML) it.next(  );
186 
187             ++numAll;
188 
189             final String serviceBaseName = serviceXML.getServiceBase(  );
190 
191             // try to get hold of the base service
192             final Class serviceBase;
193 
194             try
195             {
196                 serviceBase = Class.forName( serviceBaseName );
197             }
198             catch ( ClassNotFoundException e )
199             {
200                 AppLogService.info( "Lutece/Pluto[ServiceManager] can't find base class '" + serviceBaseName + "'." );
201 
202                 continue;
203             }
204 
205             final String serviceImplName = serviceXML.getImplementation(  );
206             final Service service;
207 
208             try
209             {
210                 final Class serviceImpl = Class.forName( serviceImplName );
211                 service = (Service) serviceImpl.newInstance(  );
212             }
213             catch ( ClassNotFoundException e )
214             {
215                 AppLogService.info( "Lutece/Pluto[ServiceManager] can't find service implementation class '" +
216                     serviceImplName + "'." );
217 
218                 continue;
219             }
220             catch ( ClassCastException e )
221             {
222                 AppLogService.info( "Lutece/Pluto[ServiceManager] class '" + serviceImplName +
223                     "' isn't a service (base class must be '" + Service.class.getName(  ) + "')." );
224 
225                 continue;
226             }
227             catch ( IllegalAccessException e )
228             {
229                 AppLogService.info( 
230                     "Lutece/Pluto[ServiceManager] no public access to empty constructor in service class '" +
231                     serviceImplName + "'." );
232 
233                 continue;
234             }
235             catch ( InstantiationException e )
236             {
237                 AppLogService.info( "Lutece/Pluto[ServiceManager] can't instanciate service class '" + serviceImplName +
238                     "'." );
239 
240                 continue;
241             }
242 
243             try
244             {
245                 final Properties serviceProps = serviceXML.getProperties(  );
246 
247                 AppLogService.info( "Lutece/Pluto[ServiceManager] " + StringUtils.nameOf( serviceBase ) +
248                     " initializing..." );
249                 service.init( config, serviceProps );
250                 AppLogService.info( "Lutece/Pluto[ServiceManager] " + StringUtils.nameOf( serviceBase ) + " done." );
251             }
252             catch ( Exception e )
253             {
254                 AppLogService.error( "Lutece/Pluto[ServiceManager] " + StringUtils.nameOf( serviceBase ) +
255                     " exception occured (" + e.getMessage(  ) + ").", e );
256 
257                 continue;
258             }
259 
260             _mapServices.put( serviceBase, service );
261 
262             // build up list in reverse order for later destruction
263             _lstServices.add( 0, service );
264 
265             ++numSuccessful;
266         }
267 
268         if ( numSuccessful != numAll )
269         {
270             AppLogService.info( "Lutece/Pluto[ServiceManager] Services initialized (" + numSuccessful + "/" + numAll +
271                 " successful)." );
272             throw new ContainerInitLutecePlutoException( "ServiceManager: Services initialized (" + numSuccessful +
273                 "/" + numAll + " successful)." );
274         }
275         else
276         {
277             AppLogService.info( "Lutece/Pluto[ServiceManager] Services initialized (" + numSuccessful + "/" + numAll +
278                 " successful)." );
279         }
280     }
281 
282     /**
283      * Calls post init for all services
284      *
285      * @param   aConfig
286      *         the servlet configuration
287      **/
288     public static void postInit( ServletConfig aConfig )
289         throws ContainerInitLutecePlutoException
290     {
291         // avoid duplicate destruction of services
292         if ( !_bInitialized )
293         {
294             return;
295         }
296 
297         /*
298         synchronized (ServiceManager.class)
299         {
300             if (_bInitialized)
301             {
302                  _bInitialized = false;
303             }
304             else
305             {
306                 return;
307             }
308         }
309         */
310 
311         // post init all services
312         int numSuccessful = 0;
313         final int numAll = _lstServices.size(  );
314 
315         for ( Iterator iterator = _lstServices.iterator(  ); iterator.hasNext(  ); )
316         {
317             final Service service = (Service) iterator.next(  );
318 
319             try
320             {
321                 service.postInit( aConfig );
322                 ++numSuccessful;
323             }
324             catch ( Exception e )
325             {
326                 AppLogService.error( "Lutece/Pluto[ServiceManager] exception occured for '" +
327                     service.getClass(  ).getName(  ) + "' postInit phase (" + e.getMessage(  ) + ").", e );
328             }
329         }
330 
331         if ( numSuccessful != numAll )
332         {
333             AppLogService.info( "Lutece/Pluto[ServiceManager] Services post-initialized (" + numSuccessful + "/" +
334                 numAll + " successful)." );
335             throw new ContainerInitLutecePlutoException( "ServiceManager: Services post-initialized (" + numSuccessful +
336                 "/" + numAll + " successful)." );
337         }
338         else
339         {
340             AppLogService.info( "Lutece/Pluto[ServiceManager] Services post-initialized (" + numSuccessful + "/" +
341                 numAll + " successful)." );
342         }
343     }
344 
345     /**
346      ** Destroys all services.
347      **
348      ** @param   aConfig
349      **          the servlet configuration
350      **/
351     public static void destroy( ServletConfig aConfig )
352     {
353         // avoid duplicate destruction of services
354         if ( !_bInitialized )
355         {
356             return;
357         }
358 
359         synchronized ( ServiceManager.class )
360         {
361             if ( _bInitialized )
362             {
363                 _bInitialized = false;
364             }
365             else
366             {
367                 return;
368             }
369         }
370 
371         ServletContext context = null;
372 
373         if ( aConfig != null )
374         {
375             context = aConfig.getServletContext(  );
376         }
377 
378         // destroy the services in reverse order
379         for ( Iterator iterator = _lstServices.iterator(  ); iterator.hasNext(  ); )
380         {
381             Service service = (Service) iterator.next(  );
382 
383             try
384             {
385                 service.destroy( aConfig );
386             }
387             catch ( Exception e )
388             {
389                 AppLogService.error( "Lutece/Pluto[ServiceManager] Service '" + service.getClass(  ).getName(  ) +
390                     "' couldn't be destroyed.", e );
391             }
392         }
393 
394         _lstServices.clear(  );
395         _mapServices.clear(  );
396     }
397 
398     /**
399      ** Returns the service implementation for the given service class, or
400      ** <CODE>null</CODE> if no such service is registered.
401      **
402      ** @param   aClass
403      **          the service class
404      **
405      ** @return   the service implementation
406      **/
407     public static Service getService( Class aClass )
408     {
409         // at this state the services map is read-only,
410         // therefore we can go without synchronization
411         return ( (Service) _mapServices.get( aClass ) );
412     }
413 }