I have two classes Server (with the main method, starting the server) and StartPageServlet with a Servlet.
The most important part of the code is:
public class Server {
public static void main(String[] args) throws Exception {
// some code
// I want to pass "anObject" to every Servlet.
Object anObject = new Object();
Server server = new Server(4000);
ServletContextHandler context =
new ServletContextHandler(ServletContextHandler.SESSIONS);
context.addServlet(StartPageServlet.class, "/");
// more code
}
And the StartPageServlet:
public class StartPageServlet extends HttpServlet {
#Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
// Here I want to access "anObject"
}
How do I do this?
Embedded Jetty is so wonderful here.
You have a few common options:
Direct instantiation of the servlet, use constructors or setters, then hand it off to Jetty via the ServletHolder (can be any value or object type)
Add it to the ServletContext in your main, and then access it via the ServletContext in your application (can be any value or object type).
Examples:
package jetty;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class ObjectPassingExample
{
public static void main(String args[]) throws Exception
{
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
// Option 1: Direct servlet instantiation and ServletHolder
HelloServlet hello = new HelloServlet("everyone");
ServletHolder helloHolder = new ServletHolder(hello);
context.addServlet(helloHolder, "/hello/*");
// Option 2: Using ServletContext attribute
context.setAttribute("my.greeting", "you");
context.addServlet(GreetingServlet.class, "/greetings/*");
server.setHandler(context);
server.start();
server.join();
}
public static class HelloServlet extends HttpServlet
{
private final String hello;
public HelloServlet(String greeting)
{
this.hello = greeting;
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/plain");
resp.getWriter().println("Hello " + this.hello);
}
}
public static class GreetingServlet extends HttpServlet
{
private String greeting;
#Override
public void init() throws ServletException
{
this.greeting = (String) getServletContext().getAttribute("my.greeting");
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/plain");
resp.getWriter().println("Greetings to " + this.greeting);
}
}
}
Singleton
You want to pass the same single instance to each servlet?
Use the Singleton pattern to create a single instance that is available globally.
The simplest fool-proof way to do that in Java is through an Enum. See Oracle Tutorial. Also see this article and the book Effective Java: Programming Language Guide, Second Edition (ISBN 978-0-321-35668-0, 2008) by Dr. Joshua Bloch.
So no need to pass an object. Each servlet can access the same single instance through the enum.
Per web app
If you want to do some work when your web app is first launching but before any servlet in that web app has handled any request, write a class that implements the ServletContextListener interface.
Mark your class with the #WebListener annotation to have your web container automatically instantiate and invoke.
I had a similar situation but needed to share a singleton with a servlet deployed via war with hot (re)deploy in a Jetty container. The accepted answer wasn't quite what I needed in my case since the servlet has a lifecycle and context managed by a deployer.
I ended up with a brute-force approach, adding the object to the server context, which persists for the life of the container, and then fetching the object from within the servlet(s). This required loading the class of the object in a parent (system) classloader so that the war webapp doesn't load its own version of the class into its own classloader, which would cause a cast exception as explained here.
Embedded Jetty server code:
Server server = new Server(8090);
// Add all classes related to the object(s) you want to share here.
WebAppContext.addSystemClasses(server, "my.package.MyFineClass", ...);
// Handler config
ContextHandlerCollection contexts = new ContextHandlerCollection();
HandlerCollection handlers = new HandlerCollection();
handlers.setHandlers(new Handler[] { contexts });
server.setHandler(handlers);
// Deployer config (hot deploy)
DeploymentManager deployer = new DeploymentManager();
DebugListener debug = new DebugListener(System.err,true,true,true);
server.addBean(debug);
deployer.addLifeCycleBinding(new DebugListenerBinding(debug));
deployer.setContexts(contexts);
deployer.setContextAttribute(
"org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$");
WebAppProvider webapp_provider = new WebAppProvider();
webapp_provider.setMonitoredDirName("/.../webapps");
webapp_provider.setScanInterval(1);
webapp_provider.setExtractWars(true);
webapp_provider.setConfigurationManager(new PropertiesConfigurationManager());
deployer.addAppProvider(webapp_provider);
server.addBean(deployer);
// Other config...
// Tuck any objects/data you want into the root server object.
server.setAttribute("my.package.MyFineClass", myFineSingleton);
server.start();
server.join();
Example servlet:
public class MyFineServlet extends HttpServlet
{
MyFineClass myFineSingleton;
#Override
public void init() throws ServletException
{
// Sneak access to the root server object (non-portable).
// Not possible to cast this to `Server` because of classloader restrictions in Jetty.
Object server = request.getAttribute("org.eclipse.jetty.server.Server");
// Because we cannot cast to `Server`, use reflection to access the object we tucked away there.
try {
myFineSingleton = (MyFineClass) server.getClass().getMethod("getAttribute", String.class).invoke(server, "my.package.MyFineClass");
} catch (Exception ex) {
throw new ServletException("Unable to reflect MyFineClass instance via Jetty Server", ex);
}
}
#Override
protected void doGet( HttpServletRequest request,
HttpServletResponse response ) throws ServletException, IOException
{
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<h1>Hello from MyFineServlet</h1>");
response.getWriter().println("Here's: " + myFineSingleton.toString());
}
}
My build file for the servlet (sbt) placed the my.package.MyFineClass dependency into the "provided" scope so it wouldn't get packaged into the war as it will already be loaded into the Jetty server.
I would recommend that you investigate Google's solution to this problem... namely: dependency injection with Guice. They have a special servlet package that deals with servlets specifically.
Related
This question was already asked, however since then all answers (that I could found) are no longer valid.
Essentially I want to implement a website with Vaadin (V23), that communicates with a WebApp via POST requests that is running on another server (physically). To do it, I want to create separate Servlet that would handle the communication (receiving side) with another Server. Let's say, this is not implemneted version:
#WebServlet(urlPatterns = "/communication", name = "QuizServlet", asyncSupported = true)
public class QuizServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(400, "Not implemented");
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(400, "Not implemented");
}
}
The problem is however, that I always get redirected to default dispatcher Servlet, and it seems, regardless of what I do:
SpringVaadinServlet was deprecated and no longer exists, extending VaadinServlet does not work.
Changing mappings in properties (vaadin.url-mapping=) also does not work, I just get redirected to this new mapping in all cases.
Trying to do servlets on separate ports yields same redirection on all ports, even if explicitly registering my custom Servlet on the Connector, with separate Sevice (WebMvcConfigurer Tomcat configuration). Answer from this post, also too old.
Registering servlet directly also does not do anything (by implementing WebApplicationInitializer).
There for the question, how to make use of two different servlets with new Vaadin 23 and Spring Boot 2.7.1?
I have found some kind of a solution to my problem. Namely on startup of my BootAplication, I am also starting the second separate Tomcat server that uses my custom Servlet :
#Service
public class QuizServer {
private final Log log = LogFactory.getLog(QuizServer.class);
#PostConstruct
public void startServer() throws IOException, LifecycleException {
start();
}
private void start() throws IOException, LifecycleException {
Tomcat tomcat = new Tomcat();
String contextPath = "/";
String appBase = new File(".").getAbsolutePath();
Context ctx = tomcat.addContext(contextPath, appBase);
Tomcat.addServlet(ctx, "quizServlet", new QuizServlet());
ctx.addServletMappingDecoded("/*", "quizServlet");
tomcat.setPort(8085);
tomcat.start();
tomcat.getConnector();
log.info("Quiz server started");
}
}
#WebServlet(urlPatterns = "/*", name = "quizServlet", asyncSupported = true)
public class QuizServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println("Test");
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.sendError(400, "Not implemented");
}
}
It is a bit crude though, since ideally, it shouldn't require a separate server.
We have unmanaged extension. We implemented custom communication API between server and client.
Now we need to ensure that client and server have same API version.
One solution - verify version in each resource. But this approach is messy and leads to code duplication.
What we want is to implement our own Filter and add it to Neo server.
Is this possible? If yes - then how?
This is possible!
Approach is a bit tricky and fragile, but it's working (blog post).
Dependency
You need neo4j-server dependency, because it contains SPIPluginLifecycle that is needed to get access to Neo4j web server.
So, add to your pom.xml:
<dependency>
<groupId>org.neo4j.app</groupId>
<artifactId>neo4j-server</artifactId>
<version>${version.neo4j}</version>
</dependency>
Filter
Create your filter. Let's take this one for example:
public class CustomFilter implements Filter {
public CustomFilter() {
}
#Override
public void init(final FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(final ServletRequest request,
final ServletResponse response,
final FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
#Override
public void destroy() {}
}
This filter doesn't do anything usefull - just continue chain further.
Lifecycle plugin
Now tricky part. We need to:
Implement SPIPluginLifecycle
Get web server
Add filter to web server
Code:
public final class ExtensionPluginLifecycle implements SPIPluginLifecycle {
private WebServer webServer;
private CustomFilter customFilter;
#Override
public Collection<Injectable<?>> start(final NeoServer neoServer) {
webServer = getWebServer(neoServer);
addFilters();
}
#Override
public void stop() {
removeFilters();
}
#Override
public Collection<Injectable<?>> start(final GraphDatabaseService graphDatabaseService,
final Configuration config) {
throw new IllegalAccessError();
}
private WebServer getWebServer(final NeoServer neoServer) {
if (neoServer instanceof AbstractNeoServer) {
return ((AbstractNeoServer) neoServer).getWebServer();
}
throw new IllegalArgumentException(String.format("Expected: [AbstractNeoServer], Received: [%s].", neoServer));
}
private void addFilters() {
customFilter = new CustomFilter();
webServer.addFilter(customFilter, "/extension-path/*");
}
private void removeFilters() {
webServer.removeFilter(customFilter, "/extension-path/*");
}
}
Tricky part is not so "legal" access to web server. This can break in future, so be carefull.
Note addFilters() and removeFilters() methods - this is why we have been done all this way.
Important: lifecycle plugin should be registered as service:
// file: META-INF/services/org.neo4j.server.plugins.PluginLifecycle
my.company.extension.ExtensionPluginLifecycle
I have been playing around with java servers and servlets. However one question still remains.
Lets say I write a server like this:
class server {
public static void main(String[] args){
int port = 8080;
try{
ServerSocket ss = new ServerSocket(port);
Socket s = ss.accept();
}catch(Exception e){
System.out.println("Something went wrong");
}
}
}
this will listen for httprequest on port 8080.
Now lets say I have a servlet that looks like this:
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<HTML>");
out.println("<HEAD><TITLE>Hello World</TITLE></HEAD>");
out.println("<BODY>");
out.println("<BIG>Hello World</BIG>");
out.println("</BODY></HTML>");
}
}
I can easily use an already existing server like tomcat or glassfish to deploy this servlet. But is it possible to deploy this from the simple server here above?
No, you need a Servlet implementation or if you want to re-invent the wheel create your own. For instance Catalina is the Tomcat servlet implementation.
No. You need java implementation that handle servlet's code and return html. Basically glassfish or tomcat is a server which listens to your request, run java code at back end and return result. On superficial level, tomcat and glassfish use basic server to capture requests. However there are a lot more things to do.
In your simple server, there is nothing to handle java code written in servlet.
Your server will return text of servelet instead of running it.
not a easy way.
servlet need a java container implementation,like tomcat or glassfish。 if you think tomcat or glassfish is too heavy, can try jetty.
public class HelloHandler extends AbstractHandler
{
public void handle(String target,Request baseRequest,
HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html;charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
response.getWriter().println("<h1>Hello World</h1>");
}
}
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
server.setHandler(new HelloHandler());
server.start();
server.join();
}
you also can write a simple Servlet implementation by netty.
I'm using Grizzly to serve a Jersey application, while using Logback for my logging needs. Please not that there are no Servlets involved here, I fire up everything "by hand" using a piece of code like this:
final URI uri = /* this is a configuration option */
this.server = new HttpServer();
final NetworkListener nl = new NetworkListener(
"grizzly", uri.getHost(), uri.getPort());
server.addListener(nl);
final GuiceComponentProviderFactory gcpf =
new GuiceComponentProviderFactory(rc, inj);
final HttpHandler processor = ContainerFactory.createContainer(
HttpHandler.class, rc, gcpf);
this.server.getServerConfiguration().addHttpHandler(
processor, uri.getPath());
server.start();
Now I would like to use Logback's MDC feature to make the socket address of the clients visible in the log records. For this purpose I would need some place to hook up a listener to the HTTP processing which gets notified about incoming requests (where I can put the address into the MDC) and when a request is done (so I can clean up the MDC). One approach I followed is to hook up a Container*Filter instance with Jersey, which looked like this:
class MdcFilter implements
ContainerRequestFilter, ContainerResponseFilter {
#Override
public ContainerRequest filter(ContainerRequest request) {
MDC.put("http-client", "foo" /* no way to get the address here */);
return request;
}
#Override
public ContainerResponse filter(
ContainerRequest request,
ContainerResponse response) {
MDC.remove("http-client");
return response;
}
}
Unfortunately, a Jersey ContainerRequest does not provide information about the connected client (which cam as a real surprise).
I suspect a similar interface should exist with Grizzly itself, but I was unable to dig it out.
For Grizzly, the relevant API is called HttpServerProbe. Using this, it comes down to something like this:
final HttpServer server = new org.glassfish.grizzly.http.server.HttpServer();
server.addListener(new NetworkListener("grizzly", "localhost", 8080));
server.getServerConfiguration().addHttpHandler(
new StaticHttpHandler("/var/www/"), "/");
server.getServerConfiguration().getMonitoringConfig().getWebServerConfig()
.addProbes(new HttpServerProbe.Adapter() {
#Override
public void onRequestReceiveEvent(
HttpServerFilter filter,
Connection connection,
Request request) {
System.out.println(request.getRemoteAddr());
MDC.put("http-client", request.getRemoteAddr());
}
#Override
public void onRequestCompleteEvent(
HttpServerFilter filter,
Connection connection,
Response response) {
MDC.remove("http-client");
}
}
server.start();
Note that there are more events which might be relevant, like suspend, resume and cancel. These should probably be handled as well, especially if long-polling (aka Comet, aka whatnot) is used. But basically this is the place to hook into.
In your MdcFilter, try to inject the HttpServletRequest into your class and use that as you would normally to call getRemoteAddr() or any other such function as in:
class MdcFilter implements
ContainerRequestFilter, ContainerResponseFilter {
#Context
protected HttpServletRequest r;
#Override
public ContainerRequest filter(ContainerRequest request) {
MDC.put("http-client", "foo" r.getRemoteAddr());
return request;
}
#Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
MDC.remove("http-client");
return response;
}
}
I tested this out using a similar approach to "firing things up by hand" and it worked out for me in that case. I assume it would work for you as well.
something awful is happening
i have 2 servlets in my project - one of them simply has a post method and is responsible for handling file uploads. i recently added the other one - it has a get and a post method.
here is the 2nd servlet code
#Singleton
#WebServlet("/Medical_Web")
public class XXXDetailsServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Inject
private Provider<XXXPersistenceManager> persistenceManager;
#Inject
private Provider<XXXChain> chainProvider;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("servlet address = " + this);
final String xxx= request.getParameter("xxx");
String json = "";
try {
final XXXBean xxxBean = persistenceManager.get().find(xxx);
json = new GsonBuilder().create().toJson(xxxBean);
} catch (Exception ex) {
ex.printStackTrace();
}
request.setAttribute("json", json.trim());
getServletContext().getRequestDispatcher("/XXX.jsp").forward(request, response);
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("servlet address = " + this);
final String xxx = request.getParameter("xxx");
try {
final XXXChain chain = chainProvider.get();
chain.getContext().setAttribute(XXX_TYPE, XXXType.DELETE);
final XXXBean xxxBean = persistenceManager.get().find(xxx);
final List<XXXBean> xxxList = new ArrayList<XXXBean>();
xxxList.add(xxxBean);
chain.process(xxxList);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
now - here's whats getting me
for some reason - even though this is marked as a #Singleton - the servlet addresses are definitely coming back as different. I noticed this initially when I hit a null pointer in my post method - whenever i call the get method, the instance of the servlet i get back has all the fields populated. whenever i call the post method, the instance of the servlet i get back (it is a different instance) does not have the fields populated (just null, seems like they didn't get injected).
I'm really struggling to figure out what's going on here. it seems as if an instance of this servlet was created outside of the guice context. if it matters - we are using JBoss 7.1
(sorry about all the XXX's, don't know if i can post actual names)
here's the rest of my guice setup
public class XXXServletContextListener extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new XXXUploadModule(), new XXXServletModule());
}
}
and here's the servlet module
public class XXXServletModule extends ServletModule {
#Override
protected void configureServlets() {
serve("/xxx1").with(XXXDetailsServlet.class); // this one fails on post
serve("/xxx2").with(XXXUploadServlet.class); // this one works
}
}
I am not familiar with how Guice servlet integration works, but having the #WebServlet("/Medical_Web") annotation means that your web container will also instantiate that servlet to serve requests. A pool of them actually, it doesn't have a concept of singletons.
My guess is you just have to remove the annotation and let ServletModule control the servlet life-cycle.