package restx.servlet;
import com.google.common.base.Optional;
import org.codehaus.commons.compiler.CompileException;
import org.codehaus.janino.ExpressionEvaluator;
import org.codehaus.janino.JaninoRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import restx.RestxMainRouterFactory;
import restx.factory.Factory;
import restx.server.WebServer;
import restx.server.WebServers;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Set;
public class RestxMainRouterServlet extends AbstractRestxMainRouterServlet {
public static final String DEPLOYED_SERVER_ID = "SERVLET-ENGINE-1";
private static final Logger logger = LoggerFactory.getLogger(RestxMainRouterServlet.class);
private String serverId;
public RestxMainRouterServlet() {
}
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
Optional<String> baseUri = Optional.fromNullable(System.getProperty("restx.baseUri"));
String baseServer = config.getServletContext().getInitParameter("restx.baseServerUri");
if (!baseUri.isPresent() && baseServer != null) {
try {
// try to use servlet3 API without actually requiring it
Collection<String> mappings = getMappings(config);
if (!mappings.isEmpty()) {
String routerPath = mappings.iterator().next();
if (routerPath.endsWith("/*")) {
routerPath = routerPath.substring(0, routerPath.length() - 2);
}
baseUri = Optional.of(baseServer + routerPath);
logger.debug("deduced baseUri from servlet registration: {}", baseUri);
}
} catch (JaninoRuntimeException e) {
logger.info("servlet <3 detected. use servlet3+ to get automatic baseUri detection");
} catch (CompileException e) {
logger.info("servlet <3 detected. use servlet3+ to get automatic baseUri detection");
} catch (InvocationTargetException e) {
logger.info("servlet <3 detected. use servlet3+ to get automatic baseUri detection");
}
}
if (!baseUri.isPresent()) {
logger.info("MINOR: baseUri cannot be found. Define it in restx.baseUri system property, or use Servlet 3+ API\n" +
"Note that is has no effect on restx behavior, it's just that it won't be able" +
" to properly display the startup banner.");
}
serverId = Optional.fromNullable(
config.getServletContext().getInitParameter("restx.serverId"))
.or(DEPLOYED_SERVER_ID);
registerIdNeeded(serverId);
init(RestxMainRouterFactory.newInstance(serverId, baseUri));
}
@SuppressWarnings("unchecked")
protected Collection<String> getMappings(ServletConfig config) throws CompileException, InvocationTargetException {
ExpressionEvaluator expressionEvaluator = new ExpressionEvaluator(
"config.getServletContext().getServletRegistration(config.getServletName()).getMappings()",
Collection.class,
new String[]{"config"},
new Class[]{ServletConfig.class});
return (Collection<String>) expressionEvaluator.evaluate(new Object[]{config});
}
@Override
public void destroy() {
super.destroy();
if (serverId != null) {
RestxMainRouterFactory.clear(serverId);
}
}
private static synchronized void registerIdNeeded(String serverId) {
Optional<WebServer> serverById = WebServers.getServerById(serverId);
if (!serverById.isPresent()) {
WebServers.register(new DeployedWebServer(serverId, guessServerType()));
}
}
/**
* Tries to guess server type based on current callstack.
*
* It uses the set of registered server types to make the guess, so this can be contributed through plugins.
*
* @return the guessed server type, may be 'unknown'
*/
private static String guessServerType() {
try {
Set<RegisteredServerType> serverTypes = Factory.getInstance().getComponents(RegisteredServerType.class);
for (StackTraceElement stackTraceElement : new Exception().fillInStackTrace().getStackTrace()) {
for (RegisteredServerType registeredServerType : serverTypes) {
if (stackTraceElement.getClassName().startsWith(registeredServerType.getPackageName())) {
return registeredServerType.getServerType();
}
}
}
return "unknown";
} catch (Exception ex) {
return "unknown";
}
}
private static class DeployedWebServer implements WebServer {
private final String serverId;
private final String serverType;
private DeployedWebServer(String serverId, String serverType) {
this.serverId = serverId;
this.serverType = serverType;
}
@Override
public void start() throws Exception {
throw new UnsupportedOperationException();
}
@Override
public void startAndAwait() throws Exception {
throw new UnsupportedOperationException();
}
@Override
public void await() throws Exception {
throw new UnsupportedOperationException();
}
@Override
public void stop() throws Exception {
throw new UnsupportedOperationException();
}
@Override
public boolean isStarted() {
return true;
}
@Override
public String baseUrl() {
return System.getProperty("restx.server.baseUrl", "");
}
@Override
public String getServerId() {
return serverId;
}
@Override
public int getPort() {
return 0;
}
@Override
public String getServerType() {
return serverType;
}
}
}