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.datamodel;
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.ArrayList;
28  import java.util.List;
29  import java.util.UUID;
30  
31  import org.efaps.admin.AbstractAdminObject;
32  import org.efaps.db.Context;
33  import org.efaps.db.transaction.ConnectionResource;
34  import org.efaps.db.wrapper.SQLPart;
35  import org.efaps.db.wrapper.SQLSelect;
36  import org.efaps.util.EFapsException;
37  import org.efaps.util.cache.CacheLogListener;
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   * Class for Dimensions inside eFaps.
46   *
47   * @author The eFaps Team
48   * @version $Id$
49   */
50  public class Dimension
51      extends AbstractAdminObject
52  {
53      /**
54       * Needed for serialization.
55       */
56      private static final long serialVersionUID = 1L;
57  
58      /**
59       * Logging instance used in this class.
60       */
61      private static final Logger LOG = LoggerFactory.getLogger(Dimension.class);
62  
63      /**
64       * This is the sql select statement to select all UoM from the database.
65       *
66       * @see #initialise
67       */
68      private static final String SQL_SELECT_UOM4DIMID = new SQLSelect()
69                      .column("ID")
70                      .column("DIMID")
71                      .column("NAME")
72                      .column("NUMERATOR")
73                      .column("DENOMINATOR")
74                      .from("T_DMUOM", 0)
75                      .addPart(SQLPart.WHERE).addColumnPart(0, "DIMID").addPart(SQLPart.EQUAL).addValuePart("?")
76                      .toString();
77  
78      /**
79       * This is the sql select statement to select all UoM from the database.
80       *
81       * @see #initialise
82       */
83      private static final String SQL_SELECT_UOM4ID = new SQLSelect()
84                      .column("ID")
85                      .column("DIMID")
86                      .column("NAME")
87                      .column("NUMERATOR")
88                      .column("DENOMINATOR")
89                      .from("T_DMUOM", 0)
90                      .addPart(SQLPart.WHERE).addColumnPart(0, "ID").addPart(SQLPart.EQUAL).addValuePart("?").toString();
91  
92      /**
93       * This is the SQL select statement to select a role from the database by
94       * ID.
95       */
96      private static final String SQL_ID = new SQLSelect()
97                      .column("ID")
98                      .column("NAME")
99                      .column("UUID")
100                     .column("DESCR")
101                     .column("BASEUOM")
102                     .from("T_DMDIM", 0)
103                     .addPart(SQLPart.WHERE).addColumnPart(0, "ID").addPart(SQLPart.EQUAL).addValuePart("?").toString();
104 
105     /**
106      * This is the SQL select statement to select a role from the database by
107      * Name.
108      */
109     private static final String SQL_NAME = new SQLSelect()
110                     .column("ID")
111                     .column("NAME")
112                     .column("UUID")
113                     .column("DESCR")
114                     .column("BASEUOM")
115                     .from("T_DMDIM", 0)
116                     .addPart(SQLPart.WHERE).addColumnPart(0, "NAME").addPart(SQLPart.EQUAL).addValuePart("?")
117                     .toString();
118 
119     /**
120      * This is the SQL select statement to select a role from the database by
121      * UUID.
122      */
123     private static final String SQL_UUID = new SQLSelect()
124                     .column("ID")
125                     .column("NAME")
126                     .column("UUID")
127                     .column("DESCR")
128                     .column("BASEUOM")
129                     .from("T_DMDIM", 0)
130                     .addPart(SQLPart.WHERE).addColumnPart(0, "UUID").addPart(SQLPart.EQUAL).addValuePart("?")
131                     .toString();
132 
133     /**
134      * Name of the Cache by UUID.
135      */
136     private static final String UUIDCACHE = Dimension.class.getName() + ".UUID";
137 
138     /**
139      * Name of the Cache by ID.
140      */
141     private static final String IDCACHE = Dimension.class.getName() + ".ID";
142 
143     /**
144      * Name of the Cache by Name.
145      */
146     private static final String NAMECACHE = Dimension.class.getName() + ".Name";
147 
148     /**
149      * Name of the Cache by ID.
150      */
151     private static final String IDCACHE4UOM = Dimension.class.getName() + ".UoM4ID";
152 
153     /**
154      * List of UoM belonging to this Dimension.
155      */
156     private final List<UoM> uoMs = new ArrayList<UoM>();
157 
158     /**
159      * Id of the base UoM.
160      */
161     private final long baseUoMId;
162 
163     /**
164      * Base UoM.
165      */
166     private UoM baseUoM;
167 
168     /**
169      * Constructor.
170      *
171      * @param _id id of this dimension
172      * @param _uuid UUID of this dimension
173      * @param _name Name of this dimension
174      * @param _description description for this dimension
175      * @param _baseUoMId id of the base UoM for this dimension
176      */
177     protected Dimension(final long _id,
178                         final String _uuid,
179                         final String _name,
180                         final String _description,
181                         final long _baseUoMId)
182     {
183         super(_id, _uuid, _name);
184         this.baseUoMId = _baseUoMId;
185     }
186 
187     /**
188      * Method to add an UoM to this dimension.
189      *
190      * @param _uom UoM to add
191      */
192     private void addUoM(final UoM _uom)
193     {
194         this.uoMs.add(_uom);
195         if (_uom.getId() == this.baseUoMId) {
196             this.baseUoM = _uom;
197         }
198     }
199 
200     /**
201      * Getter method for instance variable {@link #uoMs}.
202      *
203      * @return value of instance variable {@link #uoMs}
204      */
205     public List<UoM> getUoMs()
206     {
207         return this.uoMs;
208     }
209 
210     /**
211      * Getter method for instance variable {@link #baseUoM}.
212      *
213      * @return value of instance variable {@link #baseUoM}
214      */
215     public UoM getBaseUoM()
216     {
217         return this.baseUoM;
218     }
219 
220     /**
221      * Method to initialize the Cache of this CacheObjectInterface.
222      *
223      * @param _class class that called the method
224      * @throws CacheReloadException on error
225      */
226     public static void initialize(final Class<?> _class)
227         throws CacheReloadException
228     {
229         if (InfinispanCache.get().exists(Dimension.UUIDCACHE)) {
230             InfinispanCache.get().<UUID, Dimension>getCache(Dimension.UUIDCACHE).clear();
231         } else {
232             InfinispanCache.get().<UUID, Dimension>getCache(Dimension.UUIDCACHE)
233                             .addListener(new CacheLogListener(Dimension.LOG));
234         }
235         if (InfinispanCache.get().exists(Dimension.IDCACHE)) {
236             InfinispanCache.get().<Long, Dimension>getCache(Dimension.IDCACHE).clear();
237         } else {
238             InfinispanCache.get().<Long, Dimension>getCache(Dimension.IDCACHE)
239                             .addListener(new CacheLogListener(Dimension.LOG));
240         }
241         if (InfinispanCache.get().exists(Dimension.NAMECACHE)) {
242             InfinispanCache.get().<String, Dimension>getCache(Dimension.NAMECACHE).clear();
243         } else {
244             InfinispanCache.get().<String, Dimension>getCache(Dimension.NAMECACHE)
245                             .addListener(new CacheLogListener(Dimension.LOG));
246         }
247         if (InfinispanCache.get().exists(Dimension.IDCACHE4UOM)) {
248             InfinispanCache.get().<Long, UoM>getCache(Dimension.IDCACHE4UOM).clear();
249         } else {
250             InfinispanCache.get().<Long, UoM>getCache(Dimension.IDCACHE4UOM)
251                             .addListener(new CacheLogListener(Dimension.LOG));
252         }
253     }
254 
255     /**
256      * Method to initialize the Cache of this CacheObjectInterface.
257      *
258      * @throws CacheReloadException on error
259      */
260     public static void initialize()
261         throws CacheReloadException
262     {
263         Dimension.initialize(Dimension.class);
264     }
265 
266     /**
267      * Returns for given parameter <i>_id</i> the instance of class
268      * {@link Dimension} .
269      *
270      * @param _id id of the type to get
271      * @return instance of class {@link Dimension}
272      * @throws CacheReloadException on error
273      */
274     public static Dimension get(final long _id)
275         throws CacheReloadException
276     {
277         final Cache<Long, Dimension> cache = InfinispanCache.get().<Long, Dimension>getCache(Dimension.IDCACHE);
278         if (!cache.containsKey(_id)) {
279             Dimension.getDimensionFromDB(Dimension.SQL_ID, _id);
280         }
281         return cache.get(_id);
282     }
283 
284     /**
285      * Returns for given parameter <i>_name</i> the instance of class
286      * {@link Dimension}.
287      *
288      * @param _name name of the type to get
289      * @return instance of class {@link Dimension}
290      * @throws CacheReloadException on error
291      */
292     public static Dimension get(final String _name)
293         throws CacheReloadException
294     {
295         final Cache<String, Dimension> cache = InfinispanCache.get().<String, Dimension>getCache(Dimension.NAMECACHE);
296         if (!cache.containsKey(_name)) {
297             Dimension.getDimensionFromDB(Dimension.SQL_NAME, _name);
298         }
299         return cache.get(_name);
300     }
301 
302     /**
303      * Returns for given parameter <i>_uuid</i> the instance of class
304      * {@link Dimension}.
305      *
306      * @param _uuid uuid of the type to get
307      * @return instance of class {@link Dimension}
308      * @throws CacheReloadException on error
309      */
310     public static Dimension get(final UUID _uuid)
311         throws CacheReloadException
312     {
313         final Cache<UUID, Dimension> cache = InfinispanCache.get().<UUID, Dimension>getCache(Dimension.UUIDCACHE);
314         if (!cache.containsKey(_uuid)) {
315             Dimension.getDimensionFromDB(Dimension.SQL_UUID, String.valueOf(_uuid));
316         }
317         return cache.get(_uuid);
318     }
319 
320     /**
321      * Static Method to get an UoM for an id.
322      *
323      * @param _uoMId if the UoM is wanted for.
324      * @return UoM
325      */
326     public static UoM getUoM(final Long _uoMId)
327     {
328         final Cache<Long, UoM> cache = InfinispanCache.get().<Long, UoM>getCache(Dimension.IDCACHE4UOM);
329         if (!cache.containsKey(_uoMId)) {
330             try {
331                 Dimension.getUoMFromDB(Dimension.SQL_SELECT_UOM4ID, _uoMId);
332             } catch (final CacheReloadException e) {
333                 Dimension.LOG.error("read UoM from DB failed for id: '{}'", _uoMId);
334             }
335         }
336         return cache.get(_uoMId);
337     }
338 
339     /**
340      * @param _sql sql statment to be executed
341      * @param _criteria filter criteria
342      * @throws CacheReloadException on error
343      */
344     private static void getUoMFromDB(final String _sql,
345                                      final Object _criteria)
346         throws CacheReloadException
347     {
348         ConnectionResource con = null;
349         try {
350             final List<Object[]> values = new ArrayList<Object[]>();
351 
352             con = Context.getThreadContext().getConnectionResource();
353             PreparedStatement stmt = null;
354             try {
355                 stmt = con.getConnection().prepareStatement(_sql);
356                 stmt.setObject(1, _criteria);
357                 final ResultSet rs = stmt.executeQuery();
358                 while (rs.next()) {
359                     values.add(new Object[] {
360                                     rs.getLong(1),
361                                     rs.getLong(2),
362                                     rs.getString(3).trim(),
363                                     rs.getInt(4),
364                                     rs.getInt(5)
365                     });
366                 }
367                 rs.close();
368             } finally {
369                 if (stmt != null) {
370                     stmt.close();
371                 }
372             }
373             con.commit();
374             final Cache<Long, UoM> cache = InfinispanCache.get().<Long, UoM>getCache(Dimension.IDCACHE4UOM);
375             for (final Object[] row : values) {
376                 final long id = (Long) row[0];
377                 final long dimId = (Long) row[1];
378                 final String name = (String) row[2];
379                 final int numerator = (Integer) row[3];
380                 final int denominator = (Integer) row[4];
381                 Dimension.LOG.debug("read UoM '" + name + "' (id = " + id + ")");
382                 final Dimension dim = Dimension.get(dimId);
383                 final UoM uom = new UoM(id, dimId, name, numerator, denominator);
384                 dim.addUoM(uom);
385                 cache.put(uom.getId(), uom);
386                 // needed due to cluster serialization that does not update automatically
387                 Dimension.cacheDimension(dim);
388             }
389         } catch (final SQLException e) {
390             throw new CacheReloadException("could not read Dimension", e);
391         } catch (final EFapsException e) {
392             throw new CacheReloadException("could not read Dimension", e);
393         } finally {
394             if ((con != null) && con.isOpened()) {
395                 try {
396                     con.abort();
397                 } catch (final EFapsException e) {
398                     throw new CacheReloadException("could not read Dimension", e);
399                 }
400             }
401         }
402     }
403 
404     /**
405      * @param _dimension Dimension to be cached
406      */
407     private static void cacheDimension(final Dimension _dimension)
408     {
409         final Cache<UUID, Dimension> cache4UUID = InfinispanCache.get().<UUID, Dimension>getIgnReCache(
410                         Dimension.UUIDCACHE);
411         cache4UUID.put(_dimension.getUUID(), _dimension);
412 
413         final Cache<String, Dimension> nameCache = InfinispanCache.get().<String, Dimension>getIgnReCache(
414                         Dimension.NAMECACHE);
415         nameCache.put(_dimension.getName(), _dimension);
416 
417         final Cache<Long, Dimension> idCache = InfinispanCache.get().<Long, Dimension>getIgnReCache(
418                         Dimension.IDCACHE);
419         idCache.put(_dimension.getId(), _dimension);
420     }
421 
422     /**
423      * @param _sql sql statement to be executed
424      * @param _criteria filter criteria
425      * @throws CacheReloadException on error
426      * @return false
427      */
428     private static boolean getDimensionFromDB(final String _sql,
429                                               final Object _criteria)
430         throws CacheReloadException
431     {
432         boolean ret = false;
433         ConnectionResource con = null;
434         try {
435             Dimension dim = null;
436             con = Context.getThreadContext().getConnectionResource();
437             PreparedStatement stmt = null;
438             try {
439                 stmt = con.getConnection().prepareStatement(_sql);
440                 stmt.setObject(1, _criteria);
441                 final ResultSet rs = stmt.executeQuery();
442                 if (rs.next()) {
443                     final long id = rs.getLong(1);
444                     final String name = rs.getString(2).trim();
445                     final String uuid = rs.getString(3).trim();
446                     final String descr = rs.getString(4).trim();
447                     final long baseuom = rs.getLong(5);
448                     Dimension.LOG.debug("read Dimension '" + name + "' (id = " + id + ")");
449                     dim = new Dimension(id, uuid, name, descr, baseuom);
450                 }
451                 ret = true;
452                 rs.close();
453             } finally {
454                 if (stmt != null) {
455                     stmt.close();
456                 }
457             }
458             con.commit();
459             if (dim != null) {
460                 Dimension.cacheDimension(dim);
461                 Dimension.getUoMFromDB(Dimension.SQL_SELECT_UOM4DIMID, dim.getId());
462             }
463         } catch (final SQLException e) {
464             throw new CacheReloadException("could not read roles", e);
465         } catch (final EFapsException e) {
466             throw new CacheReloadException("could not read roles", e);
467         } finally {
468             if ((con != null) && con.isOpened()) {
469                 try {
470                     con.abort();
471                 } catch (final EFapsException e) {
472                     throw new CacheReloadException("could not read roles", e);
473                 }
474             }
475         }
476         return ret;
477     }
478 
479     /**
480      * Class for an UoM. (Unit of Measurement)
481      */
482     public static class UoM
483         implements Serializable
484     {
485 
486         /**
487          * Needed for serialization.
488          */
489         private static final long serialVersionUID = 1L;
490 
491         /**
492          * Id of this UoM.
493          */
494         private final long id;
495 
496         /**
497          * Id of the dimension this UoM belongs to.
498          */
499         private final long dimId;
500 
501         /**
502          * Name of this UoM.
503          */
504         private final String name;
505 
506         /**
507          * Numerator for this UoM.
508          */
509         private final int numerator;
510 
511         /**
512          * Denominator for this UoM.
513          */
514         private final int denominator;
515 
516         /**
517          * @param _id id of this UoM
518          * @param _dimId Id of the dimension this UoM belongs to
519          * @param _name Name of this UoM
520          * @param _numerator Numerator for this UoM
521          * @param _denominator Denominator for this UoM
522          */
523         protected UoM(final long _id,
524                       final long _dimId,
525                       final String _name,
526                       final int _numerator,
527                       final int _denominator)
528         {
529             this.id = _id;
530             this.dimId = _dimId;
531             this.name = _name;
532             this.numerator = _numerator;
533             this.denominator = _denominator;
534         }
535 
536         /**
537          * Getter method for instance variable {@link #id}.
538          *
539          * @return value of instance variable {@link #id}
540          */
541         public long getId()
542         {
543             return this.id;
544         }
545 
546         /**
547          * Getter method for instance variable {@link #dimId}.
548          *
549          * @return value of instance variable {@link #dimId}
550          */
551         public long getDimId()
552         {
553             return this.dimId;
554         }
555 
556         /**
557          * Getter method for instance variable {@link #name}.
558          *
559          * @return value of instance variable {@link #name}
560          */
561         public String getName()
562         {
563             return this.name;
564         }
565 
566         /**
567          * Getter method for instance variable {@link #numerator}.
568          *
569          * @return value of instance variable {@link #numerator}
570          */
571         public int getNumerator()
572         {
573             return this.numerator;
574         }
575 
576         /**
577          * Getter method for instance variable {@link #denominator}.
578          *
579          * @return value of instance variable {@link #denominator}
580          */
581         public int getDenominator()
582         {
583             return this.denominator;
584         }
585 
586         /**
587          * Method to get the Dimension this UoM belongs to.
588          *
589          * @return Dimension
590          */
591         public Dimension getDimension()
592         {
593             Dimension ret = null;
594             try {
595                 ret = Dimension.get(this.dimId);
596             } catch (final CacheReloadException e) {
597                 // TODO Auto-generated catch block
598                 e.printStackTrace();
599             }
600             return ret;
601         }
602 
603         /**
604          * Method to calculate the given value into an base value.
605          *
606          * @param _value value to be calculated
607          * @return calculated base value
608          */
609         public Double getBaseDouble(final Double _value)
610         {
611             return _value * this.numerator / this.denominator;
612         }
613     }
614 
615     @Override
616     public boolean equals(final Object _obj)
617     {
618         boolean ret;
619         if (_obj instanceof Dimension) {
620             ret = ((Dimension) _obj).getId() == getId();
621         } else {
622             ret = super.equals(_obj);
623         }
624         return ret;
625     }
626 
627     @Override
628     public int hashCode()
629     {
630         return  Long.valueOf(getId()).intValue();
631     }
632 }