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.user;
22  
23  import java.sql.PreparedStatement;
24  import java.sql.ResultSet;
25  import java.sql.SQLException;
26  import java.util.UUID;
27  import java.util.concurrent.TimeUnit;
28  
29  import org.efaps.db.Context;
30  import org.efaps.db.transaction.ConnectionResource;
31  import org.efaps.db.wrapper.SQLPart;
32  import org.efaps.db.wrapper.SQLSelect;
33  import org.efaps.util.EFapsException;
34  import org.efaps.util.cache.CacheLogListener;
35  import org.efaps.util.cache.CacheReloadException;
36  import org.efaps.util.cache.InfinispanCache;
37  import org.infinispan.Cache;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  /**
42   * Class represents the instance of a group in eFaps.
43   *
44   * @author The eFaps Team
45   * @version $Id$
46   */
47  public final class Group
48      extends AbstractUserObject
49  {
50      /**
51       * Needed for serialization.
52       */
53      private static final long serialVersionUID = 1L;
54  
55      /**
56       * Logging instance used in this class.
57       */
58      private static final Logger LOG = LoggerFactory.getLogger(Group.class);
59  
60      /**
61       * This is the SQL select statement to select a Group from the database by ID.
62       */
63      private static final String SQL_ID = new SQLSelect().column("ID")
64                      .column("UUID")
65                      .column("NAME")
66                      .column("STATUS")
67                      .from("V_USERGROUP", 0)
68                      .addPart(SQLPart.WHERE).addColumnPart(0, "ID").addPart(SQLPart.EQUAL).addValuePart("?").toString();
69  
70      /**
71       * This is the SQL select statement to select a Group from the database by Name.
72       */
73      private static final String SQL_NAME = new SQLSelect().column("ID")
74                      .column("UUID")
75                      .column("NAME")
76                      .column("STATUS")
77                      .from("V_USERGROUP", 0)
78                      .addPart(SQLPart.WHERE).addColumnPart(0, "NAME").addPart(SQLPart.EQUAL).addValuePart("?")
79                      .toString();
80  
81      /**
82       * This is the SQL select statement to select a Group from the database by UUID.
83       */
84      private static final String SQL_UUID = new SQLSelect().column("ID")
85                      .column("UUID")
86                      .column("NAME")
87                      .column("STATUS")
88                      .from("V_USERGROUP", 0)
89                      .addPart(SQLPart.WHERE).addColumnPart(0, "UUID").addPart(SQLPart.EQUAL).addValuePart("?")
90                      .toString();
91  
92      /**
93       * This is the SQL select statement to select a Group from the database using the JAAS key..
94       */
95      private static final String SQL_JAASKEY = new SQLSelect().column("ID")
96                      .from("V_USERGROUPJASSKEY", 0)
97                      .addPart(SQLPart.WHERE).addColumnPart(0, "JAASKEY").addPart(SQLPart.EQUAL).addValuePart("?")
98                      .addPart(SQLPart.AND).addColumnPart(0, "JAASSYSID").addPart(SQLPart.EQUAL).addValuePart("?")
99                      .toString();
100 
101     /**
102      * Name of the Cache by ID.
103      */
104     private static final String IDCACHE = Group.class.getName() + ".ID";
105 
106     /**
107      * Name of the Cache by Name.
108      */
109     private static final String NAMECACHE = Group.class.getName() + ".Name";
110 
111     /**
112      * Name of the Cache by Name.
113      */
114     private static final String UUIDCACHE = Group.class.getName() + ".UUID";
115 
116 
117     /**
118      * Use to mark not found and return <code>null</code>.
119      */
120     private static final Group NULL = new Group(0, null, null, false);
121 
122     /**
123      * Create a new group instance. The method is used from the static method
124      * {@link #initialize()} to read all groups from the database.
125      *
126      * @param _id       id of the group
127      * @param _uuid     UUID of the group
128      * @param _name     name of the group
129      * @param _status   status of the group
130      */
131     private Group(final long _id,
132                   final String _uuid,
133                   final String _name,
134                   final boolean _status)
135     {
136         super(_id, _uuid, _name, _status);
137     }
138 
139     /**
140      * Returns the viewable name of the group. The method {@link #getName} is
141      * used for the viewing name.
142      *
143      * @param _context  context for this request
144      * @return name of the group
145      * @see #getName
146      */
147     public String getViewableName(final Context _context)
148     {
149         return getName();
150     }
151 
152     /**
153      * Checks, if the given person is assigned to this group.
154      *
155      * @param _person   person to test
156      * @return <i>true</i> if the person is assigned to this group, otherwise
157      *         <i>false</i>
158      * @see Person#isAssigned(Group)
159      */
160     @Override
161     public boolean hasChildPerson(final Person _person)
162     {
163         return _person.isAssigned(this);
164     }
165 
166     /**
167      * Method to initialize the Cache of this CacheObjectInterface.
168      *
169      * @see #CACHE
170      */
171     public static void initialize()
172     {
173         if (InfinispanCache.get().exists(Group.IDCACHE)) {
174             InfinispanCache.get().<Long, Group>getCache(Group.IDCACHE).clear();
175         } else {
176             InfinispanCache.get().<Long, Group>getCache(Group.IDCACHE).addListener(new CacheLogListener(Group.LOG));
177         }
178         if (InfinispanCache.get().exists(Group.NAMECACHE)) {
179             InfinispanCache.get().<String, Group>getCache(Group.NAMECACHE).clear();
180         } else {
181             InfinispanCache.get().<String, Group>getCache(Group.NAMECACHE).addListener(new CacheLogListener(Group.LOG));
182         }
183         if (InfinispanCache.get().exists(Group.UUIDCACHE)) {
184             InfinispanCache.get().<UUID, Group>getCache(Group.UUIDCACHE).clear();
185         } else {
186             InfinispanCache.get().<UUID, Group>getCache(Group.UUIDCACHE).addListener(new CacheLogListener(Group.LOG));
187         }
188     }
189 
190     /**
191      * Returns for given parameter <i>_id</i> the instance of class
192      * {@link Group}.
193      *
194      * @param _id id to search in the cache
195      * @return instance of class {@link Group}
196      * @throws CacheReloadException on error
197      */
198     public static Group get(final long _id)
199         throws CacheReloadException
200     {
201         final Cache<Long, Group> cache = InfinispanCache.get().<Long, Group>getCache(Group.IDCACHE);
202         if (!cache.containsKey(_id) && !Group.getGroupFromDB(Group.SQL_ID, _id)) {
203             cache.put(_id, Group.NULL, 100, TimeUnit.SECONDS);
204         }
205         final Group ret = cache.get(_id);
206         return ret.equals(Group.NULL) ? null : ret;
207     }
208 
209     /**
210      * Returns for given parameter <i>_name</i> the instance of class
211      * {@link Group}.
212      *
213      * @param _name name to search in the cache
214      * @return instance of class {@link Group}
215      * @throws CacheReloadException on error
216      */
217     public static Group get(final String _name)
218         throws CacheReloadException
219     {
220         final Cache<String, Group> cache = InfinispanCache.get().<String, Group>getCache(Group.IDCACHE);
221         if (!cache.containsKey(_name) && !Group.getGroupFromDB(Group.SQL_NAME, _name)) {
222             cache.put(_name, Group.NULL, 100, TimeUnit.SECONDS);
223         }
224         final Group ret = cache.get(_name);
225         return ret.equals(Group.NULL) ? null : ret;
226     }
227 
228     /**
229      * Returns for given parameter <i>_uuid</i> the instance of class
230      * {@link Group}.
231      *
232      * @param _uuid _uuid to search in the cache
233      * @return instance of class {@link Group}
234      * @throws CacheReloadException on error
235      */
236     public static Group get(final UUID _uuid)
237         throws CacheReloadException
238     {
239         final Cache<UUID, Group> cache = InfinispanCache.get().<UUID, Group>getCache(Group.IDCACHE);
240         if (!cache.containsKey(_uuid) && !Group.getGroupFromDB(Group.SQL_UUID, _uuid.toString())) {
241             cache.put(_uuid, Group.NULL, 100, TimeUnit.SECONDS);
242         }
243         final Group ret = cache.get(_uuid);
244         return ret.equals(Group.NULL) ? null : ret;
245     }
246 
247     /**
248      * Returns for given parameter <i>_jaasKey</i> the instance of class
249      * {@link Group}. The parameter <i>_jaasKey</i> is the name of the group
250      * used in the given JAAS system for the group.
251      *
252      * @param _jaasSystem   JAAS system for which the JAAS key is named
253      * @param _jaasKey      key in the foreign JAAS system for which the group
254      *                      is searched
255      * @return instance of class {@link Group}, or <code>null</code> if group
256      *         is not found
257      * @throws EFapsException if group with JAAS key could not be fetched from
258      *                        eFaps
259      * @see #get(long)
260      */
261     public static Group getWithJAASKey(final JAASSystem _jaasSystem,
262                                        final String _jaasKey)
263         throws EFapsException
264     {
265         long groupId = 0;
266         ConnectionResource rsrc = null;
267         try {
268             rsrc = Context.getThreadContext().getConnectionResource();
269 
270             PreparedStatement stmt = null;
271             try {
272                 stmt = rsrc.getConnection().prepareStatement(Group.SQL_JAASKEY);
273                 stmt.setObject(1, _jaasKey);
274                 stmt.setObject(2, _jaasSystem.getId());
275                 final ResultSet rs = stmt.executeQuery();
276                 if (rs.next()) {
277                     groupId = rs.getLong(1);
278                 }
279                 rs.close();
280             } catch (final SQLException e) {
281                 Group.LOG.error("search for group for JAAS system '" + _jaasSystem.getName() + "' "
282                         + "with key '" + _jaasKey + "' is not possible", e);
283                 throw new EFapsException(Group.class, "getWithJAASKey.SQLException", e,
284                                          _jaasSystem.getName(), _jaasKey);
285             } finally {
286                 try {
287                     stmt.close();
288                 } catch (final SQLException e) {
289                     Group.LOG.error("Statement could not be closed", e);
290                 }
291             }
292             rsrc.commit();
293         } finally {
294             if ((rsrc != null) && rsrc.isOpened()) {
295                 rsrc.abort();
296             }
297         }
298         return Group.get(groupId);
299     }
300 
301     /**
302      * @param _group Group to be cached
303      */
304     private static void cacheGroup(final Group _group)
305     {
306         final Cache<String, Group> nameCache = InfinispanCache.get().<String, Group>getIgnReCache(Group.NAMECACHE);
307         nameCache.putIfAbsent(_group.getName(), _group);
308 
309         final Cache<Long, Group> idCache = InfinispanCache.get().<Long, Group>getIgnReCache(Group.IDCACHE);
310         idCache.putIfAbsent(_group.getId(), _group);
311 
312         final Cache<UUID, Group> uuidCache = InfinispanCache.get().<UUID, Group>getIgnReCache(Group.UUIDCACHE);
313         uuidCache.putIfAbsent(_group.getUUID(), _group);
314     }
315 
316     /**
317      * @param _sql      SQL Statment to be execuetd
318      * @param _criteria filter criteria
319      * @return true if successful
320      * @throws CacheReloadException on error
321      */
322     private static boolean getGroupFromDB(final String _sql,
323                                           final Object _criteria)
324         throws CacheReloadException
325     {
326         boolean ret = false;
327         ConnectionResource con = null;
328         try {
329             con = Context.getThreadContext().getConnectionResource();
330             PreparedStatement stmt = null;
331             try {
332                 stmt = con.getConnection().prepareStatement(_sql);
333                 stmt.setObject(1, _criteria);
334                 final ResultSet rs = stmt.executeQuery();
335                 if (rs.next()) {
336                     final long id = rs.getLong(1);
337                     final String uuid = rs.getString(2);
338                     final String name = rs.getString(3).trim();
339                     final boolean status = rs.getBoolean(4);
340                     Group.LOG.debug("read group '{}' (id = {}, uuid = {})", name, id, uuid);
341                     final Group group = new Group(id, uuid, name, status);
342                     Group.cacheGroup(group);
343                     ret = true;
344                 }
345                 rs.close();
346             } finally {
347                 if (stmt != null) {
348                     stmt.close();
349                 }
350             }
351             con.commit();
352         } catch (final SQLException e) {
353             throw new CacheReloadException("could not read Groups", e);
354         } catch (final EFapsException e) {
355             throw new CacheReloadException("could not read Groups", e);
356         } finally {
357             if ((con != null) && con.isOpened()) {
358                 try {
359                     con.abort();
360                 } catch (final EFapsException e) {
361                     throw new CacheReloadException("could not read Groups", e);
362                 }
363             }
364         }
365         return ret;
366     }
367 
368     @Override
369     public boolean equals(final Object _obj)
370     {
371         boolean ret;
372         if (_obj instanceof Group) {
373             ret = ((Group) _obj).getId() == getId();
374         } else {
375             ret = super.equals(_obj);
376         }
377         return ret;
378     }
379 
380     @Override
381     public int hashCode()
382     {
383         return  Long.valueOf(getId()).intValue();
384     }
385 }