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