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  
22  package org.efaps.jms;
23  
24  import java.util.Date;
25  import java.util.HashMap;
26  import java.util.Map;
27  import java.util.Map.Entry;
28  import java.util.Timer;
29  import java.util.TimerTask;
30  
31  import org.apache.commons.lang3.RandomStringUtils;
32  import org.efaps.admin.user.Person;
33  import org.efaps.admin.user.UserAttributesSet;
34  import org.efaps.db.Context;
35  import org.efaps.jaas.LoginHandler;
36  import org.efaps.util.EFapsException;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  
41  /**
42   * TODO comment!
43   *
44   * @author The eFaps Team
45   * @version $Id$
46   */
47  public final class JmsSession
48  {
49      /**
50       * Logger for this class.
51       */
52      private static final Logger LOG = LoggerFactory.getLogger(JmsSession.class);
53  
54      /**
55       * The map holds all cached Sessions.
56       */
57      private static final Map<String, JmsSession> CACHE = new HashMap<String, JmsSession>();
58  
59      /**
60       * Timer used to look for expired Session.
61       */
62      private static final Timer TIMER = new Timer();
63      static {
64          JmsSession.TIMER.schedule(new ExpireTask(), 0, 1000);
65      }
66  
67      /**
68       * Session timeout in seconds. Default 5 min = 300s.
69       */
70      private static int SESSIONTIMEOUT = 300;
71  
72      /**
73       * This instance map stores the Attributes which are valid for the whole
74       * session. It is passed on to the Context while opening it.
75       *
76       * @see #openContext()
77       */
78      private final Map<String, Object> sessionAttributes = new HashMap<String, Object>();
79  
80      /**
81       * Key to this Session.
82       */
83      private final String sessionKey;
84  
85      /**
86       * TimeStamp of the Session creation / last Context opening.
87       */
88      private Date timeStamp;
89  
90      /**
91       * Name of the User this Session belongs to.
92       */
93      private final String userName;
94  
95      /**
96       * Private Constructor.
97       * @param _userName name of the user this Session belong to.
98       * @throws EFapsException on error
99       */
100     private JmsSession(final String _userName)
101         throws EFapsException
102     {
103         this.sessionKey = RandomStringUtils.randomAlphanumeric(15);
104         this.timeStamp = new Date();
105         this.userName = _userName;
106     }
107 
108     /**
109      * Getter method for the instance variable {@link #sessionKey}.
110      *
111      * @return value of instance variable {@link #sessionKey}
112      */
113     private String getSessionKey()
114     {
115         return this.sessionKey;
116     }
117 
118     /**
119      * Method to check if a user is checked in.
120      *
121      * @return true if a user is checked in, else false
122      * @see #userName
123      */
124     public boolean isLogedIn()
125     {
126         boolean ret = false;
127         if (this.userName != null) {
128             ret = true;
129         }
130         return ret;
131     }
132 
133     /**
134      * Method that opens a new Context in eFaps, setting the User, the Locale,
135      * the Attributes of this Session {@link #sessionAttributes}.
136      * @throws EFapsException on error
137      *
138      * @see #attach()
139      */
140     public void openContext()
141         throws EFapsException
142     {
143         if (isLogedIn()) {
144             if (!Context.isTMActive()) {
145                 if (!this.sessionAttributes.containsKey(UserAttributesSet.CONTEXTMAPKEY)) {
146                     Context.begin(null, false);
147                     this.sessionAttributes.put(UserAttributesSet.CONTEXTMAPKEY, new UserAttributesSet(this.userName));
148                     Context.rollback();
149                 }
150                 Context.begin(this.userName, null, this.sessionAttributes, null, null, false);
151                 this.timeStamp = new Date();
152             }
153         }
154     }
155 
156     /**
157      * Method that closes the opened Context {@link #openContext()}, by
158      * committing or rollback it.
159      * @throws EFapsException on error
160      *
161      * @see #detach()
162      */
163     public void closeContext()
164         throws EFapsException
165     {
166         if (isLogedIn()) {
167             try {
168                 if (!Context.isTMNoTransaction()) {
169                     if (Context.isTMActive()) {
170                         Context.commit();
171                     } else {
172                         Context.rollback();
173                     }
174                 }
175             } catch (final SecurityException e) {
176                 throw new EFapsException("SecurityException", e);
177             } catch (final IllegalStateException e) {
178                 throw new EFapsException("IllegalStateException", e);
179             }
180         }
181     }
182 
183     /**
184      * Method to remove the current Session from the Cache.
185      */
186     public void logout()
187     {
188         JmsSession.CACHE.remove(getSessionKey());
189     }
190     /**
191      * Getter method for the instance variable {@link #timeStamp}.
192      *
193      * @return value of instance variable {@link #timeStamp}
194      */
195     protected Date getTimeStamp()
196     {
197         return this.timeStamp;
198     }
199 
200     /**
201      * @param _sessionKey   get the Session for a given Key
202      * @return if found Session for the given Sessionkey, else null
203      */
204     public static JmsSession getSession(final String _sessionKey)
205     {
206         return JmsSession.CACHE.get(_sessionKey);
207     }
208 
209     /**
210      * Login and create a Session.
211      *
212      * @param _userName         Name of the user to login
213      * @param _passwd           Password of the user to login
214      * @param _applicationKey   key of the application that requests
215      * @return SessionKey if login successful, else null
216      * @throws EFapsException on error
217      */
218     public static String login(final String _userName,
219                                final String _passwd,
220                                final String _applicationKey)
221         throws EFapsException
222     {
223         String ret = null;
224         if (JmsSession.checkLogin(_userName, _passwd, _applicationKey)) {
225             final JmsSession session = new JmsSession(_userName);
226             JmsSession.CACHE.put(session.getSessionKey(), session);
227             ret = session.getSessionKey();
228         }
229         return ret;
230     }
231 
232     /**
233      * @param _userName         name of the User to login
234      * @param _passwd           Password of the User
235      * @param _applicationKey   key of the application that requests
236      * @return true if login successfull, else false
237      */
238     private static boolean checkLogin(final String _userName,
239                                       final String _passwd,
240                                       final String _applicationKey)
241     {
242         boolean loginOk = false;
243         Context context = null;
244         try {
245             if (Context.isTMActive()) {
246                 context = Context.getThreadContext();
247             } else {
248                 context = Context.begin(null, false);
249             }
250             boolean ok = false;
251 
252             try {
253                 // on a new login the cache for Person is reseted
254                 Person.initialize();
255                 final LoginHandler loginHandler = new LoginHandler(_applicationKey);
256                 final Person person = loginHandler.checkLogin(_userName, _passwd);
257                 if (person != null && !person.getRoles().isEmpty()) {
258                     loginOk = true;
259                 }
260                 ok = true;
261             } finally {
262                 if (ok && context.allConnectionClosed() && Context.isTMActive()) {
263                     Context.commit();
264                 } else {
265                     if (Context.isTMMarkedRollback()) {
266                         JmsSession.LOG.error("transaction is marked to roll back");
267                     } else if (!context.allConnectionClosed()) {
268                         JmsSession.LOG.error("not all connection to database are closed");
269                     } else {
270                         JmsSession.LOG.error("transaction manager in undefined status");
271                     }
272                     Context.rollback();
273                 }
274             }
275         } catch (final EFapsException e) {
276             JmsSession.LOG.error("could not check name and password", e);
277         } finally {
278             context.close();
279         }
280         return loginOk;
281     }
282 
283     /**
284      * Setter method for variable {@link #SESSIONTIMEOUT}.
285      *
286      * @param _sessionTimeout value for variable {@link #SESSIONTIMEOUT}
287      */
288 
289     protected static void setSessionTimeout(final int _sessionTimeout)
290     {
291         JmsSession.SESSIONTIMEOUT = _sessionTimeout;
292     }
293 
294     /**
295      * Expire the Session.
296      */
297     public static class ExpireTask
298         extends TimerTask
299     {
300         @Override
301         public void run()
302         {
303             final Date now = new Date();
304             for (final Entry<String, JmsSession> entry : JmsSession.CACHE.entrySet()) {
305                 if (now.getTime() - entry.getValue().getTimeStamp().getTime() > 1000 * JmsSession.SESSIONTIMEOUT) {
306                     entry.getValue().logout();
307                 }
308             }
309         }
310     }
311 }