View Javadoc
1   /*
2    * Copyright (c) 2002-2025, 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.portal.service.datastore;
35  
36  import fr.paris.lutece.portal.business.datastore.DataEntity;
37  import fr.paris.lutece.portal.business.datastore.DataEntityHome;
38  import fr.paris.lutece.portal.service.template.FreeMarkerTemplateService;
39  import fr.paris.lutece.portal.service.util.AppLogService;
40  import fr.paris.lutece.portal.service.util.AppPathService;
41  import fr.paris.lutece.portal.service.util.NoDatabaseException;
42  import fr.paris.lutece.util.ReferenceList;
43  
44  import java.util.List;
45  import java.util.regex.Matcher;
46  import java.util.regex.Pattern;
47  
48  /**
49   * Datastore Service
50   */
51  public final class DatastoreService
52  {
53      public static final String VALUE_TRUE = "true";
54      public static final String VALUE_FALSE = "false";
55      private static final String DATASTORE_KEY = "dskey";
56      private static final Pattern PATTERN_DATASTORE_KEY = Pattern.compile( "#" + DATASTORE_KEY + "\\{(.*?)\\}" );
57      static final String VALUE_MISSING = "DS Value Missing";
58      private static DatastoreCacheService _cache;
59      private static boolean _bDatabase = true;
60  
61      /**
62       * Private constructor
63       */
64      private DatastoreService( )
65      {
66      }
67  
68      /**
69       * initialize the service
70       */
71      public static void init( )
72      {
73          FreeMarkerTemplateService.getInstance( ).setSharedVariable( DATASTORE_KEY, new DatastoreTemplateMethod( ) );
74      }
75  
76      /**
77       * Get entity
78       *
79       * @param strKey
80       *            The entity's key
81       * @param strDefault
82       *            The default value
83       * @return The value
84       */
85      public static String getDataValue( String strKey, String strDefault )
86      {
87          try
88          {
89              if ( _bDatabase )
90              {
91                  DataEntity entity = null;
92  
93                  if ( _cache != null )
94                  {
95                      entity = (DataEntity) _cache.getFromCache( _cache.getEntityCacheKey( strKey ) );
96                  }
97  
98                  if ( entity == null )
99                  {
100                     entity = DataEntityHome.findByPrimaryKey( strKey );
101 
102                     if ( entity == null )
103                     {
104                         return strDefault;
105                     }
106 
107                     if ( _cache != null )
108                     {
109                         _cache.putInCache( _cache.getEntityCacheKey( strKey ), entity );
110                     }
111                 }
112 
113                 return entity.getValue( );
114             }
115         }
116         catch( NoDatabaseException e )
117         {
118             disableDatastore( e );
119         }
120 
121         return strDefault;
122     }
123 
124     /**
125      * Get entity depending the current web app instance
126      *
127      * @param strKey
128      *            The entity's key
129      * @param strDefault
130      *            The default value
131      * @return The value
132      */
133     public static String getInstanceDataValue( String strKey, String strDefault )
134     {
135         String strInstanceKey = getInstanceKey( strKey );
136 
137         return getDataValue( strInstanceKey, strDefault );
138     }
139 
140     /**
141      * Set entity
142      *
143      * @param strKey
144      *            The entity's key
145      * @param strValue
146      *            The value
147      */
148     public static void setDataValue( String strKey, String strValue )
149     {
150         try
151         {
152             if ( _bDatabase )
153             {
154                 DataEntityl/business/datastore/DataEntity.html#DataEntity">DataEntity p = new DataEntity( strKey, strValue );
155                 DataEntity entity = DataEntityHome.findByPrimaryKey( strKey );
156 
157                 if ( entity != null )
158                 {
159                     DataEntityHome.update( p );
160 
161                     if ( _cache != null )
162                     {
163                         _cache.removeKey( _cache.getEntityCacheKey( strKey ) );
164                         _cache.removeCachedPrefixes( );
165                     }
166                 }
167                 else
168                 {
169                     DataEntityHome.create( p );
170                     if ( _cache != null )
171                     {
172                         _cache.removeCachedPrefixes( );
173                     }
174                 }
175             }
176         }
177         catch( NoDatabaseException e )
178         {
179             disableDatastore( e );
180         }
181     }
182 
183     /**
184      * Set entity depending the current web app instance
185      *
186      * @param strKey
187      *            The entity's key
188      * @param strValue
189      *            The value
190      */
191     public static void setInstanceDataValue( String strKey, String strValue )
192     {
193         String strInstanceKey = getInstanceKey( strKey );
194         setDataValue( strInstanceKey, strValue );
195     }
196 
197     /**
198      * Remove a give key
199      *
200      * @param strKey
201      *            The key
202      */
203     public static void removeData( String strKey )
204     {
205         try
206         {
207             if ( _bDatabase )
208             {
209                 DataEntityHome.remove( strKey );
210 
211                 if ( _cache != null )
212                 {
213                     _cache.removeKey( _cache.getEntityCacheKey( strKey ) );
214                     _cache.removeCachedPrefixes( );
215                 }
216             }
217         }
218         catch( NoDatabaseException e )
219         {
220             disableDatastore( e );
221         }
222     }
223 
224     /**
225      * Remove a give key depending the current web app instance
226      *
227      * @param strKey
228      *            The key
229      */
230     public static void removeInstanceData( String strKey )
231     {
232         String strInstanceKey = getInstanceKey( strKey );
233         removeData( strInstanceKey );
234     }
235 
236     /**
237      * Remove all data where keys begin with a given prefix
238      *
239      * @param strPrefix
240      *            The prefix
241      */
242     public static void removeDataByPrefix( String strPrefix )
243     {
244         try
245         {
246             if ( _bDatabase )
247             {
248                 List<DataEntity> listEntities = DataEntityHome.findAll( );
249 
250                 for ( DataEntity entity : listEntities )
251                 {
252                     if ( entity.getKey( ).startsWith( strPrefix ) )
253                     {
254                         removeData( entity.getKey( ) );
255                     }
256                 }
257             }
258         }
259         catch( NoDatabaseException e )
260         {
261             disableDatastore( e );
262         }
263     }
264 
265     /**
266      * Remove all data where keys begin with a given prefix depending the current web app instance
267      *
268      * @param strPrefix
269      *            The prefix
270      */
271     public static void removeInstanceDataByPrefix( String strPrefix )
272     {
273         String strInstancePrefix = getInstanceKey( strPrefix );
274         removeDataByPrefix( strInstancePrefix );
275     }
276 
277     /**
278      * Gets a list of key/value where keys are matching a given prefix
279      *
280      * @param strPrefix
281      *            The prefix
282      * @return The list
283      */
284     public static ReferenceList getDataByPrefix( String strPrefix )
285     {
286         if ( !_bDatabase )
287         {
288             return new ReferenceList( );
289         }
290         try
291         {
292             ReferenceList list = null;
293 
294             if ( _cache != null )
295             {
296                 list = ( ReferenceList ) _cache.getFromCache( _cache.getPrefixCacheKey( strPrefix ) );
297             }
298 
299             if ( list == null )
300             {
301                 list = new ReferenceList( );
302                 for ( DataEntity entity : DataEntityHome.findByPrefix( strPrefix ) )
303                 {
304                     list.addItem( entity.getKey( ), entity.getValue( ) );
305                 }
306 
307                 if ( _cache != null )
308                 {
309                     _cache.putInCache( _cache.getPrefixCacheKey( strPrefix ), list );
310                 }
311             }
312 
313             return list;
314         }
315         catch ( NoDatabaseException e )
316         {
317             disableDatastore( e );
318         }
319 
320         return new ReferenceList( );
321     }
322 
323     /**
324      * Gets a list of key/value where keys are matching a given prefix depending the current web app instance
325      *
326      * @param strPrefix
327      *            The prefix
328      * @return The list
329      */
330     public static ReferenceList getInstanceDataByPrefix( String strPrefix )
331     {
332         String strInstancePrefix = getInstanceKey( strPrefix );
333 
334         return getDataByPrefix( strInstancePrefix );
335     }
336 
337     /**
338      * This method replace keys by their value into a given content
339      *
340      * @param strSource
341      *            The string that contains datastore keys
342      * @return The string with keys replaced
343      */
344     public static String replaceKeys( String strSource )
345     {
346         String result = strSource;
347 
348         if ( strSource != null )
349         {
350             Matcher matcher = PATTERN_DATASTORE_KEY.matcher( strSource );
351 
352             if ( matcher.find( ) )
353             {
354                 StringBuffer sb = new StringBuffer( );
355 
356                 do
357                 {
358                     String strKey = matcher.group( 1 );
359                     String strValue = DatastoreService.getDataValue( strKey, VALUE_MISSING );
360 
361                     if ( VALUE_MISSING.equals( strValue ) )
362                     {
363                         AppLogService.error( "Datastore Key missing : {} - Please fix to avoid performance issues.", strKey );
364                     }
365 
366                     matcher.appendReplacement( sb, strValue );
367                 }
368                 while ( matcher.find( ) );
369 
370                 matcher.appendTail( sb );
371                 result = sb.toString( );
372             }
373         }
374 
375         return result;
376     }
377 
378     /**
379      * Check if a key is available in the datastore
380      *
381      * @param strKey
382      *            The key
383      * @return True if the key is found otherwise false
384      */
385     public static boolean existsKey( String strKey )
386     {
387         try
388         {
389             if ( _bDatabase )
390             {
391                 DataEntity entity = null;
392 
393                 if ( _cache != null )
394                 {
395                     entity = (DataEntity) _cache.getFromCache( _cache.getEntityCacheKey( strKey ) );
396                 }
397 
398                 if ( entity == null )
399                 {
400                     entity = DataEntityHome.findByPrimaryKey( strKey );
401 
402                     if ( entity == null )
403                     {
404                         return false;
405                     }
406                 }
407 
408                 return true;
409             }
410         }
411         catch( NoDatabaseException e )
412         {
413             disableDatastore( e );
414         }
415 
416         return false;
417     }
418 
419     /**
420      * Check if a key is available in the datastore depending the current web app instance
421      *
422      * @param strKey
423      *            The key
424      * @return True if the key is found otherwise false
425      */
426     public static boolean existsInstanceKey( String strKey )
427     {
428         String strInstanceKey = getInstanceKey( strKey );
429 
430         return existsKey( strInstanceKey );
431     }
432 
433     /**
434      * Start cache. NB : Cache can't be created at DataStore creation because CacheService uses DatastoreService (Circular reference)
435      */
436     public static void startCache( )
437     {
438         _cache = new DatastoreCacheService( );
439         AppLogService.info( "Datastore's cache started." );
440     }
441 
442     /**
443      * Disable the Datastore if a NoDatabaseException is catched
444      * 
445      * @param e
446      *            The NoDatabaseException
447      */
448     private static void disableDatastore( NoDatabaseException e )
449     {
450         _bDatabase = false;
451         AppLogService.error( "##### CRITICAL ERROR ##### : Datastore has been disabled due to a NoDatabaseException catched", e );
452     }
453 
454     /**
455      * Return a datastore key for the current webapp instance
456      * 
457      * @param strKey
458      *            The key
459      * @return The key for the current instance
460      */
461     private static String getInstanceKey( String strKey )
462     {
463         if ( !AppPathService.isDefaultWebappInstance( ) )
464         {
465             StringBuilder sbInstanceKey = new StringBuilder( );
466             sbInstanceKey.append( AppPathService.getWebappInstance( ) ).append( "." ).append( strKey );
467 
468             return sbInstanceKey.toString( );
469         }
470 
471         return strKey;
472     }
473 }