KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > asset > AssetExternalizerImpl


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.tapestry.asset;
16
17 import java.io.BufferedInputStream JavaDoc;
18 import java.io.BufferedOutputStream JavaDoc;
19 import java.io.File JavaDoc;
20 import java.io.FileOutputStream JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.io.InputStream JavaDoc;
23 import java.io.OutputStream JavaDoc;
24 import java.net.URL JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.Map JavaDoc;
27
28 import javax.servlet.ServletContext JavaDoc;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.hivemind.ApplicationRuntimeException;
32 import org.apache.hivemind.ClassResolver;
33 import org.apache.tapestry.Tapestry;
34 import org.apache.tapestry.engine.IPropertySource;
35 import org.apache.tapestry.util.StringSplitter;
36
37 /**
38  * Implementation of the {@link org.apache.tapestry.asset.AssetExternalizer}service interface.
39  * Responsible for copying assets from the classpath to an external directory that is visible to the
40  * web server. The externalizer is stored inside the {@link ServletContext}as a named attribute.
41  * <p>
42  * The externalizer uses the name
43  * <code>org.apache.tapestry.AssetExternalizer.<i>application name</i>
44  * </code>. It configures
45  * itself using two additional properties (searching in
46  * {@link org.apache.tapestry.IEngine#getPropertySource()}. <table border=1>
47  * <tr>
48  * <th>Parameter</th>
49  * <th>Description</th>
50  * </tr>
51  * <tr valign=top>
52  * <td><code>org.apache.tapestry.asset.dir</code></td>
53  * <td>The directory to which assets will be copied.</td>
54  * </tr>
55  * <tr valign=top>
56  * <td><code>org.apache.tapestry.asset.URL</code></td>
57  * <td>The corresponding URL for the asset directory.</td>
58  * </tr>
59  * </table>
60  * <p>
61  * If either of these parameters is null, then no externalization occurs. Private assets will still
62  * be available, just less efficiently, as the application will be invoked via its servlet and,
63  * ultimately, the {@link AssetService}will need to retrieve the asset.
64  * <p>
65  * Assets maintain thier directory structure when copied. For example, an asset with a resource path
66  * of <code>/com/skunkworx/Banner.gif</code> would be copied to the file system as
67  * <code><i>dir</i>/com/skunkworx/Banner.gif</code> and would have a URL of
68  * <code><i>URL</i>/com/skunkworx/Banner.gif</code>.
69  * <p>
70  * The externalizer will create any directories as needed.
71  * <p>
72  * The externalizer will not overwrite existing files. When a new version of the application is
73  * deployed with changed assets, there are two deployment stategies:
74  * <ul>
75  * <li>Delete the existing asset directory and allow the externalizer to recreate and repopulate
76  * it.
77  * <li>Change the asset directory and URL, allowing the old and new assets to exist side-by-side.
78  * </ul>
79  * <p>
80  * When using the second approach, it is best to use a directory that has a version number in it,
81  * for example, <code>D:/inetpub/assets/0</code> mapped to the URL <code>/assets/0</code>. When
82  * a new version of the application is deployed, the trailing version number is incremented from 0
83  * to 1.
84  *
85  * @author Howard Lewis Ship
86  */

