001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.jci.examples.serverpages;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.io.PrintWriter;
023: import java.util.HashMap;
024: import java.util.HashSet;
025: import java.util.Iterator;
026: import java.util.Map;
027: import java.util.Set;
028:
029: import javax.servlet.ServletException;
030: import javax.servlet.http.HttpServlet;
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.http.HttpServletResponse;
033:
034: import org.apache.commons.jci.ReloadingClassLoader;
035: import org.apache.commons.jci.compilers.CompilationResult;
036: import org.apache.commons.jci.compilers.JavaCompilerFactory;
037: import org.apache.commons.jci.listeners.CompilingListener;
038: import org.apache.commons.jci.monitor.FilesystemAlterationMonitor;
039: import org.apache.commons.jci.monitor.FilesystemAlterationObserver;
040: import org.apache.commons.jci.problems.CompilationProblem;
041: import org.apache.commons.jci.readers.ResourceReader;
042: import org.apache.commons.jci.stores.MemoryResourceStore;
043: import org.apache.commons.jci.stores.TransactionalResourceStore;
044: import org.apache.commons.jci.utils.ConversionUtils;
045:
046: /**
047: * A mini JSP servlet that monitors a certain directory and
048: * recompiles and then instantiates the JSP pages as soon as
049: * they have changed.
050: *
051: * @author tcurdt
052: */
053: public final class ServerPageServlet extends HttpServlet {
054:
055: private static final long serialVersionUID = 1L;
056:
057: private final ReloadingClassLoader classloader = new ReloadingClassLoader(
058: ServerPageServlet.class.getClassLoader());
059: private FilesystemAlterationMonitor fam;
060: private CompilingListener jspListener;
061:
062: private Map servletsByClassname = new HashMap();
063:
064: public void init() throws ServletException {
065: super .init();
066:
067: final File serverpagesDir = new File(getServletContext()
068: .getRealPath("/")
069: + getInitParameter("serverpagesDir"));
070:
071: log("Monitoring serverpages in " + serverpagesDir);
072:
073: final TransactionalResourceStore store = new TransactionalResourceStore(
074: new MemoryResourceStore()) {
075:
076: private Set newClasses;
077: private Map newServletsByClassname;
078:
079: public void onStart() {
080: super .onStart();
081:
082: newClasses = new HashSet();
083: newServletsByClassname = new HashMap(
084: servletsByClassname);
085: }
086:
087: public void onStop() {
088: super .onStop();
089:
090: boolean reload = false;
091: for (Iterator it = newClasses.iterator(); it.hasNext();) {
092: final String clazzName = (String) it.next();
093:
094: try {
095: final Class clazz = classloader
096: .loadClass(clazzName);
097:
098: if (!HttpServlet.class.isAssignableFrom(clazz)) {
099: log(clazzName + " is not a servlet");
100: continue;
101: }
102:
103: // create new instance of jsp page
104: final HttpServlet servlet = (HttpServlet) clazz
105: .newInstance();
106: newServletsByClassname.put(clazzName, servlet);
107:
108: reload = true;
109: } catch (Exception e) {
110: log("", e);
111: }
112: }
113:
114: if (reload) {
115: log("Activating new map of servlets "
116: + newServletsByClassname);
117: servletsByClassname = newServletsByClassname;
118: }
119: }
120:
121: public void write(String pResourceName, byte[] pResourceData) {
122: super .write(pResourceName, pResourceData);
123:
124: if (pResourceName.endsWith(".class")) {
125:
126: // compiler writes a new class, remember the classes to reload
127: newClasses.add(pResourceName.replace('/', '.')
128: .substring(
129: 0,
130: pResourceName.length()
131: - ".class".length()));
132: }
133: }
134:
135: };
136:
137: // listener that generates the java code from the jsp page and provides that to the compiler
138: jspListener = new CompilingListener(new JavaCompilerFactory()
139: .createCompiler("eclipse"), store) {
140:
141: private final JspGenerator transformer = new JspGenerator();
142: private final Map sources = new HashMap();
143: private final Set resourceToCompile = new HashSet();
144:
145: public void onStart(FilesystemAlterationObserver pObserver) {
146: super .onStart(pObserver);
147:
148: resourceToCompile.clear();
149: }
150:
151: public void onFileChange(File pFile) {
152: if (pFile.getName().endsWith(".jsp")) {
153: final String resourceName = ConversionUtils
154: .stripExtension(getSourceNameFromFile(
155: observer, pFile))
156: + ".java";
157:
158: log("Updating " + resourceName);
159:
160: sources.put(resourceName, transformer
161: .generateJavaSource(resourceName, pFile));
162:
163: resourceToCompile.add(resourceName);
164: }
165: super .onFileChange(pFile);
166: }
167:
168: public void onFileCreate(File pFile) {
169: if (pFile.getName().endsWith(".jsp")) {
170: final String resourceName = ConversionUtils
171: .stripExtension(getSourceNameFromFile(
172: observer, pFile))
173: + ".java";
174:
175: log("Creating " + resourceName);
176:
177: sources.put(resourceName, transformer
178: .generateJavaSource(resourceName, pFile));
179:
180: resourceToCompile.add(resourceName);
181: }
182: super .onFileCreate(pFile);
183: }
184:
185: public String[] getResourcesToCompile(
186: FilesystemAlterationObserver pObserver) {
187: // we only want to compile the jsp pages
188: final String[] resourceNames = new String[resourceToCompile
189: .size()];
190: resourceToCompile.toArray(resourceNames);
191: return resourceNames;
192: }
193:
194: public ResourceReader getReader(
195: final FilesystemAlterationObserver pObserver) {
196: return new JspReader(sources, super
197: .getReader(pObserver));
198: }
199: };
200: jspListener.addReloadNotificationListener(classloader);
201:
202: fam = new FilesystemAlterationMonitor();
203: fam.addListener(serverpagesDir, jspListener);
204: fam.start();
205: }
206:
207: private String convertRequestToServletClassname(
208: final HttpServletRequest request) {
209:
210: final String path = request.getPathInfo().substring(1);
211:
212: final String clazz = ConversionUtils.stripExtension(path)
213: .replace('/', '.');
214:
215: return clazz;
216: }
217:
218: protected void service(HttpServletRequest request,
219: HttpServletResponse response) throws ServletException,
220: IOException {
221:
222: log("Request " + request.getRequestURI());
223:
224: final CompilationResult result = jspListener
225: .getCompilationResult();
226: final CompilationProblem[] errors = result.getErrors();
227:
228: if (errors.length > 0) {
229:
230: // if there are errors we provide the compilation errors instead of the jsp page
231:
232: final PrintWriter out = response.getWriter();
233:
234: out.append("<html><body>");
235:
236: for (int i = 0; i < errors.length; i++) {
237: final CompilationProblem problem = errors[i];
238: out.append(problem.toString()).append("<br/>").append(
239: '\n');
240: }
241:
242: out.append("</body></html>");
243:
244: out.flush();
245: out.close();
246: return;
247: }
248:
249: final String servletClassname = convertRequestToServletClassname(request);
250:
251: log("Checking for serverpage " + servletClassname);
252:
253: final HttpServlet servlet = (HttpServlet) servletsByClassname
254: .get(servletClassname);
255:
256: if (servlet == null) {
257: log("No servlet for " + request.getRequestURI());
258: response.sendError(404);
259: return;
260: }
261:
262: log("Delegating request to " + servletClassname);
263:
264: servlet.service(request, response);
265: }
266:
267: public void destroy() {
268:
269: fam.stop();
270:
271: super.destroy();
272: }
273: }
|