001: /**
002: * Copyright 2006 Webmedia Group Ltd.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: **/package org.araneaframework.framework.router;
016:
017: import java.io.Serializable;
018: import java.util.Collections;
019: import java.util.Date;
020: import java.util.HashMap;
021: import java.util.Iterator;
022: import java.util.Map;
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025: import org.araneaframework.Environment;
026: import org.araneaframework.InputData;
027: import org.araneaframework.OutputData;
028: import org.araneaframework.Path;
029: import org.araneaframework.Service;
030: import org.araneaframework.core.Assert;
031: import org.araneaframework.core.StandardEnvironment;
032: import org.araneaframework.framework.ExpiringServiceContext;
033: import org.araneaframework.framework.ManagedServiceContext;
034:
035: /**
036: * Router service that kills child services after specified period of inactivity is over.
037: * This implementation checks for child services whose lifetime has expired only when
038: * servicing request.
039: *
040: * @author Taimo Peelo (taimo@araneaframework.org)
041: */
042: public abstract class BaseExpiringServiceRouterService extends
043: BaseServiceRouterService implements ExpiringServiceContext {
044:
045: private static final Log log = LogFactory
046: .getLog(BaseExpiringServiceRouterService.class);
047: private Map timeCapsules;
048: private Map serviceTTLMap;
049:
050: public Map getServiceTTLMap() {
051: if (serviceTTLMap == null)
052: return null;
053: return Collections.unmodifiableMap(serviceTTLMap);
054: }
055:
056: protected void action(Path path, InputData input, OutputData output)
057: throws Exception {
058: TimeCapsule capsule = null;
059: if (timeCapsules != null) {
060: killExpiredServices(System.currentTimeMillis());
061: capsule = (TimeCapsule) getTimeCapsules().get(
062: getServiceId(input));
063: }
064:
065: serviceTTLMap = null;
066: if (capsule != null) {
067: if (getEnvironment().getEntry(ExpiringServiceContext.class) != null) {
068: serviceTTLMap = ((ExpiringServiceContext) getEnvironment()
069: .getEntry(ExpiringServiceContext.class))
070: .getServiceTTLMap();
071: }
072: if (serviceTTLMap == null) {
073: serviceTTLMap = new HashMap();
074: }
075:
076: serviceTTLMap.put(getKeepAliveKey(), capsule
077: .getTimeToLive());
078: }
079:
080: if (!isKeepAlive(input)) {
081: super .action(path, input, output);
082: } else {
083: if (log.isDebugEnabled())
084: log.debug(Assert.this ToString(this )
085: + " received keepalive for service '"
086: + getServiceId(input).toString() + "'");
087: }
088:
089: if (capsule != null)
090: capsule
091: .setLastActivity(new Long(System
092: .currentTimeMillis()));
093: }
094:
095: protected Environment getChildEnvironment(Object serviceId)
096: throws Exception {
097: Map entries = new HashMap();
098: entries.put(ManagedServiceContext.class,
099: new ServiceRouterContextImpl(serviceId));
100: entries.put(ExpiringServiceContext.class, this );
101: return new StandardEnvironment(super
102: .getChildEnvironment(serviceId), entries);
103: }
104:
105: protected void closeService(Object serviceId) {
106: super .closeService(serviceId);
107: getTimeCapsules().remove(serviceId);
108: }
109:
110: protected void killExpiredServices(long now) {
111: synchronized (getTimeCapsules()) {
112: for (Iterator i = getTimeCapsules().entrySet().iterator(); i
113: .hasNext();) {
114: Map.Entry entry = (Map.Entry) i.next();
115: if (((TimeCapsule) entry.getValue()).isExpired(now)) {
116: super .closeService(entry.getKey());
117: i.remove();
118: if (log.isDebugEnabled())
119: log.debug(Assert.this ToString(this )
120: + " killed expired service '"
121: + entry.getKey().toString() + "'.");
122: }
123: }
124: }
125: }
126:
127: /**
128: * Returns the key which presence in {@link InputData} indicates that request is
129: * keepalive request for this {@link BaseExpiringServiceRouterService}.
130: * @return keepalive key for this {@link BaseExpiringServiceRouterService}
131: */
132: public abstract Object getKeepAliveKey();
133:
134: protected boolean isKeepAlive(InputData input) {
135: return input.getGlobalData().get(getKeepAliveKey()) != null;
136: }
137:
138: private synchronized Map getTimeCapsules() {
139: if (timeCapsules == null)
140: timeCapsules = Collections.synchronizedMap(new HashMap());
141:
142: return timeCapsules;
143: }
144:
145: public static class TimeCapsule implements Serializable {
146: private Long ttl;
147: private Long lastActivity;
148:
149: public TimeCapsule(Long timeToLive) {
150: this .ttl = timeToLive;
151: lastActivity = new Long(new Date().getTime());
152: }
153:
154: public void setLastActivity(Long lastActivity) {
155: this .lastActivity = lastActivity;
156: }
157:
158: public Long getTimeToLive() {
159: return this .ttl;
160: }
161:
162: public boolean isExpired(long time) {
163: return (time > lastActivity.longValue() + ttl.longValue());
164: }
165: }
166:
167: protected class ServiceRouterContextImpl extends
168: BaseServiceRouterService.ServiceRouterContextImpl {
169: protected ServiceRouterContextImpl(Object serviceId) {
170: super (serviceId);
171: }
172:
173: public Service addService(Object id, Service service,
174: Long timeToLive) {
175: Service result = super .addService(id, service);
176: if (timeToLive != null)
177: getTimeCapsules().put(id, new TimeCapsule(timeToLive));
178:
179: return result;
180: }
181: }
182: }
|