blob: a31564824aa2b9cf164228a58c28a3682c1e7cde [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.sling.scripting.java;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.SingleThreadModel;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.sling.api.SlingException;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingIOException;
import org.apache.sling.api.SlingServletException;
import org.apache.sling.api.scripting.SlingBindings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*/
public class ServletWrapper {
/** The logger. */
private final Logger logger = LoggerFactory.getLogger(getClass());
private final String servletUri;
/** The servlet config. */
private ServletConfig config;
private Servlet theServlet;
private long available = 0L;
private Exception compileException;
private CompilationContext ctxt;
/**
* A wrapper for servlets.
*/
public ServletWrapper(final ServletConfig config,
final Options options,
final SlingIOProvider ioProvider,
final String servletPath,
final ServletCache servletCache) {
this.config = config;
this.servletUri = servletPath;
this.ctxt = new CompilationContext(servletUri, options,
ioProvider, servletCache, this);
}
public CompilationContext getCompilationContext() {
return this.ctxt;
}
/**
* Get the servlet.
* @throws ServletException
* @throws IOException
* @throws FileNotFoundException
*/
private void getServlet()
throws ServletException {
destroy();
Servlet servlet = null;
try {
final Class<?> servletClass = ctxt.load();
servlet = (Servlet) servletClass.newInstance();
} catch (IllegalAccessException e) {
throw new ServletException(e);
} catch (InstantiationException e) {
throw new ServletException(e);
} catch (ServletException se) {
throw se;
} catch (Exception e) {
throw new ServletException(e);
}
servlet.init(config);
theServlet = servlet;
}
/**
* Sets the compilation exception for this ServletWrapper.
*
* @param je The compilation exception
*/
public void setCompilationException(final Exception je) {
this.compileException = je;
}
/**
* @param bindings
* @throws SlingIOException
* @throws SlingServletException
* @throws IllegalArgumentException if the Jasper Precompile controller
* request parameter has an illegal value.
*/
public void service(SlingBindings bindings) {
final SlingHttpServletRequest request = bindings.getRequest();
final Object oldValue = request.getAttribute(SlingBindings.class.getName());
try {
request.setAttribute(SlingBindings.class.getName(), bindings);
service(request, bindings.getResponse());
} catch (SlingException se) {
// rethrow as is
throw se;
} catch (IOException ioe) {
throw new SlingIOException(ioe);
} catch (ServletException se) {
throw new SlingServletException(se);
} finally {
request.setAttribute(SlingBindings.class.getName(), oldValue);
}
}
/**
* Call the servlet.
* @param request The current request.
* @param response The current response.
* @throws ServletException
* @throws IOException
* @throws FileNotFoundException
*/
private void service(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
if (ctxt.isRemoved()) {
throw new FileNotFoundException(servletUri);
}
if ((available > 0L) && (available < Long.MAX_VALUE)) {
if (available > System.currentTimeMillis()) {
response.setDateHeader("Retry-After", available);
response.sendError
(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
"Servlet unavailable.");
logger.error("Java servlet {} is unavailable.", this.servletUri);
return;
}
// Wait period has expired. Reset.
available = 0;
}
// check for compilation
if (ctxt.getLastModificationTest() == 0 ) {
synchronized (this) {
if (ctxt.getLastModificationTest() == 0 ) {
if ( ctxt.compile() ) {
// (re)load the servlet class
getServlet();
}
} else if (compileException != null) {
// Throw cached compilation exception
throw compileException;
}
}
} else if (compileException != null) {
// Throw cached compilation exception
throw compileException;
}
} catch (FileNotFoundException ex) {
ctxt.incrementRemoved();
try {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
ex.getMessage());
logger.error("Java servlet {} not found.", this.servletUri);
} catch (IllegalStateException ise) {
logger.error("Java servlet source not found." +
ex.getMessage(), ex);
}
return;
} catch (SlingException ex) {
throw ex;
} catch (ServletException ex) {
throw ex;
} catch (IOException ex) {
throw ex;
} catch (IllegalStateException ex) {
throw ex;
} catch (Exception ex) {
throw new ServletException(ex);
}
try {
// invoke the servlet
if (theServlet instanceof SingleThreadModel) {
// sync on the wrapper so that the freshness
// of the page is determined right before servicing
synchronized (this) {
theServlet.service(request, response);
}
} else {
theServlet.service(request, response);
}
} catch (UnavailableException ex) {
int unavailableSeconds = ex.getUnavailableSeconds();
if (unavailableSeconds <= 0) {
unavailableSeconds = 60; // Arbitrary default
}
available = System.currentTimeMillis() +
(unavailableSeconds * 1000L);
response.sendError
(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
ex.getMessage());
logger.error("Java servlet {} is unavailable.", this.servletUri);
return;
} catch (SlingException ex) {
throw ex;
} catch (ServletException ex) {
throw ex;
} catch (IOException ex) {
throw ex;
} catch (IllegalStateException ex) {
throw ex;
} catch (Exception ex) {
throw new ServletException(ex);
}
}
/**
* Destroy the servlet.
*/
public void destroy() {
if (theServlet != null) {
theServlet.destroy();
theServlet = null;
}
}
}