blob: a1dac719ba9f4be4ac3993297df53a4833c3ac96 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.jci2.examples.serverpages;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.String;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.jci2.ReloadingClassLoader;
import org.apache.commons.jci2.compilers.CompilationResult;
import org.apache.commons.jci2.compilers.JavaCompilerFactory;
import org.apache.commons.jci2.listeners.CompilingListener;
import org.apache.commons.jci2.monitor.FilesystemAlterationMonitor;
import org.apache.commons.jci2.monitor.FilesystemAlterationObserver;
import org.apache.commons.jci2.problems.CompilationProblem;
import org.apache.commons.jci2.readers.ResourceReader;
import org.apache.commons.jci2.stores.MemoryResourceStore;
import org.apache.commons.jci2.stores.TransactionalResourceStore;
import org.apache.commons.jci2.utils.ConversionUtils;
/**
* A mini JSP servlet that monitors a certain directory and
* recompiles and then instantiates the JSP pages as soon as
* they have changed.
*
* @author tcurdt
*/
public final class ServerPageServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final ReloadingClassLoader classloader = new ReloadingClassLoader(ServerPageServlet.class.getClassLoader());
private FilesystemAlterationMonitor fam;
private CompilingListener jspListener;
private Map<String, HttpServlet> servletsByClassname = new HashMap<String, HttpServlet>();
public void init() throws ServletException {
super.init();
final File serverpagesDir = new File(getServletContext().getRealPath("/") + getInitParameter("serverpagesDir"));
log("Monitoring serverpages in " + serverpagesDir);
final TransactionalResourceStore store = new TransactionalResourceStore(new MemoryResourceStore()) {
private Set<String> newClasses;
private Map<String, HttpServlet> newServletsByClassname;
public void onStart() {
super.onStart();
newClasses = new HashSet<String>();
newServletsByClassname = new HashMap<String, HttpServlet>(servletsByClassname);
}
public void onStop() {
super.onStop();
boolean reload = false;
for (final String clazzName : newClasses) {
try {
final Class clazz = classloader.loadClass(clazzName);
if (!HttpServlet.class.isAssignableFrom(clazz)) {
log(clazzName + " is not a servlet");
continue;
}
// create new instance of jsp page
final HttpServlet servlet = (HttpServlet) clazz.newInstance();
newServletsByClassname.put(clazzName, servlet);
reload = true;
} catch(final Exception e) {
log("", e);
}
}
if (reload) {
log("Activating new map of servlets "+ newServletsByClassname);
servletsByClassname = newServletsByClassname;
}
}
public void write(final String pResourceName, final byte[] pResourceData) {
super.write(pResourceName, pResourceData);
if (pResourceName.endsWith(".class")) {
// compiler writes a new class, remember the classes to reload
newClasses.add(pResourceName.replace('/', '.').substring(0, pResourceName.length() - ".class".length()));
}
}
};
// listener that generates the java code from the jsp page and provides that to the compiler
jspListener = new CompilingListener(new JavaCompilerFactory().createCompiler("eclipse"), store) {
private final JspGenerator transformer = new JspGenerator();
private final Map<String, byte[]> sources = new HashMap<String, byte[]>();
private final Set<String> resourceToCompile = new HashSet<String>();
public void onStart(final FilesystemAlterationObserver pObserver) {
super.onStart(pObserver);
resourceToCompile.clear();
}
public void onFileChange(final File pFile) {
if (pFile.getName().endsWith(".jsp")) {
final String resourceName = ConversionUtils.stripExtension(getSourceNameFromFile(observer, pFile)) + ".java";
log("Updating " + resourceName);
sources.put(resourceName, transformer.generateJavaSource(resourceName, pFile));
resourceToCompile.add(resourceName);
}
super.onFileChange(pFile);
}
public void onFileCreate(final File pFile) {
if (pFile.getName().endsWith(".jsp")) {
final String resourceName = ConversionUtils.stripExtension(getSourceNameFromFile(observer, pFile)) + ".java";
log("Creating " + resourceName);
sources.put(resourceName, transformer.generateJavaSource(resourceName, pFile));
resourceToCompile.add(resourceName);
}
super.onFileCreate(pFile);
}
public String[] getResourcesToCompile(final FilesystemAlterationObserver pObserver) {
// we only want to compile the jsp pages
final String[] resourceNames = new String[resourceToCompile.size()];
resourceToCompile.toArray(resourceNames);
return resourceNames;
}
public ResourceReader getReader( final FilesystemAlterationObserver pObserver ) {
return new JspReader(sources, super.getReader(pObserver));
}
};
jspListener.addReloadNotificationListener(classloader);
fam = new FilesystemAlterationMonitor();
fam.addListener(serverpagesDir, jspListener);
fam.start();
}
private String convertRequestToServletClassname( final HttpServletRequest request ) {
final String path = request.getPathInfo().substring(1);
return ConversionUtils.stripExtension(path).replace('/', '.');
}
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
log("Request " + request.getRequestURI());
final CompilationResult result = jspListener.getCompilationResult();
final CompilationProblem[] errors = result.getErrors();
if (errors.length > 0) {
// if there are errors we provide the compilation errors instead of the jsp page
final PrintWriter out = response.getWriter();
out.append("<html><body>");
for (final CompilationProblem problem : errors) {
out.append(problem.toString()).append("<br/>").append('\n');
}
out.append("</body></html>");
out.flush();
out.close();
return;
}
final String servletClassname = convertRequestToServletClassname(request);
log("Checking for serverpage " + servletClassname);
final HttpServlet servlet = servletsByClassname.get(servletClassname);
if (servlet == null) {
log("No servlet for " + request.getRequestURI());
response.sendError(404);
return;
}
log("Delegating request to " + servletClassname);
servlet.service(request, response);
}
public void destroy() {
fam.stop();
super.destroy();
}
}