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: package org.millstone.webadapter;
038:
039: import java.io.ByteArrayInputStream;
040: import java.io.ByteArrayOutputStream;
041: import java.io.IOException;
042: import java.io.InputStream;
043: import java.lang.ref.SoftReference;
044: import java.net.URL;
045: import java.net.URLConnection;
046: import java.util.Collection;
047: import java.util.HashMap;
048: import java.util.Iterator;
049: import java.util.LinkedList;
050: import java.util.Map;
051:
052: import javax.servlet.ServletContext;
053:
054: /** Theme source for reading themes from a JAR archive.
055: * At this time only jar files are supported and an archive
056: * may not contain any recursive archives.
057: * @author IT Mill Ltd.
058: * @version 3.1.1
059: * @since 3.0
060: */
061: public class ServletThemeSource implements ThemeSource {
062:
063: private ServletContext context;
064: private Theme theme;
065: private String path;
066: private WebAdapterServlet webAdapterServlet;
067: private Cache resourceCache = new Cache();
068:
069: /** Collection of subdirectory entries */
070: private Collection subdirs = new LinkedList();
071: private URL descFile;
072:
073: /** Creates a new instance of ThemeRepository by reading the themes
074: * from a local directory.
075: * @param file Path to the JAR archive .
076: * @param path Path inside the archive to be processed.
077: * @throws FileNotFoundException if no theme files are found
078: */
079: public ServletThemeSource(ServletContext context,
080: WebAdapterServlet webAdapterServlet, String path)
081: throws IOException, ThemeException {
082:
083: this .theme = null;
084: this .webAdapterServlet = webAdapterServlet;
085: this .context = context;
086:
087: // Format path
088: this .path = path;
089: if ((this .path.length() > 0) && !this .path.endsWith("/")) {
090: this .path = this .path + "/";
091: }
092: if ((this .path.length() > 0) && !this .path.startsWith("/")) {
093: this .path = "/" + this .path;
094: }
095:
096: // Load description file
097: this .descFile = context.getResource(this .path
098: + Theme.DESCRIPTIONFILE);
099: InputStream entry = context.getResourceAsStream(this .path
100: + Theme.DESCRIPTIONFILE);
101: try {
102: if (entry != null) {
103: try {
104: this .theme = new Theme(entry);
105: } catch (Exception e) {
106: throw new ThemeException(
107: "ServletThemeSource: Failed to load '"
108: + path + "': " + e);
109: }
110: entry.close();
111:
112: // Debug info
113: if (webAdapterServlet.isDebugMode()) {
114: Log.debug("Added ServletThemeSource: " + this .path);
115: }
116:
117: } else {
118: throw new IllegalArgumentException(
119: "ServletThemeSource: Invalid theme resource: "
120: + path);
121: }
122: } finally {
123: if (entry != null)
124: entry.close();
125: }
126: }
127:
128: /**
129: * @see org.millstone.webadapter.ThemeSource#getXSLStreams(Theme, WebBrowser)
130: */
131: public Collection getXSLStreams(Theme theme, WebBrowser type)
132: throws ThemeException {
133: Collection xslFiles = new LinkedList();
134:
135: // If this directory contains a theme
136: // return XSL from this theme
137: if (this .theme != null) {
138:
139: if (webAdapterServlet.isDebugMode()) {
140: Log.info("ServletThemeSource: Loading theme: " + theme);
141: }
142:
143: // Reload the description file
144: InputStream entry = context.getResourceAsStream(this .path
145: + Theme.DESCRIPTIONFILE);
146: try {
147: if (entry != null) {
148: this .theme = new Theme(entry);
149: }
150: } catch (Exception e) {
151: throw new ThemeException(
152: "ServletThemeSource: Failed to load '" + path
153: + "': " + e);
154: } finally {
155: if (entry != null)
156: try {
157: entry.close();
158: } catch (IOException ignored) {
159: }
160: }
161:
162: Collection fileNames = theme.getFileNames(type);
163: // Add all XSL file streams
164: for (Iterator i = fileNames.iterator(); i.hasNext();) {
165: String entryName = (String) i.next();
166: entry = context
167: .getResourceAsStream((this .path + entryName));
168: xslFiles.add(new XSLStream(entryName, entry));
169: }
170:
171: }
172: return xslFiles;
173: }
174:
175: /** Return modication time of the description file.
176: * @see org.millstone.webadapter.ThemeSource#getModificationTime()
177: */
178: public long getModificationTime() {
179: long modTime = 0;
180: try {
181: URLConnection conn = this .descFile.openConnection();
182: modTime = conn.getLastModified();
183: } catch (Exception ignored) {
184: // In case of exceptions, return zero
185: }
186: return modTime;
187: }
188:
189: /**
190: * @see org.millstone.webadapter.ThemeSource#getResource(String)
191: */
192: public InputStream getResource(String resourceId)
193: throws ThemeSource.ThemeException {
194:
195: // Check the id
196: String name = this .getName();
197: int namelen = name.length();
198: if (resourceId == null || !resourceId.startsWith(name + "/")
199: || resourceId.length() <= (namelen + 1)) {
200: return null;
201: }
202:
203: // Find the resource
204: String streamName = this .path
205: + resourceId.substring(namelen + 1);
206: InputStream stream = context.getResourceAsStream(streamName);
207: if (stream != null)
208: try {
209:
210: // Try cache
211: byte[] data = (byte[]) resourceCache.get(stream);
212: if (data != null)
213: return new ByteArrayInputStream(data);
214:
215: // Read data
216: int bufSize = 1024;
217: ByteArrayOutputStream out = new ByteArrayOutputStream(
218: bufSize);
219: byte[] buf = new byte[bufSize];
220: int n = 0;
221: while ((n = stream.read(buf)) >= 0) {
222: out.write(buf, 0, n);
223: }
224: try {
225: stream.close();
226: } catch (IOException ignored) {
227: }
228: data = out.toByteArray();
229:
230: // Cache data
231: resourceCache.put(stream, data);
232: return new ByteArrayInputStream(data);
233: } catch (IOException e) {
234: }
235:
236: throw new ThemeSource.ThemeException("Resource " + resourceId
237: + " not found.");
238: }
239:
240: /**
241: * @see org.millstone.webadapter.ThemeSource#getThemes()
242: */
243: public Collection getThemes() {
244: Collection themes = new LinkedList();
245: if (this .theme != null) {
246: themes.add(this .theme);
247: }
248: return themes;
249: }
250:
251: /**
252: * @see org.millstone.webadapter.ThemeSource#getName()
253: */
254: public String getName() {
255: return this .theme.getName();
256: }
257:
258: /**
259: * @see org.millstone.webadapter.ThemeSource#getThemeByName(String)
260: */
261: public Theme getThemeByName(String name) {
262: Collection themes = this .getThemes();
263: for (Iterator i = themes.iterator(); i.hasNext();) {
264: Theme t = (Theme) i.next();
265: if (name != null && name.equals(t.getName()))
266: return t;
267: }
268: return null;
269: }
270:
271: /**
272: * @author IT Mill Ltd.
273: * @version 3.1.1
274: * @since 3.0
275: */
276: private class Cache {
277:
278: private Map data = new HashMap();
279:
280: public void put(Object key, Object value) {
281: data.put(key, new SoftReference(new CacheItem(value, key
282: .toString())));
283: }
284:
285: public Object get(Object key) {
286: SoftReference ref = (SoftReference) data.get(key);
287: if (ref != null)
288: return ((CacheItem) ref.get()).getData();
289: return null;
290: }
291:
292: public void clear() {
293: data.clear();
294: }
295: }
296:
297: /**
298: * @author IT Mill Ltd.
299: * @version 3.1.1
300: * @since 3.0
301: */
302: private class CacheItem {
303:
304: private Object data;
305: private String name;
306:
307: public CacheItem(Object data, String name) {
308: this .name = name;
309: this .data = data;
310: }
311:
312: public Object getData() {
313: return this .data;
314: };
315:
316: public void finalize() throws Throwable {
317: this.data = null;
318: this.name = null;
319: super.finalize();
320: }
321:
322: }
323:
324: }
|