1   /*
2    * Copyright 2003 - 2013 The eFaps Team
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   * Revision:        $Rev$
17   * Last Changed:    $Date$
18   * Last Changed By: $Author$
19   */
20  
21  package org.efaps.admin.common;
22  
23  import java.io.Serializable;
24  import java.sql.PreparedStatement;
25  import java.sql.ResultSet;
26  import java.sql.SQLException;
27  import java.util.Formatter;
28  import java.util.Locale;
29  import java.util.UUID;
30  
31  import org.efaps.db.Context;
32  import org.efaps.db.transaction.ConnectionResource;
33  import org.efaps.db.wrapper.SQLPart;
34  import org.efaps.db.wrapper.SQLSelect;
35  import org.efaps.util.EFapsException;
36  import org.efaps.util.cache.CacheLogListener;
37  import org.efaps.util.cache.CacheObjectInterface;
38  import org.efaps.util.cache.CacheReloadException;
39  import org.efaps.util.cache.InfinispanCache;
40  import org.infinispan.Cache;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  /**
45   * TODO comment!
46   *
47   * @author The eFaps Team
48   * @version $Id$
49   */
50  public final class NumberGenerator
51      implements CacheObjectInterface, Serializable
52  {
53  
54      /**
55       * Needed for serialization.
56       */
57      private static final long serialVersionUID = 1L;
58  
59      /**
60       * This is the SQL select statement to select a role from the database by
61       * ID.
62       */
63      private static final String SQL_ID = new SQLSelect()
64                      .column(0, "ID")
65                      .column(1, "NAME")
66                      .column(1, "UUID")
67                      .column(0, "FORMAT")
68                      .from("T_CMNUMGEN", 0)
69                      .leftJoin("T_CMABSTRACT", 1, "ID", 0, "ID")
70                      .addPart(SQLPart.WHERE).addColumnPart(0, "ID").addPart(SQLPart.EQUAL).addValuePart("?").toString();
71  
72      /**
73       * This is the SQL select statement to select a role from the database by
74       * Name.
75       */
76      private static final String SQL_NAME = new SQLSelect()
77                      .column(0, "ID")
78                      .column(1, "NAME")
79                      .column(1, "UUID")
80                      .column(0, "FORMAT")
81                      .from("T_CMNUMGEN", 0)
82                      .leftJoin("T_CMABSTRACT", 1, "ID", 0, "ID")
83                      .addPart(SQLPart.WHERE).addColumnPart(1, "NAME").addPart(SQLPart.EQUAL).addValuePart("?")
84                      .toString();
85  
86      /**
87       * This is the SQL select statement to select a role from the database by
88       * UUID.
89       */
90      private static final String SQL_UUID = new SQLSelect()
91                      .column(0, "ID")
92                      .column(1, "NAME")
93                      .column(1, "UUID")
94                      .column(0, "FORMAT")
95                      .from("T_CMNUMGEN", 0)
96                      .leftJoin("T_CMABSTRACT", 1, "ID", 0, "ID")
97                      .addPart(SQLPart.WHERE).addColumnPart(1, "UUID").addPart(SQLPart.EQUAL).addValuePart("?")
98                      .toString();
99  
100     /**
101      * Name of the Cache by UUID.
102      */
103     private static final String UUIDCACHE = NumberGenerator.class.getName() + ".UUID";
104 
105     /**
106      * Name of the Cache by ID.
107      */
108     private static final String IDCACHE = NumberGenerator.class.getName() + ".ID";
109 
110     /**
111      * Name of the Cache by Name.
112      */
113     private static final String NAMECACHE = NumberGenerator.class.getName() + ".Name";
114 
115     /**
116      * Logging instance used in this class.
117      */
118     private static final Logger LOG = LoggerFactory.getLogger(NumberGenerator.class);
119 
120     /**
121      * Prefix for the name used in the database.
122      */
123     private static final String NAME_PREFIX = "numgen_";
124 
125     /**
126      * Suffix for the name used in the database.
127      */
128     private static final String NAME_SUFFIX = "_seq";
129 
130     /**
131      * Id of this NumberGenerator.
132      */
133     private final long id;
134 
135     /**
136      * Name of this NumberGenerator.
137      */
138     private final String name;
139 
140     /**
141      * UUID of this NumberGenerator.
142      */
143     private final UUID uuid;
144 
145     /**
146      * Format for this NumberGenerator.
147      */
148     private final String format;
149 
150     /**
151      * @param _id Id of this NumberGenerator.
152      * @param _name Name of this NumberGenerator.
153      * @param _uuid UUID of this NumberGenerator.
154      * @param _format format of this NumberGenerator.
155      */
156     private NumberGenerator(final long _id,
157                             final String _name,
158                             final String _uuid,
159                             final String _format)
160     {
161         this.id = _id;
162         this.name = _name;
163         this.uuid = UUID.fromString(_uuid);
164         this.format = _format;
165     }
166 
167     /**
168      * Get the name used for this sequence in the database.
169      *
170      * @return name of this sequence in the database;
171      */
172     public String getDBName()
173     {
174         return NumberGenerator.NAME_PREFIX + this.id + NumberGenerator.NAME_SUFFIX;
175     }
176 
177     /**
178      * Method to get the next value for this sequence. To get the long value use
179      * {@link #getNextValAsLong()}
180      *
181      * @return next value for this sequence
182      * @throws EFapsException on error
183      */
184     public String getNextVal()
185         throws EFapsException
186     {
187         return getNextVal(new Object[0]);
188     }
189 
190     /**
191      * Method to get the next value for this sequence. Including additional
192      * format information. To get the long value use {@link #getNextValAsLong()}
193      *
194      * @param _args arguments for the formatter
195      * @return next value for this sequence
196      * @throws EFapsException on error
197      */
198     public String getNextVal(final Object... _args)
199         throws EFapsException
200     {
201         final Object[] args = new Object[_args.length + 1];
202         try {
203             final long val = Context.getDbType().nextSequence(Context.getThreadContext().getConnection(), getDBName());
204             args[0] = val;
205         } catch (final SQLException e) {
206             throw new EFapsException(NumberGenerator.class, " getNextVal()", e);
207         }
208         for (int i = 0; i < _args.length; i++) {
209             args[i + 1] = _args[i];
210         }
211         final Locale local = Context.getThreadContext().getLocale();
212         final Formatter formatter = new Formatter(local);
213         formatter.format(this.format, args);
214         final String ret = formatter.toString();
215         formatter.close();
216         return ret;
217     }
218 
219     /**
220      * Method to get the next long value for this sequence. To get the formated
221      * value use {@link #getNextVal()}
222      *
223      * @return next value for this sequence
224      * @throws EFapsException on error
225      */
226     public Long getNextValAsLong()
227         throws EFapsException
228     {
229         long ret = 0;
230         try {
231             ret = Context.getDbType().nextSequence(Context.getThreadContext().getConnection(), getDBName());
232         } catch (final SQLException e) {
233             throw new EFapsException(NumberGenerator.class, " getNextValAsLong()", e);
234         }
235         return ret;
236     }
237 
238     /**
239      * Set the value for the numberGenerator. The next call of
240      * {@link #getNextVal()} or {@link #getNextVal()} normally will return the
241      * value + 1.
242      *
243      * @param _value value for the sequence
244      * @throws EFapsException on error
245      */
246     public void setVal(final String _value)
247         throws EFapsException
248     {
249         try {
250             Context.getDbType().setSequence(Context.getThreadContext().getConnection(), getDBName(),
251                             Long.valueOf(_value));
252         } catch (final SQLException e) {
253             throw new EFapsException(NumberGenerator.class, "setVal()", e);
254         }
255     }
256 
257     /**
258      * {@inheritDoc}
259      */
260     @Override
261     public long getId()
262     {
263         return this.id;
264     }
265 
266     /**
267      * {@inheritDoc}
268      */
269     @Override
270     public String getName()
271     {
272         return this.name;
273     }
274 
275     /**
276      * {@inheritDoc}
277      */
278     @Override
279     public UUID getUUID()
280     {
281         return this.uuid;
282     }
283 
284     /**
285      * Method to initialize the Cache of this CacheObjectInterface.
286      *
287      * @throws CacheReloadException on error
288      */
289     public static void initialize()
290         throws CacheReloadException
291     {
292         NumberGenerator.initialize(NumberGenerator.class);
293     }
294 
295     /**
296      * Method to initialize the Cache of this CacheObjectInterface.
297      *
298      * @param _class class that called the method
299      * @throws CacheReloadException on error
300      */
301     public static void initialize(final Class<?> _class)
302         throws CacheReloadException
303     {
304         if (InfinispanCache.get().exists(NumberGenerator.UUIDCACHE)) {
305             InfinispanCache.get().<UUID, NumberGenerator>getCache(NumberGenerator.UUIDCACHE).clear();
306         } else {
307             InfinispanCache.get().<UUID, NumberGenerator>getCache(NumberGenerator.UUIDCACHE)
308                             .addListener(new CacheLogListener(NumberGenerator.LOG));
309         }
310         if (InfinispanCache.get().exists(NumberGenerator.IDCACHE)) {
311             InfinispanCache.get().<Long, NumberGenerator>getCache(NumberGenerator.IDCACHE).clear();
312         } else {
313             InfinispanCache.get().<Long, NumberGenerator>getCache(NumberGenerator.IDCACHE)
314                             .addListener(new CacheLogListener(NumberGenerator.LOG));
315         }
316         if (InfinispanCache.get().exists(NumberGenerator.NAMECACHE)) {
317             InfinispanCache.get().<String, NumberGenerator>getCache(NumberGenerator.NAMECACHE).clear();
318         } else {
319             InfinispanCache.get().<String, NumberGenerator>getCache(NumberGenerator.NAMECACHE)
320                             .addListener(new CacheLogListener(NumberGenerator.LOG));
321         }
322     }
323 
324     /**
325      * Returns for given parameter <i>_id</i> the instance of class Type. .
326      *
327      * @param _id id of the type to get
328      * @return instance of class {@link Type}
329      * @throws CacheReloadException on error
330      */
331     public static NumberGenerator get(final long _id)
332         throws CacheReloadException
333     {
334         final Cache<Long, NumberGenerator> cache = InfinispanCache.get().<Long, NumberGenerator>getCache(
335                         NumberGenerator.IDCACHE);
336         if (!cache.containsKey(_id)) {
337             NumberGenerator.getNumberGeneratorFromDB(NumberGenerator.SQL_ID, _id);
338         }
339         return cache.get(_id);
340     }
341 
342     /**
343      * Returns for given parameter <i>_name</i> the instance of class
344      * {@link Type}.
345      *
346      * @param _name name of the type to get
347      * @return instance of class {@link Type}
348      * @throws CacheReloadException on error
349      */
350     public static NumberGenerator get(final String _name)
351         throws CacheReloadException
352     {
353         final Cache<Long, NumberGenerator> cache = InfinispanCache.get().<Long, NumberGenerator>getCache(
354                         NumberGenerator.NAMECACHE);
355         if (!cache.containsKey(_name)) {
356             NumberGenerator.getNumberGeneratorFromDB(NumberGenerator.SQL_NAME, _name);
357         }
358         return cache.get(_name);
359     }
360 
361     /**
362      * Returns for given parameter <i>_uuid</i> the instance of class
363      * {@link Type}.
364      *
365      * @param _uuid uuid of the type to get
366      * @return instance of class {@link Type}
367      * @throws CacheReloadException on error
368      */
369     public static NumberGenerator get(final UUID _uuid)
370         throws CacheReloadException
371     {
372         final Cache<UUID, NumberGenerator> cache = InfinispanCache.get().<UUID, NumberGenerator>getCache(
373                         NumberGenerator.UUIDCACHE);
374         if (!cache.containsKey(_uuid)) {
375             NumberGenerator.getNumberGeneratorFromDB(NumberGenerator.SQL_UUID, String.valueOf(_uuid));
376         }
377         return cache.get(_uuid);
378     }
379 
380     /**
381      * @param _numberGenerator NumberGenerator to be cached
382      */
383     private static void cacheNumberGenerator(final NumberGenerator _numberGenerator)
384     {
385         final Cache<UUID, NumberGenerator> cache4UUID = InfinispanCache.get().<UUID, NumberGenerator>getIgnReCache(
386                         NumberGenerator.UUIDCACHE);
387         cache4UUID.putIfAbsent(_numberGenerator.getUUID(), _numberGenerator);
388 
389         final Cache<String, NumberGenerator> nameCache = InfinispanCache.get().<String, NumberGenerator>getIgnReCache(
390                         NumberGenerator.NAMECACHE);
391         nameCache.putIfAbsent(_numberGenerator.getName(), _numberGenerator);
392 
393         final Cache<Long, NumberGenerator> idCache = InfinispanCache.get().<Long, NumberGenerator>getIgnReCache(
394                         NumberGenerator.IDCACHE);
395         idCache.putIfAbsent(_numberGenerator.getId(), _numberGenerator);
396     }
397 
398     /**
399      * @param _sql sql statement to be executed
400      * @param _criteria filter criteria
401      * @throws CacheReloadException on error
402      * @return false
403      */
404     private static boolean getNumberGeneratorFromDB(final String _sql,
405                                                     final Object _criteria)
406         throws CacheReloadException
407     {
408         boolean ret = false;
409         ConnectionResource con = null;
410         try {
411             con = Context.getThreadContext().getConnectionResource();
412             PreparedStatement stmt = null;
413             try {
414                 stmt = con.getConnection().prepareStatement(_sql);
415                 stmt.setObject(1, _criteria);
416                 final ResultSet rs = stmt.executeQuery();
417                 if (rs.next()) {
418                     final long id = rs.getLong(1);
419                     final String name = rs.getString(2).trim();
420                     final String uuid = rs.getString(3).trim();
421                     final String format = rs.getString(4).trim();
422                     NumberGenerator.LOG.debug("read NumberGenerator '{}' (id = {}), format = '{}'", name, id, format);
423                     final NumberGenerator generator = new NumberGenerator(id, name, uuid, format);
424                     NumberGenerator.cacheNumberGenerator(generator);
425                 }
426                 ret = true;
427                 rs.close();
428             } finally {
429                 if (stmt != null) {
430                     stmt.close();
431                 }
432             }
433             con.commit();
434 
435         } catch (final SQLException e) {
436             throw new CacheReloadException("could not read roles", e);
437         } catch (final EFapsException e) {
438             throw new CacheReloadException("could not read roles", e);
439         } finally {
440             if ((con != null) && con.isOpened()) {
441                 try {
442                     con.abort();
443                 } catch (final EFapsException e) {
444                     throw new CacheReloadException("could not read roles", e);
445                 }
446             }
447         }
448         return ret;
449     }
450 
451     @Override
452     public boolean equals(final Object _obj)
453     {
454         boolean ret;
455         if (_obj instanceof NumberGenerator) {
456             ret = ((NumberGenerator) _obj).getId() == getId();
457         } else {
458             ret = super.equals(_obj);
459         }
460         return ret;
461     }
462 
463     @Override
464     public int hashCode()
465     {
466         return  Long.valueOf(getId()).intValue();
467     }
468 }