View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of 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  package fr.paris.lutece.util.pool;
35  
36  import java.io.InputStream;
37  import java.sql.Connection;
38  import java.util.Collection;
39  import java.util.Enumeration;
40  import java.util.HashMap;
41  import java.util.Hashtable;
42  import java.util.Map;
43  import java.util.Map.Entry;
44  import java.util.Properties;
45  
46  import javax.sql.DataSource;
47  
48  import org.apache.logging.log4j.LogManager;
49  import org.apache.logging.log4j.Logger;
50  
51  import fr.paris.lutece.portal.service.init.LuteceInitException;
52  import fr.paris.lutece.portal.service.util.AppPropertiesService;
53  import fr.paris.lutece.util.ReferenceList;
54  import fr.paris.lutece.util.pool.service.ConnectionService;
55  import fr.paris.lutece.util.pool.service.LuteceConnectionService;
56  
57  /**
58   * This class can manages a set of database connections pools. It's implemented as a singleton. It provides methods to get or release a connection from a given
59   * pool.
60   */
61  public final class PoolManager
62  {
63      private static final String LOGGER_NAME = "lutece.pool";
64      private static PoolManager _instance;
65      private Logger _logger;
66      private Map<String, ConnectionService> _pools = new HashMap<>( );
67  
68      /**
69       * Creates a new PoolManager object.
70       *
71       *
72       * @param isDbProperties
73       *            A properties file containing pools parameters.
74       * @throws LuteceInitException
75       *             If any error occured
76       */
77      private PoolManager( InputStream isDbProperties ) throws LuteceInitException
78      {
79          init( isDbProperties );
80      }
81  
82      /**
83       * This method returns the unique instance of the PoolManager.
84       *
85       * @return The unique instance of Poolmanager.
86       * @param isDbProperties
87       *            An InputStream on a db.properties File to initialiaze the pool if it's not already created.
88       * @throws LuteceInitException
89       *             If any error occured
90       */
91      public static synchronized PoolManager getInstance( InputStream isDbProperties ) throws LuteceInitException
92      {
93          if ( _instance == null )
94          {
95              _instance = new PoolManager( isDbProperties );
96          }
97  
98          return _instance;
99      }
100 
101     /**
102      * Initializes pools with parameters defined in a db.properties File.
103      *
104      * @param is
105      *            An InputStream on a db.properties File.
106      * @throws LuteceInitException
107      *             If any error occured
108      */
109     private void init( InputStream is ) throws LuteceInitException
110     {
111         _logger = LogManager.getLogger( LOGGER_NAME );
112 
113         Properties dbProps = new Properties( );
114 
115         try
116         {
117             dbProps.load( is );
118         }
119         catch( Exception e )
120         {
121             throw new LuteceInitException( "Can't read the properties file. Make sure db.properties is in the CLASSPATH", e );
122         }
123         
124         overrideProperties(dbProps);       
125         createPools( dbProps );
126     }
127 
128     /**
129      * Override properties with the api config
130      * @param dbProps the database properties 
131      */
132 	private void overrideProperties(Properties dbProps) {
133 		Enumeration propertiesName=  dbProps.propertyNames();
134         while (propertiesName.hasMoreElements()) {
135         	 String key = (String) propertiesName.nextElement();
136         	 String value= AppPropertiesService.getProperty(key);
137         	 if( value != null ) {
138         		 dbProps.put(key, value);
139         	 }
140         }
141 	}
142 
143     /**
144      * Creates all pools defined in a properties file.
145      *
146      * @param props
147      *            A properties file containing pools parameters.
148      * @throws LuteceInitException
149      *             If any error occured
150      */
151     private void createPools( Properties props ) throws LuteceInitException
152     {
153         Enumeration propNames = props.propertyNames( );
154         String strPoolName = "";
155 
156         Map<String, Hashtable<String, String>> htPools = new HashMap<>( );
157 
158         while ( propNames.hasMoreElements( ) )
159         {
160             String name = (String) propNames.nextElement( );
161 
162             try
163             {
164                 strPoolName = name.substring( 0, name.lastIndexOf( '.' ) );
165 
166                 // tests if the pool has yet somme of its porperties stored in the hatsable
167                 Hashtable<String, String> htParamsPool = htPools.computeIfAbsent( strPoolName, s -> new Hashtable<>( ) );
168                 htParamsPool.put( name, props.getProperty( name ) );
169                 htPools.put( strPoolName, htParamsPool );
170 
171                 _logger.debug( "property {}", name );
172                 _logger.debug( "pool name {}", strPoolName );
173             }
174             catch( Exception e )
175             {
176                 throw new LuteceInitException( "Invalid initialization of the pools. Problem encoutered with the property :  " + name, e );
177             }
178         }
179 
180         for ( Entry<String, Hashtable<String, String>> entry : htPools.entrySet( ) )
181         {
182             String key = entry.getKey( );
183             try
184             {
185                 Hashtable<String, String> htParamsPool = htPools.get( key );
186                 ConnectionService cs = getConnectionService( htParamsPool, key );
187 
188                 if ( cs != null )
189                 {
190                     cs.setPoolName( key );
191                     cs.setLogger( _logger );
192                     cs.init( htParamsPool );
193                     _pools.put( key, cs );
194                 }
195             }
196             catch( Exception e )
197             {
198                 throw new LuteceInitException( "Exception when getting the pool '" + key + "'. Please check your '/WEB-INF/conf/db.properties' file.", e );
199             }
200         }
201     }
202 
203     private ConnectionService getConnectionService( Map<String, String> htParamsPool, String key ) throws LuteceInitException
204     {
205         ConnectionService cs = null;
206 
207         try
208         {
209             String strConnectionService = htParamsPool.get( key + ".poolservice" );
210 
211             cs = (ConnectionService) Class.forName( strConnectionService ).newInstance( );
212         }
213         catch( NullPointerException nullEx )
214         {
215             cs = new LuteceConnectionService( );
216         }
217         catch( Exception e )
218         {
219             throw new LuteceInitException( "Exception when getting the property poolservice", e );
220         }
221         return cs;
222     }
223 
224     /**
225      * Returns an available connection from the pool.
226      *
227      * @param strPoolName
228      *            The pool name
229      * @return A connection
230      */
231     public Connection getConnection( String strPoolName )
232     {
233         Connection conn = null;
234         ConnectionService pool = _pools.get( strPoolName );
235 
236         if ( pool != null )
237         {
238             conn = pool.getConnection( );
239         }
240 
241         return conn;
242     }
243 
244     /**
245      * Returns a connection to pool.
246      *
247      * @param strPoolName
248      *            Pool's name
249      * @param con
250      *            A released connection
251      */
252     public void freeConnection( String strPoolName, Connection con )
253     {
254         ConnectionService cs = _pools.get( strPoolName );
255 
256         if ( cs != null )
257         {
258             cs.freeConnection( con );
259         }
260     }
261 
262     /**
263      * Releases all connections from all the pool.
264      */
265     public synchronized void release( )
266     {
267         for ( ConnectionService pool : _pools.values( ) )
268         {
269             pool.release( );
270         }
271     }
272 
273     /**
274      * Returns all pools available
275      * 
276      * @return The list of available pools
277      */
278     public Collection<ConnectionService> getPools( )
279     {
280         return _pools.values( );
281     }
282 
283     /**
284      * Returns pool's infos (currently opened connections)
285      * 
286      * @return The pool's infos
287      */
288     public ReferenceList getPoolsInfos( )
289     {
290         ReferenceListReferenceList">ReferenceList listPoolsInfos = new ReferenceList( );
291         Collection<ConnectionService> listPools = getPools( );
292 
293         for ( ConnectionService cs : listPools )
294         {
295             String strCurrentConnections = ( cs.getCurrentConnections( ) == ConnectionService.INFO_NOT_AVAILABLE ) ? "-" : ( "" + cs.getCurrentConnections( ) );
296             String strMaxConnections = ( cs.getMaxConnections( ) == ConnectionService.INFO_NOT_AVAILABLE ) ? "-" : ( "" + cs.getMaxConnections( ) );
297             listPoolsInfos.addItem( cs.getPoolName( ), strCurrentConnections + " / " + strMaxConnections + " (" + cs.getPoolProvider( ) + ")" );
298         }
299 
300         return listPoolsInfos;
301     }
302 
303     /**
304      * Returns the datasource for a given pool name
305      * 
306      * @param strPoolName
307      *            The Pool name
308      * @return A data source object
309      */
310     public DataSource getDataSource( String strPoolName )
311     {
312         return _pools.get( strPoolName ).getDataSource( );
313     }
314 }