Skip to content
This repository was archived by the owner on Sep 8, 2019. It is now read-only.

Servlet filter #9

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,58 @@ TODO

TODO

## Use in your application server

If you don't like the embedded server, you can use any application server which support servlet filter.

First exclude jetty in your pom.xml

```xml
<dependency>
<groupId>net.code-story</groupId>
<artifactId>http</artifactId>
<version>1.31</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</exclusion>
</exclusions>
</dependency>
```

Then declare the filter in your web.xml with your class for configuration :
```xml
<filter>
<filter-name>codestoryfilter</filter-name>
<filter-class>net.codestory.http.WebServer</filter-class>
<init-param>
<param-name>configClass</param-name>
<param-value>com.mycompany.ConfigClass</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>codestoryfilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
```

The class pass throw the parameter configClass must implement WebServerConfig, here is an exemple :
```java
public class ConfigClass implements WebServerConfig {
@Override
public void configure(WebServer webServer) {
webServer.configure(routes ->
routes.get("/hello", "Hello World"));
}
}
```

# Deploy on Maven Central

Build the release:
Expand Down Expand Up @@ -596,7 +648,6 @@ Synchro to Maven Central is done hourly.
+ Cleanup Payload class. Make Payload immutable?
+ Auto reload meme sans les lambda avec capture de variables locales
+ Singletons qui utilise les annotations standards
+ Remplacer Simple par un Servlet Filter qui fonctionne par defaut sur un Jetty Http
+ Help use local storage
+ Add your own/reuse Servlet filters
+ Supporter les coffee et less pré-générés
Expand Down
17 changes: 14 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,14 @@

<dependencies>
<dependency>
<groupId>org.simpleframework</groupId>
<artifactId>simple</artifactId>
<version>5.1.6</version>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.1.3.v20140225</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>9.1.3.v20140225</version>
</dependency>
<dependency>
<groupId>com.github.sommeri</groupId>
Expand Down Expand Up @@ -305,5 +310,11 @@
<version>3.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>9.1.3.v20140225</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
152 changes: 109 additions & 43 deletions src/main/java/net/codestory/http/WebServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
package net.codestory.http;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.*;
import java.nio.file.Path;
import java.util.*;
Expand All @@ -27,21 +29,34 @@
import net.codestory.http.payload.*;
import net.codestory.http.reload.*;
import net.codestory.http.routes.*;
import net.codestory.http.servlet.WebServerConfig;
import net.codestory.http.ssl.*;

import org.simpleframework.http.*;
import org.simpleframework.http.core.*;
import org.simpleframework.transport.*;
import org.simpleframework.transport.connect.*;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.slf4j.*;

