001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/presence/tags/sakai_2-4-1/presence-impl/impl/src/java/org/sakaiproject/presence/impl/BasePresenceService.java $
003: * $Id: BasePresenceService.java 19514 2006-12-14 21:53:05Z josrodri@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.presence.impl;
021:
022: import java.util.Collections;
023: import java.util.Enumeration;
024: import java.util.HashSet;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Set;
028: import java.util.Vector;
029:
030: import org.apache.commons.logging.Log;
031: import org.apache.commons.logging.LogFactory;
032: import org.sakaiproject.api.privacy.PrivacyManager;
033: import org.sakaiproject.entity.api.Entity;
034: import org.sakaiproject.event.api.Event;
035: import org.sakaiproject.event.api.EventTrackingService;
036: import org.sakaiproject.event.api.UsageSession;
037: import org.sakaiproject.event.api.UsageSessionService;
038: import org.sakaiproject.presence.api.PresenceService;
039: import org.sakaiproject.tool.api.Session;
040: import org.sakaiproject.tool.api.SessionBindingEvent;
041: import org.sakaiproject.tool.api.SessionBindingListener;
042: import org.sakaiproject.tool.api.SessionManager;
043: import org.sakaiproject.tool.api.ToolSession;
044: import org.sakaiproject.user.api.UserDirectoryService;
045:
046: /**
047: * <p>
048: * Implements the PresenceService, all but a Storage model.
049: * </p>
050: */
051: public abstract class BasePresenceService implements PresenceService {
052: /** Our log (commons). */
053: private static Log M_log = LogFactory
054: .getLog(BasePresenceService.class);
055:
056: /** SessionState key. */
057: protected final static String SESSION_KEY = "sakai.presence.service";
058:
059: /** Storage. */
060: protected Storage m_storage = null;
061:
062: /**
063: * Allocate a new storage object.
064: *
065: * @return A new storage object.
066: */
067: protected abstract Storage newStorage();
068:
069: /**********************************************************************************************************************************************************************************************************************************************************
070: * Constructors, Dependencies and their setter methods
071: *********************************************************************************************************************************************************************************************************************************************************/
072:
073: /** Dependency: SessionManager */
074: protected SessionManager m_sessionManager = null;
075:
076: /**
077: * Dependency: SessionManager.
078: *
079: * @param service
080: * The SessionManager.
081: */
082: public void setSessionManager(SessionManager service) {
083: m_sessionManager = service;
084: }
085:
086: /** Dependency: UsageSessionService */
087: protected UsageSessionService m_usageSessionService = null;
088:
089: /**
090: * Dependency: UsageSessionService.
091: *
092: * @param service
093: * The UsageSessionService.
094: */
095: public void setUsageSessionService(UsageSessionService service) {
096: m_usageSessionService = service;
097: }
098:
099: /** Dependency: UserDirectoryService */
100: protected UserDirectoryService m_userDirectoryService = null;
101:
102: /**
103: * Dependency: UserDirectoryService.
104: *
105: * @param service
106: * The UserDirectoryService.
107: */
108: public void setUserDirectoryService(UserDirectoryService service) {
109: m_userDirectoryService = service;
110: }
111:
112: /** Dependency: EventTrackingService */
113: protected EventTrackingService m_eventTrackingService = null;
114:
115: /**
116: * Dependency: EventTrackingService.
117: *
118: * @param service
119: * The EventTrackingService.
120: */
121: public void setEventTrackingService(EventTrackingService service) {
122: m_eventTrackingService = service;
123: }
124:
125: /** Dependency: PrivacyManager */
126: protected PrivacyManager m_privacyManager = null;
127:
128: /**
129: * Dependency: PrivacyManager.
130: *
131: * @param service
132: * The PrivacyManager.
133: */
134: public void setPrivacyManager(PrivacyManager service) {
135: m_privacyManager = service;
136: }
137:
138: /** Configuration: milliseconds till a non-refreshed presence entry times out. */
139: protected int m_timeout = 60000;
140:
141: /**
142: * Configuration: SECONDS till a non-refreshed presence entry times out.
143: *
144: * @param value
145: * timeout seconds.
146: */
147: public void setTimeoutSeconds(String value) {
148: try {
149: m_timeout = Integer.parseInt(value) * 1000;
150: } catch (Exception ignore) {
151: }
152: }
153:
154: /**********************************************************************************************************************************************************************************************************************************************************
155: * Init and Destroy
156: *********************************************************************************************************************************************************************************************************************************************************/
157:
158: /**
159: * Final initialization, once all dependencies are set.
160: */
161: public void init() {
162: try {
163: // storage
164: m_storage = newStorage();
165:
166: M_log.info("init()");
167:
168: } catch (Throwable t) {
169: M_log.warn("init(): ", t);
170: }
171: }
172:
173: /**
174: * Returns to uninitialized state.
175: */
176: public void destroy() {
177: m_storage = null;
178: M_log.info("destroy()");
179: }
180:
181: /**********************************************************************************************************************************************************************************************************************************************************
182: * PresenceService implementation
183: *********************************************************************************************************************************************************************************************************************************************************/
184:
185: /**
186: * {@inheritDoc}
187: */
188: public String presenceReference(String id) {
189: return REFERENCE_ROOT + Entity.SEPARATOR + id;
190:
191: } // presenceReference
192:
193: /**
194: * {@inheritDoc}
195: */
196: protected String presenceId(String ref) {
197: String start = presenceReference("");
198: int i = ref.indexOf(start);
199: if (i == -1)
200: return ref;
201: String id = ref.substring(i + start.length());
202: return id;
203:
204: } // presenceId
205:
206: /**
207: * {@inheritDoc}
208: */
209: public void setPresence(String locationId) {
210: if (locationId == null)
211: return;
212:
213: if (!checkPresence(locationId, true)) {
214: // presence relates a usage session (the current one) with a location
215: UsageSession curSession = m_usageSessionService
216: .getSession();
217: if (curSession == null)
218: return;
219:
220: // update the storage
221: m_storage.setPresence(curSession.getId(), locationId);
222:
223: // generate the event
224: Event event = m_eventTrackingService
225: .newEvent(EVENT_PRESENCE,
226: presenceReference(locationId), true);
227: m_eventTrackingService.post(event, curSession);
228:
229: // create a presence for tracking
230:
231: // bind a presence tracking object to the sakai session for auto-cleanup when logout or inactivity invalidates the sakai session
232: Session session = m_sessionManager.getCurrentSession();
233: ToolSession ts = session.getToolSession(SESSION_KEY);
234: Presence p = new Presence(curSession, locationId);
235: ts.setAttribute(locationId, p);
236: }
237:
238: // retire any expired presence
239: checkPresenceForExpiration();
240:
241: } // setPresence
242:
243: /**
244: * {@inheritDoc}
245: */
246: public void removePresence(String locationId) {
247: if (locationId == null)
248: return;
249:
250: if (checkPresence(locationId, false)) {
251: UsageSession curSession = m_usageSessionService
252: .getSession();
253:
254: // tell maintenance
255: m_storage.removePresence(curSession.getId(), locationId);
256:
257: // generate the event
258: Event event = m_eventTrackingService.newEvent(
259: EVENT_ABSENCE, presenceReference(locationId), true);
260: m_eventTrackingService.post(event, curSession);
261:
262: // remove from state
263: Session session = m_sessionManager.getCurrentSession();
264: ToolSession ts = session.getToolSession(SESSION_KEY);
265: Presence p = (Presence) ts.getAttribute(locationId);
266: if (p != null) {
267: p.deactivate();
268: ts.removeAttribute(locationId);
269: }
270: }
271:
272: } // removePresence
273:
274: /**
275: * {@inheritDoc}
276: */
277: public List getPresence(String locationId) {
278: // get the sessions at this location
279: List sessions = m_storage.getSessions(locationId);
280:
281: // sort
282: Collections.sort(sessions);
283:
284: return sessions;
285:
286: } // getPresence
287:
288: /**
289: * {@inheritDoc}
290: */
291: public List getPresentUsers(String locationId) {
292: // get the sessions
293: List sessions = m_storage.getSessions(locationId);
294:
295: // form a list of user ids
296: List userIds = new Vector();
297: for (Iterator i = sessions.iterator(); i.hasNext();) {
298: UsageSession s = (UsageSession) i.next();
299:
300: if (!userIds.contains(s.getUserId())) {
301: userIds.add(s.getUserId());
302: }
303: }
304:
305: // get the users for these ids
306: List users = m_userDirectoryService.getUsers(userIds);
307:
308: // sort
309: Collections.sort(users);
310:
311: return users;
312: }
313:
314: /**
315: * {@inheritDoc}
316: */
317: public List getPresentUsers(String locationId, String siteId) {
318: // get the sessions
319: List sessions = m_storage.getSessions(locationId);
320:
321: // form a list of user ids
322: List userIds = new Vector();
323:
324: for (Iterator i = sessions.iterator(); i.hasNext();) {
325: UsageSession s = (UsageSession) i.next();
326:
327: if (!userIds.contains(s.getUserId())) {
328: userIds.add(s.getUserId());
329: }
330: }
331:
332: Set userIdsSet = m_privacyManager.findViewable("/site/"
333: + siteId, new HashSet(userIds));
334:
335: // get the users for these ids
336: List users = m_userDirectoryService.getUsers(userIdsSet);
337:
338: // sort
339: Collections.sort(users);
340:
341: return users;
342: }
343:
344: /**
345: * {@inheritDoc}
346: */
347: public List getLocations() {
348: List locations = m_storage.getLocations();
349:
350: Collections.sort(locations);
351:
352: return locations;
353:
354: } // getLocations
355:
356: /**
357: * {@inheritDoc}
358: */
359: public String locationId(String site, String page, String tool) {
360: // TODO: remove
361: return "";
362: }
363:
364: /**
365: * {@inheritDoc}
366: */
367: public String getLocationDescription(String location) {
368: // TODO: get a description for a placement!
369: return "location: " + location;
370: }
371:
372: /**
373: * {@inheritDoc}
374: */
375: public int getTimeout() {
376: return m_timeout / 1000;
377: }
378:
379: /**
380: * Check if the current session is present at the location - optionally refreshing it
381: *
382: * @param locationId
383: * The location to check.
384: * @param refresh
385: * If true, refresh the timeout on the presence if found
386: * @return True if the current session is present at that location, false if not.
387: */
388: protected boolean checkPresence(String locationId, boolean refresh) {
389: Session session = m_sessionManager.getCurrentSession();
390: ToolSession ts = session.getToolSession(SESSION_KEY);
391: Presence p = (Presence) ts.getAttribute(locationId);
392:
393: if ((p != null) && refresh) {
394: p.setActive();
395: }
396:
397: return (p != null);
398: }
399:
400: /**
401: * Check current session presences and remove any expired ones
402: */
403: protected void checkPresenceForExpiration() {
404: Session session = m_sessionManager.getCurrentSession();
405: ToolSession ts = session.getToolSession(SESSION_KEY);
406: Enumeration locations = ts.getAttributeNames();
407: while (locations.hasMoreElements()) {
408: String location = (String) locations.nextElement();
409:
410: Presence p = (Presence) ts.getAttribute(location);
411: if (p.isExpired()) {
412: ts.removeAttribute(location);
413: }
414: }
415: }
416:
417: /**********************************************************************************************************************************************************************************************************************************************************
418: * Storage
419: *********************************************************************************************************************************************************************************************************************************************************/
420:
421: protected interface Storage {
422: /**
423: * Add this session id's presence at this location, if not already there.
424: *
425: * @param sessionId
426: * The session id.
427: * @param locationId
428: * The location id.
429: */
430: void setPresence(String sessionId, String locationId);
431:
432: /**
433: * Remove this sessions id's presence at this location.
434: *
435: * @param sessionId
436: * The session id.
437: * @param locationId
438: * The location id.
439: */
440: void removePresence(String sessionId, String locationId);
441:
442: /**
443: * Access the List of UsageSessions present at this location.
444: *
445: * @param locationId
446: * The location id.
447: * @return The List of sessions (UsageSession) present at this location.
448: */
449: List getSessions(String locationId);
450:
451: /**
452: * Access the List of all known location ids.
453: *
454: * @return The List (String) of all known locations.
455: */
456: List getLocations();
457: }
458:
459: /**********************************************************************************************************************************************************************************************************************************************************
460: * Presence
461: *********************************************************************************************************************************************************************************************************************************************************/
462:
463: protected class Presence implements SessionBindingListener {
464: /** The session. */
465: protected UsageSession m_session = null;
466:
467: /** The location id. */
468: protected String m_locationId = null;
469:
470: /** If true, process the unbound. */
471: protected boolean m_active = true;
472:
473: /** Time to expire. */
474: protected long m_expireTime = 0;
475:
476: public Presence(UsageSession session, String locationId) {
477: m_session = session;
478: m_locationId = locationId;
479: m_expireTime = System.currentTimeMillis() + m_timeout;
480: }
481:
482: public void deactivate() {
483: m_active = false;
484: }
485:
486: /**
487: * Reset the timeout based on current activity
488: */
489: public void setActive() {
490: m_expireTime = System.currentTimeMillis() + m_timeout;
491: }
492:
493: /**
494: * Has this presence timed out?
495: *
496: * @return true if expired, false if not.
497: */
498: public boolean isExpired() {
499: return System.currentTimeMillis() > m_expireTime;
500: }
501:
502: /**
503: * {@inheritDoc}
504: */
505: public void valueBound(SessionBindingEvent event) {
506: }
507:
508: /**
509: * {@inheritDoc}
510: */
511: public void valueUnbound(SessionBindingEvent evt) {
512: if (m_active) {
513: m_storage.removePresence(m_session.getId(),
514: m_locationId);
515:
516: // generate the event
517: Event event = m_eventTrackingService.newEvent(
518: EVENT_ABSENCE, presenceReference(m_locationId),
519: true);
520: m_eventTrackingService.post(event, m_session);
521: }
522: }
523: }
524: }
|