001: /* *************************************************************************
002:
003: Millstone(TM)
004: Open Sourced User Interface Library for
005: Internet Development with Java
006:
007: Millstone is a registered trademark of IT Mill Ltd
008: Copyright (C) 2000-2005 IT Mill Ltd
009:
010: *************************************************************************
011:
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the GNU Lesser General Public
014: license version 2.1 as published by the Free Software Foundation.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: *************************************************************************
026:
027: For more information, contact:
028:
029: IT Mill Ltd phone: +358 2 4802 7180
030: Ruukinkatu 2-4 fax: +358 2 4802 7181
031: 20540, Turku email: info@itmill.com
032: Finland company www: www.itmill.com
033:
034: Primary source for MillStone information and releases: www.millstone.org
035:
036: ********************************************************************** */
037:
038: package org.millstone.webadapter;
039:
040: import java.util.Date;
041: import java.util.HashMap;
042: import java.util.HashSet;
043: import java.util.LinkedList;
044: import java.util.Map;
045: import java.util.Iterator;
046:
047: /** Class implementing the MillStone WebAdapter UIDLTransformer Factory.
048: * The factory creates and maintains a pool of transformers that are used
049: * for transforming UIDL to HTML.
050: *
051: * @author IT Mill Ltd.
052: * @version 3.1.1
053: * @since 3.0
054: */
055:
056: public class UIDLTransformerFactory {
057:
058: /** Time between repository modified queries. */
059: private static final int CACHE_CHECK_INTERVAL_MILLIS = 5 * 1000;
060:
061: /** The time transformers are cached by default*/
062: private static final long DEFAULT_TRANSFORMER_CACHETIME = 60 * 60 * 1000;
063:
064: /** Maximum number of transformers in use */
065: private int maxConcurrentTransformers = 1;
066:
067: /** Last time theme modification time was checked */
068: private long lastModificationCheckTime = 0;
069:
070: /** Last time theme source was modified */
071: private long themeSourceModificationTime = 0;
072:
073: /** How long to cache transformers. */
074: private long cacheTime = DEFAULT_TRANSFORMER_CACHETIME;
075:
076: /** Spool manager thread */
077: private SpoolManager spoolManager;
078:
079: private Map transformerSpool = new HashMap();
080: private ThemeSource themeSource;
081: private WebAdapterServlet webAdapterServlet;
082: private int transformerCount = 0;
083: private int transformersInUse = 0;
084:
085: /** Constructor for transformer factory.
086: * Method UIDLTransformerFactory.
087: * @param themeSource Theme source to be used for themes.
088: * @param webAdapterServlet The Adapter servlet.
089: * @param maxConcurrentTransformers Maximum number of concurrent themes in use.
090: * @param cacheTime Time to cache the transformers.
091: */
092: public UIDLTransformerFactory(ThemeSource themeSource,
093: WebAdapterServlet webAdapterServlet,
094: int maxConcurrentTransformers, long cacheTime) {
095: this .webAdapterServlet = webAdapterServlet;
096: if (themeSource == null)
097: throw new NullPointerException();
098: this .themeSource = themeSource;
099: this .themeSourceModificationTime = themeSource
100: .getModificationTime();
101: this .maxConcurrentTransformers = maxConcurrentTransformers;
102: if (cacheTime >= 0)
103: this .cacheTime = cacheTime;
104: this .spoolManager = new SpoolManager(this .cacheTime);
105: this .spoolManager.setDaemon(true);
106: //Enable manager only if time > 0
107: if (this .cacheTime > 0)
108: this .spoolManager.start();
109: }
110:
111: /** Get new transformer of the specified type
112: * @param type Type of the requested transformer.
113: * @param variableMap WebVariable map used by the transformer
114: * @return Created new transformer.
115: */
116: public synchronized UIDLTransformer getTransformer(
117: UIDLTransformerType type) throws UIDLTransformerException {
118:
119: while (transformersInUse >= maxConcurrentTransformers) {
120: try {
121: this .wait();
122: } catch (InterruptedException e) {
123: return null;
124: }
125: }
126:
127: // Get list of transformers for this type
128: TransformerList list = (TransformerList) this .transformerSpool
129: .get(type);
130:
131: // Check the modification time between fixed intervals
132: long now = System.currentTimeMillis();
133: if (now - CACHE_CHECK_INTERVAL_MILLIS > this .lastModificationCheckTime) {
134:
135: this .lastModificationCheckTime = now;
136:
137: // Check if the theme source has been modified and flush
138: // list if necessary
139: long lastmod = this .themeSource.getModificationTime();
140: if (list != null
141: && this .themeSourceModificationTime < lastmod) {
142: if (webAdapterServlet.isDebugMode()) {
143: Log
144: .info("Theme source modified since "
145: + new Date(
146: this .themeSourceModificationTime)
147: .toString()
148: + ". Reloading...");
149: }
150: // Force refresh by removing from spool
151: this .transformerSpool.clear();
152: list = null;
153: this .transformerCount = 0;
154: this .themeSourceModificationTime = lastmod;
155: }
156: }
157:
158: UIDLTransformer t = null;
159:
160: if (list != null && !list.isEmpty()) {
161: // If available, return the first available transformer
162: t = (UIDLTransformer) list.removeFirst();
163: if (webAdapterServlet.isDebugMode()) {
164: Log.info("Reserved existing transformer: " + type);
165: }
166: } else {
167:
168: // Create new transformer and return it. Transformers are added to
169: // spool when they are released.
170: t = new UIDLTransformer(type, themeSource,
171: webAdapterServlet);
172: transformerCount++;
173: if (webAdapterServlet.isDebugMode()) {
174: Log.info("Created new transformer (" + transformerCount
175: + "):" + type);
176: }
177:
178: // Create new list, if not found
179: if (list == null) {
180: list = new TransformerList(type);
181: this .transformerSpool.put(type, list);
182: if (webAdapterServlet.isDebugMode()) {
183: Log.info("Created new type: " + type);
184: }
185: }
186:
187: }
188: transformersInUse++;
189: return t;
190: }
191:
192: /** Recycle a used transformer back to spool.
193: * One must guarantee not to use the transformer after it have been released.
194: * @param transformer UIDLTransformer to be recycled
195: */
196: public synchronized void releaseTransformer(
197: UIDLTransformer transformer) {
198:
199: try {
200: // Reset the transformer before returning it to spool
201: transformer.reset();
202:
203: // Recycle the transformer back to spool
204: TransformerList list = (TransformerList) this .transformerSpool
205: .get(transformer.getTransformerType());
206: if (list != null) {
207: list.add(transformer);
208: if (webAdapterServlet.isDebugMode()) {
209: Log.info("Released transformer: "
210: + transformer.getTransformerType()
211: + "(In use: " + transformersInUse
212: + ",Spooled: " + list.size() + ")");
213: }
214: list.lastUsed = System.currentTimeMillis();
215: } else {
216: Log
217: .info("Tried to release non-existing transformer. Ignoring."
218: + " (Type:"
219: + transformer.getTransformerType()
220: + ")");
221: }
222: } finally {
223: if (transformersInUse > 0)
224: transformersInUse--;
225: notifyAll();
226: }
227: }
228:
229: private class TransformerList {
230:
231: private UIDLTransformerType type = null;
232: private LinkedList list = new LinkedList();
233: private long lastUsed = 0;
234:
235: public TransformerList(UIDLTransformerType type) {
236: this .type = type;
237: }
238:
239: public void add(UIDLTransformer transformer) {
240: list.add(transformer);
241: }
242:
243: public UIDLTransformer removeFirst() {
244: return (UIDLTransformer) ((LinkedList) list).removeFirst();
245: }
246:
247: public boolean isEmpty() {
248: return list.isEmpty();
249: }
250:
251: public int size() {
252: return list.size();
253: }
254: }
255:
256: private synchronized void removeUnusedTransformers() {
257: long currentTime = System.currentTimeMillis();
258: HashSet keys = new HashSet();
259: keys.addAll(this .transformerSpool.keySet());
260: for (Iterator i = keys.iterator(); i.hasNext();) {
261: UIDLTransformerType type = (UIDLTransformerType) i.next();
262: TransformerList l = (TransformerList) this .transformerSpool
263: .get(type);
264: if (l != null) {
265: if (l.lastUsed > 0
266: && l.lastUsed < (currentTime - this .cacheTime)) {
267: if (webAdapterServlet.isDebugMode()) {
268: Log.info("Removed transformer: " + type
269: + " Not used since "
270: + new Date(l.lastUsed));
271: }
272: this .transformerSpool.remove(type);
273: }
274: }
275: }
276: }
277:
278: /** Class for periodically remove unused transformers from memory.
279: * @author IT Mill Ltd.
280: * @version 3.1.1
281: * @since 3.0
282: */
283: protected class SpoolManager extends Thread {
284:
285: long refreshTime;
286:
287: public SpoolManager(long refreshTime) {
288: super ("UIDLTransformerFactory.SpoolManager");
289: this .refreshTime = refreshTime;
290: }
291:
292: public void run() {
293: while (true) {
294: try {
295: sleep(refreshTime);
296: } catch (Exception e) {
297: }
298: removeUnusedTransformers();
299: }
300: }
301: }
302:
303: }
|