87
88 public class AssetExternalizerImpl implements AssetExternalizer
89 {
90     /** @since 4.0 */
91     private Log _log;
92
93     private ClassResolver _resolver;
94
95     /** @since 4.0 */
96     private IPropertySource _propertySource;
97
98     private File JavaDoc _assetDir;
99
100     private String JavaDoc _URL;
101
102     /**
103      * A map from resource path (as a String) to final URL (as a String).
104      */

105
106     private Map JavaDoc _resources = new HashMap JavaDoc();
107
108     private static final int BUFFER_SIZE = 2048;
109
110     public void initializeService()
111     {
112         String JavaDoc directory = _propertySource.getPropertyValue("org.apache.tapestry.asset.dir");
113
114         if (directory == null)
115             return;
116
117         _URL = _propertySource.getPropertyValue("org.apache.tapestry.asset.URL");
118
119         if (_URL == null)
120             return;
121
122         _assetDir = new File JavaDoc(directory);
123
124         _log.debug("Initialized with directory " + _assetDir + " mapped to " + _URL);
125     }
126
127     protected void externalize(String JavaDoc resourcePath) throws IOException JavaDoc
128     {
129         if (_log.isDebugEnabled())
130             _log.debug("Externalizing " + resourcePath);
131
132         File JavaDoc file = _assetDir;
133
134         // Resources are always split by the unix seperator, even on Win32.
135

136         StringSplitter splitter = new StringSplitter('/');
137
138         String JavaDoc[] path = splitter.splitToArray(resourcePath);
139
140         // The path is expected to start with a leading slash, but the StringSplitter
141
// will ignore that leading slash.
142

143         for (int i = 0; i < path.length - 1; i++)
144         {
145             // Doing it this way makes sure the path seperators are right.
146

147             file = new File JavaDoc(file, path[i]);
148         }
149
150         // Make sure the directories exist.
151

152         file.mkdirs();
153
154         file = new File JavaDoc(file, path[path.length - 1]);
155
156         // If the file exists, then assume all is well. This is OK for development,
157
// but there may be multithreading (or even multiprocess) race conditions
158
// around the creation of the file.
159

160         if (file.exists())
161             return;
162
163         // Get the resource and copy it to the file.
164

165         URL JavaDoc inputURL = _resolver.getResource(resourcePath);
166         if (inputURL == null)
167             throw new IOException JavaDoc(Tapestry.format("missing-resource", resourcePath));
168
169         InputStream JavaDoc in = null;
170         OutputStream JavaDoc out = null;
171
172         try
173         {
174             in = new BufferedInputStream JavaDoc(inputURL.openStream());
175
176             out = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(file));
177
178             byte[] buffer = new byte[BUFFER_SIZE];
179
180             while (true)
181             {
182                 int bytesRead = in.read(buffer, 0, BUFFER_SIZE);
183                 if (bytesRead < 0)
184                     break;
185
186                 out.write(buffer, 0, bytesRead);
187             }
188         }
189         finally
190         {
191             close(in);
192             close(out);
193         }
194
195         // The file is copied!
196
}
197
198     private void close(InputStream JavaDoc in)
199     {
200         if (in != null)
201             try
202             {
203                 in.close();
204             }
205             catch (IOException JavaDoc ex)
206             {
207                 // Ignore.
208
}
209     }
210
211     private void close(OutputStream JavaDoc out)
212     {
213         if (out != null)
214
215             try
216             {
217                 out.close();
218             }
219             catch (IOException JavaDoc ex)
220             {
221                 // Ignore
222
}
223     }
224
225     /**
226      * Gets the URL to a private resource. If the resource was previously copied out of the
227      * classpath, the previously generated URL is returned.
228      * <p>
229      * If the asset directory and URL are not configured, then returns null.
230      * <p>
231      * Otherwise, the asset is copied out to the asset directory, the URL is constructed (and
232      * recorded for later) and the URL is returned.
233      * <p>
234      * This method is not explicitly synchronized but should work multi-threaded. It synchronizes on
235      * the internal <code>Map</code> used to map resource paths to URLs.
236      *
237      * @param resourcePath
238      * The full path of the resource within the classpath. This is expected to include a
239      * leading slash. For example: <code>/com/skunkworx/Banner.gif</code>.
240      */

241
242     public String JavaDoc getURL(String JavaDoc resourcePath)
243     {
244         if (_assetDir == null)
245             return null;
246
247         synchronized (_resources)
248         {
249             String JavaDoc result = (String JavaDoc) _resources.get(resourcePath);
250
251             if (result != null)
252                 return result;
253
254             try
255             {
256                 externalize(resourcePath);
257             }
258             catch (IOException JavaDoc ex)
259             {
260                 throw new ApplicationRuntimeException(Tapestry.format(
261                         "AssetExternalizer.externalize-failure",
262                         resourcePath,
263                         _assetDir), ex);
264             }
265
266             result = _URL + resourcePath;
267
268             _resources.put(resourcePath, result);
269
270             return result;
271         }
272     }
273
274     /** @since 4.0 */
275     public void setLog(Log log)
276     {
277         _log = log;
278     }
279
280     /** @since 4.0 */
281     public void setClassResolver(ClassResolver resolver)
282     {
283         _resolver = resolver;
284     }
285
286     /** since 4.0 */
287     public void setPropertySource(IPropertySource propertySource)
288     {
289         _propertySource = propertySource;
290     }
291 }
Popular Tags