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