001: /*
002: * Context.java
003: *
004: * Version: $Revision: 2104 $
005: *
006: * Date: $Date: 2007-07-27 10:54:35 -0500 (Fri, 27 Jul 2007) $
007: *
008: * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040: package org.dspace.core;
041:
042: import java.sql.Connection;
043: import java.sql.SQLException;
044: import java.util.ArrayList;
045: import java.util.HashMap;
046: import java.util.Iterator;
047: import java.util.List;
048: import java.util.Locale;
049: import java.util.Map;
050:
051: import org.apache.log4j.Logger;
052: import org.dspace.eperson.EPerson;
053: import org.dspace.eperson.Group;
054: import org.dspace.event.Event;
055: import org.dspace.event.EventManager;
056: import org.dspace.event.Dispatcher;
057: import org.dspace.storage.rdbms.DatabaseManager;
058:
059: /**
060: * Class representing the context of a particular DSpace operation. This stores
061: * information such as the current authenticated user and the database
062: * connection being used.
063: * <P>
064: * Typical use of the context object will involve constructing one, and setting
065: * the current user if one is authenticated. Several operations may be performed
066: * using the context object. If all goes well, <code>complete</code> is called
067: * to commit the changes and free up any resources used by the context. If
068: * anything has gone wrong, <code>abort</code> is called to roll back any
069: * changes and free up the resources.
070: * <P>
071: * The context object is also used as a cache for CM API objects.
072: *
073: *
074: * @version $Revision: 2104 $
075: */
076: public class Context {
077: private static final Logger log = Logger.getLogger(Context.class);
078:
079: /** Database connection */
080: private Connection connection;
081:
082: /** Current user - null means anonymous access */
083: private EPerson currentUser;
084:
085: /** Current Locale */
086: private Locale currentLocale;
087:
088: /** Extra log info */
089: private String extraLogInfo;
090:
091: /** Indicates whether authorisation subsystem should be ignored */
092: private boolean ignoreAuth;
093:
094: /** Object cache for this context */
095: private Map objectCache;
096:
097: /** Group IDs of special groups user is a member of */
098: private List specialGroups;
099:
100: /** Content events */
101: private List<Event> events = null;
102:
103: /** Event dispatcher name */
104: private String dispName = null;
105:
106: /**
107: * Construct a new context object. A database connection is opened. No user
108: * is authenticated.
109: *
110: * @exception SQLException
111: * if there was an error obtaining a database connection
112: */
113: public Context() throws SQLException {
114: // Obtain a non-auto-committing connection
115: connection = DatabaseManager.getConnection();
116: connection.setAutoCommit(false);
117:
118: currentUser = null;
119: currentLocale = I18nUtil.DEFAULTLOCALE;
120: extraLogInfo = "";
121: ignoreAuth = false;
122:
123: objectCache = new HashMap();
124: specialGroups = new ArrayList();
125: }
126:
127: /**
128: * Get the database connection associated with the context
129: *
130: * @return the database connection
131: */
132: public Connection getDBConnection() {
133: return connection;
134: }
135:
136: /**
137: * Set the current user. Authentication must have been performed by the
138: * caller - this call does not attempt any authentication.
139: *
140: * @param user
141: * the new current user, or <code>null</code> if no user is
142: * authenticated
143: */
144: public void setCurrentUser(EPerson user) {
145: currentUser = user;
146: }
147:
148: /**
149: * Get the current (authenticated) user
150: *
151: * @return the current user, or <code>null</code> if no user is
152: * authenticated
153: */
154: public EPerson getCurrentUser() {
155: return currentUser;
156: }
157:
158: /**
159: * Gets the current Locale
160: *
161: * @return Locale
162: * the current Locale
163: */
164: public Locale getCurrentLocale() {
165: return currentLocale;
166: }
167:
168: /**
169: * set the current Locale
170: *
171: * @param Locale
172: * the current Locale
173: */
174: public void setCurrentLocale(Locale locale) {
175: currentLocale = locale;
176: }
177:
178: /**
179: * Find out if the authorisation system should be ignored for this context.
180: *
181: * @return <code>true</code> if authorisation should be ignored for this
182: * session.
183: */
184: public boolean ignoreAuthorization() {
185: return ignoreAuth;
186: }
187:
188: /**
189: * Specify whether the authorisation system should be ignored for this
190: * context. This should be used sparingly.
191: *
192: * @param b
193: * if <code>true</code>, authorisation should be ignored for
194: * this session.
195: */
196: public void setIgnoreAuthorization(boolean b) {
197: ignoreAuth = b;
198: }
199:
200: /**
201: * Set extra information that should be added to any message logged in the
202: * scope of this context. An example of this might be the session ID of the
203: * current Web user's session:
204: * <P>
205: * <code>setExtraLogInfo("session_id="+request.getSession().getId());</code>
206: *
207: * @param info
208: * the extra information to log
209: */
210: public void setExtraLogInfo(String info) {
211: extraLogInfo = info;
212: }
213:
214: /**
215: * Get extra information to be logged with message logged in the scope of
216: * this context.
217: *
218: * @return the extra log info - guaranteed non- <code>null</code>
219: */
220: public String getExtraLogInfo() {
221: return extraLogInfo;
222: }
223:
224: /**
225: * Close the context object after all of the operations performed in the
226: * context have completed succesfully. Any transaction with the database is
227: * committed.
228: *
229: * @exception SQLException
230: * if there was an error completing the database transaction
231: * or closing the connection
232: */
233: public void complete() throws SQLException {
234: // FIXME: Might be good not to do a commit() if nothing has actually
235: // been written using this connection
236: try {
237: // Commit any changes made as part of the transaction
238: commit();
239: } finally {
240: // Free the connection
241: DatabaseManager.freeConnection(connection);
242: connection = null;
243: }
244: }
245:
246: /**
247: * Commit any transaction that is currently in progress, but do not close
248: * the context.
249: *
250: * @exception SQLException
251: * if there was an error completing the database transaction
252: * or closing the connection
253: */
254: public void commit() throws SQLException {
255: // Commit any changes made as part of the transaction
256: Dispatcher dispatcher = null;
257:
258: try {
259: if (events != null) {
260:
261: if (dispName == null) {
262: dispName = EventManager.DEFAULT_DISPATCHER;
263: }
264:
265: dispatcher = EventManager.getDispatcher(dispName);
266: connection.commit();
267: dispatcher.dispatch(this );
268: } else {
269: connection.commit();
270: }
271:
272: } finally {
273: events = null;
274: if (dispatcher != null) {
275: EventManager.returnDispatcher(dispName, dispatcher);
276: }
277: }
278:
279: }
280:
281: /**
282: * Select an event dispatcher, <code>null</code> selects the default
283: *
284: */
285: public void setDispatcher(String dispatcher) {
286: if (log.isDebugEnabled()) {
287: log.debug(this .toString() + ": setDispatcher(\""
288: + dispatcher + "\")");
289: }
290: dispName = dispatcher;
291: }
292:
293: /**
294: * Add an event to be dispatched when this context is committed.
295: *
296: * @param event
297: */
298: public void addEvent(Event event) {
299: if (events == null) {
300: events = new ArrayList<Event>();
301: }
302:
303: events.add(event);
304: }
305:
306: /**
307: * Get the current event list. If there is a separate list of events from
308: * already-committed operations combine that with current list.
309: *
310: * @return List of all available events.
311: */
312: public List<Event> getEvents() {
313: return events;
314: }
315:
316: /**
317: * Close the context, without committing any of the changes performed using
318: * this context. The database connection is freed. No exception is thrown if
319: * there is an error freeing the database connection, since this method may
320: * be called as part of an error-handling routine where an SQLException has
321: * already been thrown.
322: */
323: public void abort() {
324: try {
325: connection.rollback();
326: } catch (SQLException se) {
327: log.error(se.getMessage());
328: se.printStackTrace();
329: } finally {
330: DatabaseManager.freeConnection(connection);
331: connection = null;
332: events = null;
333: }
334: }
335:
336: /**
337: *
338: * Find out if this context is valid. Returns <code>false</code> if this
339: * context has been aborted or completed.
340: *
341: * @return <code>true</code> if the context is still valid, otherwise
342: * <code>false</code>
343: */
344: public boolean isValid() {
345: // Only return true if our DB connection is live
346: return (connection != null);
347: }
348:
349: /**
350: * Store an object in the object cache.
351: *
352: * @param objectClass
353: * Java Class of object to check for in cache
354: * @param id
355: * ID of object in cache
356: *
357: * @return the object from the cache, or <code>null</code> if it's not
358: * cached.
359: */
360: public Object fromCache(Class objectClass, int id) {
361: String key = objectClass.getName() + id;
362:
363: return objectCache.get(key);
364: }
365:
366: /**
367: * Store an object in the object cache.
368: *
369: * @param o
370: * the object to store
371: * @param id
372: * the object's ID
373: */
374: public void cache(Object o, int id) {
375: String key = o.getClass().getName() + id;
376: objectCache.put(key, o);
377: }
378:
379: /**
380: * Remove an object from the object cache.
381: *
382: * @param o
383: * the object to remove
384: * @param id
385: * the object's ID
386: */
387: public void removeCached(Object o, int id) {
388: String key = o.getClass().getName() + id;
389: objectCache.remove(key);
390: }
391:
392: /**
393: * Remove all the objects from the object cache
394: */
395: public void clearCache() {
396: objectCache.clear();
397: }
398:
399: /**
400: * set membership in a special group
401: *
402: * @param groupID
403: * special group's ID
404: */
405: public void setSpecialGroup(int groupID) {
406: specialGroups.add(new Integer(groupID));
407:
408: // System.out.println("Added " + groupID);
409: }
410:
411: /**
412: * test if member of special group
413: *
414: * @param groupID
415: * ID of special group to test
416: * @return true if member
417: */
418: public boolean inSpecialGroup(int groupID) {
419: if (specialGroups.contains(new Integer(groupID))) {
420: // System.out.println("Contains " + groupID);
421: return true;
422: }
423:
424: return false;
425: }
426:
427: /**
428: * gets an array of all of the special groups that current user is a member
429: * of
430: *
431: * @return
432: * @throws SQLException
433: */
434: public Group[] getSpecialGroups() throws SQLException {
435: List myGroups = new ArrayList();
436:
437: Iterator i = specialGroups.iterator();
438:
439: while (i.hasNext()) {
440: myGroups.add(Group.find(this , ((Integer) i.next())
441: .intValue()));
442: }
443:
444: return (Group[]) myGroups.toArray(new Group[0]);
445: }
446:
447: protected void finalize() {
448: /*
449: * If a context is garbage-collected, we roll back and free up the
450: * database connection if there is one.
451: */
452: if (connection != null) {
453: abort();
454: }
455: }
456: }
|