import javax.net.ssl.*;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class WebServer {
public class WebServer implements Filter {
private final static Logger LOG = LoggerFactory.getLogger(WebServer.class);

private final Server server;
private final SocketConnection connection;
private Server server;
private RoutesProvider routesProvider;
private int port;

Expand All @@ -51,12 +66,6 @@ public WebServer() {
}

public WebServer(Configuration configuration) {
try {
server = new ContainerServer(this::handle);
connection = new SocketConnection(server);
} catch (IOException e) {
throw new IllegalStateException("Unable to create http server", e);
}
configure(configuration);
}

Expand Down Expand Up @@ -96,7 +105,7 @@ public WebServer start(int port) {
}

public WebServer startSSL(int port, Path pathCertificate, Path pathPrivateKey) {
SSLContext context;
SslContextFactory context;
try {
context = new SSLContextFactory().create(pathCertificate, pathPrivateKey);
} catch (Exception e) {
Expand All @@ -105,11 +114,33 @@ public WebServer startSSL(int port, Path pathCertificate, Path pathPrivateKey) {
return startWithContext(port, context);
}

private WebServer startWithContext(int port, SSLContext context) {
private WebServer startWithContext(int port, SslContextFactory context) {
try {
this.port = Env.INSTANCE.overriddenPort(port);
embedded = true;

if (context == null) {
server = new Server(this.port);
} else {
server = new Server();

HttpConfiguration https = new HttpConfiguration();
https.addCustomizer(new SecureRequestCustomizer());

ServerConnector sslConnector = new ServerConnector(server,
new SslConnectionFactory(context, "http/1.1"),
new HttpConnectionFactory(https));
sslConnector.setPort(this.port);

server.addConnector(sslConnector);
}

ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
servletHandler.addFilter(new FilterHolder(this), "/*", EnumSet.of(DispatcherType.REQUEST));

connection.connect(new InetSocketAddress(this.port), context);
server.setHandler(servletHandler);

server.start();

LOG.info("Server started on port {}", this.port);
} catch (RuntimeException e) {
Expand All @@ -135,33 +166,8 @@ public void reset() {
public void stop() {
try {
server.stop();
} catch (IOException e) {
throw new IllegalStateException("Unable to stop the web server", e);
}
}

void handle(Request request, Response response) {
Context context = null;

try {
RouteCollection routes = routesProvider.get();
context = new Context(request, response, routes.getIocAdapter());

applyRoutes(routes, context);
} catch (Exception e) {
if (context == null) {
// Didn't manage to initialize a full context
// because the routes failed to load
//
context = new Context(request, response, null);
}
handleServerError(context, e);
} finally {
try {
response.close();
} catch (IOException e) {
// Ignore
}
throw new IllegalStateException("Unable to stop the web server", e);
}
}

Expand Down Expand Up @@ -198,4 +204,64 @@ protected Payload errorPage(Payload payload, Exception e) {
Exception shownError = Env.INSTANCE.prodMode() ? null : e;
return new ErrorPage(payload, shownError).payload();
}

private boolean embedded = false;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
if (embedded) {
return;
}
String configClassName = filterConfig.getInitParameter("configClass");
if (configClassName == null) {
throw new IllegalArgumentException("Parameter configClass must be specified for the filter.");
}

try {
Class<?> configClass = Class.forName(configClassName);
Constructor<?> constructor = configClass.getConstructor();
Object configObject = constructor.newInstance();
if (!(configObject instanceof WebServerConfig)) {
throw new IllegalArgumentException(configClassName + " must implement WebServerConfig");
}
WebServerConfig webServerConfig = (WebServerConfig) configObject;
webServerConfig.configure(this);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Parameter configClass must be set with a class", e);
} catch (NoSuchMethodException|InvocationTargetException|InstantiationException|IllegalAccessException e) {
throw new IllegalArgumentException(configClassName + " must have a public constructor with no args", e);
}
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

Context context = null;

try {
RouteCollection routes = routesProvider.get();
context = new Context((HttpServletRequest)request, (HttpServletResponse)response, routes.getIocAdapter());

applyRoutes(routes, context);
} catch (Exception e) {
if (context == null) {
// Didn't manage to initialize a full context
// because the routes failed to load
//
context = new Context((HttpServletRequest)request, (HttpServletResponse)response, null);
}
handleServerError(context, e);
} finally {
try {
response.getOutputStream().close();
} catch (IOException e) {
// Ignore
}
}
}

@Override
public void destroy() {

}
}
4 changes: 3 additions & 1 deletion src/main/java/net/codestory/http/convert/TypeConvert.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
package net.codestory.http.convert;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;

import net.codestory.http.internal.*;

import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import net.codestory.http.io.InputStreams;

public class TypeConvert {
private static ObjectMapper OBJECT_MAPPER = new ObjectMapper()
Expand Down Expand Up @@ -71,7 +73,7 @@ public static <T> T convert(Context context, Class<T> type) {

String json;
try {
json = context.request().getContent();
json = InputStreams.readString(context.request().getInputStream(), Charset.forName("UTF-8"));
} catch (IOException e) {
throw new IllegalArgumentException("Unable read request content", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import net.codestory.http.internal.*;
import net.codestory.http.payload.*;

import org.simpleframework.http.*;

import twitter4j.*;
import twitter4j.conf.*;
Expand Down Expand Up @@ -70,16 +69,16 @@ public Payload apply(String uri, Context context, PayloadSupplier nextFilter) th
}

return Payload.seeOther("/")
.withCookie(new Cookie("userId", user.id.toString(), "/", true))
.withCookie(new Cookie("screenName", user.screenName, "/", true))
.withCookie(new Cookie("userPhoto", user.imageUrl, "/", true));
.withCookie("userId", user.id.toString())
.withCookie("screenName", user.screenName)
.withCookie("userPhoto", user.imageUrl);
}

if (uri.equals(uriPrefix + "logout")) {
return Payload.seeOther("/")
.withCookie(new Cookie("userId", "", "/", false))
.withCookie(new Cookie("screenName", "", "/", false))
.withCookie(new Cookie("userPhoto", "", "/", false));
.withCookie("userId", "")
.withCookie("screenName", "")
.withCookie("userPhoto", "");
}

String userId = context.cookieValue("userId");
Expand Down
Loading