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.
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.
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.
When handling a Jetty response I want to know on which port the request originated on?
public static void main(String[] args) {
Server server = new Server();
server.setConnectors(new Connector[] {connectorUnsecure, connectorSecure});
ServletContextHandler handler = new ServletContextHandler();
handler.setContextPath("/");
handler.addServlet(MyServlet.class, "/*");
server.setHandler(handler);
server.start();
server.join();
}
public abstract class MyServlet extends HttpServlet {
#Override
protected final void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Find out on which connector port the request came from.
// (The request.getRequestURL() does not contain the port at all times.)
}
}
When using a custom Handler, I could use something like:
public class CustomHandler extends AbstractHandler {
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// baseRequest.getConnection().getConnector().getPort()
}
}
However, I don't want to use a custom handler.
Is it possible to obtain the connector and its port when using a plain HttpServlet?
Thanks!
There's 5 methods on javax.servlet.http.HttpServletRequest that might be of use for you.
.getLocalAddr() - the server address the request is being processed on (could be IPv4 or IPv6)
.getLocalPort() - the server port the request is being processed on
.getRemoteAddr() - the client address the request is being processed on (could be IPv4 or IPv6)
.getRemotePort() - the client port the request is being processed on
.getHeader("Host") - the requested HTTP Host (and port) that the client thinks its talking to. (part of the HTTP spec, and especially useful for virtual hosts)
Note: the HTTP Request Host header can also be obtained via the .getRequestURI() method.
String serverAddr = URI.create(request.getRequestURI()).getHost();
I have a task to examine incoming HTTP requests and do some processing on the header and request body then store that locally.
If I understand correctly, I can do that with an apache module for Apache servers and an IIS extension for IIS. I'm not clear on what I would use for Java based application servers.
Any help/guidance would be appreciated.
If you will use a Java application server, then you should handle this in a Servlet Filter. Here's an example:
#WebFilter("/app/*")
public class LoginFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
//Set init params and load any resources to be used in this class.
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
//Here you will pre and post process the request-response cycle.
//Pre process is before executing "chain.doFilter(req, res);"
//Post process is after executing "chain.doFilter(req, res);"
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
String someRequestHeader = request.getHeader("<header-you-want-or-need>");
//do what you need to do
chain.doFilter(req, res);
}
#Override
public void destroy() {
//Close/free any resources used in this class.
}
}
More info:
The Java EE 7 Tutorial. 17 Java Servlet Technology. 17.6 Filtering Requests and Responses
Maybe you could use a HttpServlet.
Also have a look at the tutorial.
I have 2 web apps, no front-end(i.e html/Jsp) in either. Both have one servlet each.
Lets call them WebApp1/WebApp2 and ServiceServlet1/ServiceServlet2.
I have 2 war files, WebApp1.war and WebApp2.war and both deployed.
I call the ServiceServlet1 directly from the browser with -
http://localhost:8080/WebApp1/ServiceServlet1
Obviously the doGet method will be called(POST is associated only with FORM, correct me if I am wrong).
The ServiceServlet1 is build something like -
public class ServiceServlet1 extends HttpServlet {
#Override
protected void doGet(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws ServletException, IOException {
doPost(httpRequest, httpResponse);
}
#Override
protected void doPost(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) throws ServletException,
IOException {
RequestDispatcher requestDispatcher;
try {
// Process something
requestDispatcher = getServletContext().getRequestDispatcher("/WebApp2/ServiceServlet2");
requestDispatcher.forward(httpServletRequest, httpServletResponse);
} catch (IOException ioException) {
ioException.printStackTrace();
} catch (ServletException servletException) {
servletException.printStackTrace();
}
}
}
Essentially, what I require is to call the doPost() of ServiceServlet2
I have tried few different ways with httpReq.getRequestDispatcher(), sendRedirect etc. but have failed so far.
So how can I make it happen?
Thank you.
In addition to the answer of ckuetbach, you can't change the request method when dispatching the request. If the second servlet cannot be changed to execute the same business logic on doGet() as well, then you have to fire a POST request yourself programmatically.
HttpURLConnection connection = (HttpURLConnection) new URL("http://localhost/WebApp2/ServiceServlet2").openConnection();
connection.setRequestMethod("POST");
InputStream response = connection.getInputStream();
// ... Write to OutputStream of your HttpServletResponse?
See also:
How to use URLConnection to fire and handle HTTP requests?
The two servlets don't share the same Classloader, because they are in different ´*.WAR´-files.
As far as I know, you have to chances to do what you want:
Disable Classloader-Separation in tomcat (I've never done this tomcat)
Repackage the two aplication within one *.WAR