001 /*
002 *
003 * $Revision: 15422 $ $Date: 2009-07-01 10:48:51 +0200 (Wed, 01 Jul 2009) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.common;
025
026 import static org.mycore.common.events.MCRSessionEvent.Type.activated;
027 import static org.mycore.common.events.MCRSessionEvent.Type.passivated;
028
029 import java.security.Principal;
030 import java.util.Arrays;
031 import java.util.Collections;
032 import java.util.Hashtable;
033 import java.util.Iterator;
034 import java.util.List;
035 import java.util.Map;
036 import java.util.Set;
037 import java.util.Map.Entry;
038 import java.util.concurrent.atomic.AtomicInteger;
039
040 import org.apache.log4j.Logger;
041 import org.hibernate.Transaction;
042 import org.mycore.backend.hibernate.MCRHIBConnection;
043 import org.mycore.common.events.MCRSessionEvent;
044 import org.mycore.common.events.MCRSessionListener;
045 import org.mycore.frontend.servlets.MCRServletJob;
046
047 /**
048 * Instances of this class collect information kept during a session like the
049 * currently active user, the preferred language etc.
050 *
051 * @author Detlev Degenhardt
052 * @author Jens Kupferschmidt
053 * @author Frank L\u00fctzenkirchen
054 *
055 * @version $Revision: 15422 $ $Date: 2008-03-17 17:12:15 +0100 (Mo, 17 Mrz
056 * 2008) $
057 */
058 public class MCRSession implements Cloneable {
059 /** A map storing arbitrary session data * */
060 private Map<Object, Object> map = new Hashtable<Object, Object>();
061
062 @SuppressWarnings("unchecked")
063 private Map.Entry<Object, Object>[] emptyEntryArray = new Map.Entry[0];
064
065 private List<Map.Entry<Object, Object>> mapEntries;
066
067 private boolean mapChanged = true;
068
069 AtomicInteger accessCount;
070
071 AtomicInteger concurrentAccess;
072
073 ThreadLocal<AtomicInteger> currentThreadCount = new ThreadLocal<AtomicInteger>() {
074 public AtomicInteger initialValue() {
075 return new AtomicInteger();
076 }
077 };
078
079 /** the logger */
080 static Logger LOGGER = Logger.getLogger(MCRSession.class.getName());
081
082 /** The user ID of the session */
083 private String userID = null;
084
085 /** The language for this session as upper case character */
086 private String language = null;
087
088 /** The unique ID of this session */
089 private String sessionID = null;
090
091 private String FullName = null;
092
093 private String CurrentDocumentID = null;
094
095 private String ip = null;
096
097 private long loginTime, lastAccessTime, thisAccessTime, createTime;
098
099 private boolean dataBaseAccess;
100
101 private ThreadLocal<Transaction> transaction = new ThreadLocal<Transaction>();
102
103 /**
104 * The constructor of a MCRSession. As default the user ID is set to the
105 * value of the property variable named 'MCR.Users.Guestuser.UserName'.
106 */
107 MCRSession() {
108 MCRConfiguration config = MCRConfiguration.instance();
109 userID = config.getString("MCR.Users.Guestuser.UserName", "gast");
110 language = config.getString("MCR.Metadata.DefaultLang", "de");
111 dataBaseAccess = MCRConfiguration.instance().getBoolean("MCR.Persistence.Database.Enable", true);
112
113 accessCount = new AtomicInteger();
114 concurrentAccess = new AtomicInteger();
115
116 ip = "";
117 sessionID = buildSessionID();
118 MCRSessionMgr.addSession(this);
119
120 LOGGER.debug("MCRSession created " + sessionID);
121 setLoginTime();
122 createTime = loginTime;
123
124 }
125
126 public final void setLoginTime() {
127 loginTime = System.currentTimeMillis();
128 lastAccessTime = loginTime;
129 thisAccessTime = loginTime;
130 }
131
132 /**
133 * Constructs a unique session ID for this session, based on current time
134 * and IP address of host where the code runs.
135 */
136 private static synchronized String buildSessionID() {
137 String localip = getLocalIP();
138
139 java.util.StringTokenizer st = new java.util.StringTokenizer(localip, ".");
140
141 long sum = Integer.parseInt(st.nextToken());
142
143 while (st.hasMoreTokens())
144 sum = (sum << 8) + Integer.parseInt(st.nextToken());
145
146 String address = Long.toString(sum, 36);
147 address = "000000" + address;
148
149 String prefix = address.substring(address.length() - 6);
150
151 long now = System.currentTimeMillis();
152 String suffix = Long.toString(now, 36);
153
154 return prefix + "-" + suffix;
155 }
156
157 /**
158 * Returns the unique ID of this session
159 */
160 public String getID() {
161 return sessionID;
162 }
163
164 /**
165 * Returns a list of all stored object keys within MCRSession.
166 * This method is not thread safe.
167 * I you need thread safe access to all stored objects use {@link MCRSession#getMapEntries()} instead.
168 * @return Returns a list of all stored object keys within MCRSession as
169 * java.util.Ierator
170 */
171 public Iterator<Object> getObjectsKeyList() {
172 return Collections.unmodifiableSet(map.keySet()).iterator();
173 }
174
175 /**
176 * Returns an unmodifiable list of all entries in this MCRSession
177 * This method is thread safe.
178 */
179 public List<Map.Entry<Object, Object>> getMapEntries() {
180 if (this.mapChanged) {
181 this.mapChanged = false;
182 final Set<Entry<Object, Object>> entrySet = Collections.unmodifiableMap(map).entrySet();
183 final Map.Entry<Object, Object>[] entryArray = entrySet.toArray(emptyEntryArray);
184 this.mapEntries = Collections.unmodifiableList(Arrays.asList(entryArray));
185 }
186 return this.mapEntries;
187 }
188
189 /** returns the current user ID */
190 public final String getCurrentUserID() {
191 return userID;
192 }
193
194 /** sets the current user ID */
195 public final void setCurrentUserID(String userID) {
196 this.userID = userID;
197 }
198
199 /** returns the current language */
200 public final String getCurrentLanguage() {
201 return language;
202 }
203
204 /** sets the current language */
205 public final void setCurrentLanguage(String language) {
206 this.language = language;
207 }
208
209 /** returns the current document ID */
210 public final String getCurrentDocumentID() {
211 return CurrentDocumentID;
212 }
213
214 /** returns the current document ID */
215 public final String getCurrentUserName() {
216 return FullName;
217 }
218
219 /** sets the current user fullname */
220 public final void setCurrentUserName(String userName) {
221 this.FullName = userName;
222 }
223
224 /** sets the current document ID */
225 public final void setCurrentDocumentID(String DocumentID) {
226 this.CurrentDocumentID = DocumentID;
227 }
228
229 /** Write data to the logger for debugging purposes */
230 public final void debug() {
231 LOGGER.debug("SessionID = " + sessionID);
232 LOGGER.debug("UserID = " + userID);
233 LOGGER.debug("IP = " + ip);
234 LOGGER.debug("language = " + language);
235 }
236
237 /** Stores an object under the given key within the session * */
238 public Object put(Object key, Object value) {
239 mapChanged = true;
240 return map.put(key, value);
241 }
242
243 /** Returns the object that was stored in the session under the given key * */
244 public Object get(Object key) {
245 return map.get(key);
246 }
247
248 public void deleteObject(Object key) {
249 mapChanged = true;
250 map.remove(key);
251 }
252
253 /** Get the ip value to the local IP */
254 public static final String getLocalIP() {
255 try {
256 return java.net.InetAddress.getLocalHost().getHostAddress();
257 } catch (java.net.UnknownHostException ignored) {
258 return "127.0.0.1";
259 }
260 }
261
262 /** Get the current ip value */
263 public String getCurrentIP() {
264 return ip;
265 }
266
267 /** Set the ip to the given IP */
268 public final void setCurrentIP(String newip) {
269 java.util.StringTokenizer st = new java.util.StringTokenizer(newip, ".");
270 if (st.countTokens() != 4)
271 return;
272 try {
273 while (st.hasMoreTokens()) {
274 int i = Integer.parseInt(st.nextToken());
275 if ((i < 0) || (i > 255)) {
276 return;
277 }
278 }
279 } catch (Exception e) {
280 LOGGER.debug("Exception while parsing new ip " + newip + " using old value.", e);
281 return;
282 }
283 this.ip = newip;
284 }
285
286 public final long getLoginTime() {
287 return loginTime;
288 }
289
290 public void close() {
291 // remove from session list
292 LOGGER.debug("Remove myself from MCRSession list");
293 MCRSessionMgr.removeSession(this);
294 // clear bound objects
295 LOGGER.debug("Clearing local map.");
296 map.clear();
297 mapEntries = null;
298 this.sessionID = null;
299 }
300
301 public String toString() {
302 StringBuilder sb = new StringBuilder();
303 sb.append("MCRSession[");
304 sb.append(getID());
305 sb.append(",user:'");
306 sb.append(getCurrentUserID());
307 sb.append("',ip:");
308 sb.append(getCurrentIP());
309 sb.append("]");
310 return sb.toString();
311 }
312
313 public long getLastAccessedTime() {
314 return lastAccessTime;
315 }
316
317 /**
318 * Activate this session. For internal use mainly by MCRSessionMgr.
319 *
320 * @see MCRSessionMgr#setCurrentSession(MCRSession)
321 */
322 void activate() {
323 lastAccessTime = thisAccessTime;
324 thisAccessTime = System.currentTimeMillis();
325 accessCount.incrementAndGet();
326 if (currentThreadCount.get().getAndIncrement() == 0) {
327 fireSessionEvent(activated, concurrentAccess.incrementAndGet());
328 } else {
329 try {
330 throw new MCRException("Cannot activate a Session more than once per thread: " + currentThreadCount.get().get());
331 } catch (Exception e) {
332 LOGGER.debug("Too many activate() calls stacktrace:", e);
333 }
334 }
335 }
336
337 /**
338 * Passivate this session. For internal use mainly by MCRSessionMgr.
339 *
340 * @see MCRSessionMgr#releaseCurrentSession()
341 */
342 void passivate() {
343 if (currentThreadCount.get().getAndDecrement() == 1) {
344 fireSessionEvent(passivated, concurrentAccess.decrementAndGet());
345 } else {
346 LOGGER.debug("deactivate currentThreadCount: " + currentThreadCount.get().get());
347 }
348 }
349
350 /**
351 * Fire MCRSessionEvents.
352 *
353 * This is a common method that fires all types of MCRSessionEvent.
354 *
355 * Mainly for internal use of MCRSession and MCRSessionMgr.
356 *
357 * @param type
358 * type of event
359 * @param concurrentAccessors
360 * number of concurrentThreads (passivateEvent gets 0 for
361 * singleThread)
362 */
363 void fireSessionEvent(MCRSessionEvent.Type type, int concurrentAccessors) {
364 List<MCRSessionListener> listeners = MCRSessionMgr.getListeners();
365 if (listeners.size() == 0) {
366 return;
367 }
368 MCRSessionEvent event = new MCRSessionEvent(this, type, concurrentAccessors);
369 LOGGER.debug(event);
370 MCRSessionMgr.getListenersLock().readLock().lock();
371 MCRSessionListener[] list = listeners.toArray(new MCRSessionListener[listeners.size()]);
372 MCRSessionMgr.getListenersLock().readLock().unlock();
373 for (MCRSessionListener listener : list) {
374 listener.sessionEvent(event);
375 }
376 }
377
378 public long getThisAccessTime() {
379 return thisAccessTime;
380 }
381
382 public long getCreateTime() {
383 return createTime;
384 }
385
386 public Principal getUserPrincipal() {
387 MCRServletJob job = (MCRServletJob) get("MCRServletJob");
388 if (job == null)
389 return null;
390 return job.getRequest().getUserPrincipal();
391 }
392
393 public boolean isPrincipalInRole(String role) {
394 Principal p = getUserPrincipal();
395 if (p == null)
396 return false;
397 MCRServletJob job = (MCRServletJob) get("MCRServletJob");
398 if (job == null)
399 return false;
400 return job.getRequest().isUserInRole(role);
401 }
402
403 /**
404 * starts a new database transaction.
405 */
406 public void beginTransaction() {
407 if (dataBaseAccess)
408 transaction.set(MCRHIBConnection.instance().getSession().beginTransaction());
409 }
410
411 /**
412 * commits the database transaction.
413 * Commit is only done if {@link #isTransactionActive()} returns true.
414 */
415 public void commitTransaction() {
416 if (isTransactionActive()) {
417 transaction.get().commit();
418 beginTransaction();
419 MCRHIBConnection.instance().getSession().clear();
420 transaction.get().commit();
421 transaction.remove();
422 }
423 }
424
425 /**
426 * forces the database transaction to roll back.
427 * Roll back is only performed if {@link #isTransactionActive()} returns true.
428 */
429 public void rollbackTransaction() {
430 if (isTransactionActive()) {
431 transaction.get().rollback();
432 MCRHIBConnection.instance().getSession().close();
433 transaction.remove();
434 }
435 }
436
437 /**
438 * Is the transaction still alive?
439 * @return true if the transaction is still alive
440 */
441 public boolean isTransactionActive() {
442 return dataBaseAccess && transaction.get() != null && transaction.get().isActive();
443 }
444
445 }