Java: Simple HTTP Server application that responds in JSON - java

I want to create a very simple HTTP server application in Java.
For example, if I run the server on localhost in port 8080, and I make to following call from my browser, I want to get a Json array with the string 'hello world!':
http://localhost:8080/func1?param1=123&param2=456
I would like to have in the server something that looks like this (very abstract code):
// Retunrs JSON String
String func1(String param1, String param2) {
// Do Something with the params
String jsonFormattedResponse = "['hello world!']";
return jsonFormattedResponse;
}
I guess that this function should not actually "return" the json, but to send it using some HTTP response handler or something similar...
What it the simplest way to do it, without a need to get familiar with many kinds of 3rd party libraries that have special features and methodology?

You could use classes from the package com.sun.net.httpserver:
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class JsonServer {
private static final String HOSTNAME = "localhost";
private static final int PORT = 8080;
private static final int BACKLOG = 1;
private static final String HEADER_ALLOW = "Allow";
private static final String HEADER_CONTENT_TYPE = "Content-Type";
private static final Charset CHARSET = StandardCharsets.UTF_8;
private static final int STATUS_OK = 200;
private static final int STATUS_METHOD_NOT_ALLOWED = 405;
private static final int NO_RESPONSE_LENGTH = -1;
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String ALLOWED_METHODS = METHOD_GET + "," + METHOD_OPTIONS;
public static void main(final String... args) throws IOException {
final HttpServer server = HttpServer.create(new InetSocketAddress(HOSTNAME, PORT), BACKLOG);
server.createContext("/func1", he -> {
try {
final Headers headers = he.getResponseHeaders();
final String requestMethod = he.getRequestMethod().toUpperCase();
switch (requestMethod) {
case METHOD_GET:
final Map<String, List<String>> requestParameters = getRequestParameters(he.getRequestURI());
// do something with the request parameters
final String responseBody = "['hello world!']";
headers.set(HEADER_CONTENT_TYPE, String.format("application/json; charset=%s", CHARSET));
final byte[] rawResponseBody = responseBody.getBytes(CHARSET);
he.sendResponseHeaders(STATUS_OK, rawResponseBody.length);
he.getResponseBody().write(rawResponseBody);
break;
case METHOD_OPTIONS:
headers.set(HEADER_ALLOW, ALLOWED_METHODS);
he.sendResponseHeaders(STATUS_OK, NO_RESPONSE_LENGTH);
break;
default:
headers.set(HEADER_ALLOW, ALLOWED_METHODS);
he.sendResponseHeaders(STATUS_METHOD_NOT_ALLOWED, NO_RESPONSE_LENGTH);
break;
}
} finally {
he.close();
}
});
server.start();
}
private static Map<String, List<String>> getRequestParameters(final URI requestUri) {
final Map<String, List<String>> requestParameters = new LinkedHashMap<>();
final String requestQuery = requestUri.getRawQuery();
if (requestQuery != null) {
final String[] rawRequestParameters = requestQuery.split("[&;]", -1);
for (final String rawRequestParameter : rawRequestParameters) {
final String[] requestParameter = rawRequestParameter.split("=", 2);
final String requestParameterName = decodeUrlComponent(requestParameter[0]);
requestParameters.putIfAbsent(requestParameterName, new ArrayList<>());
final String requestParameterValue = requestParameter.length > 1 ? decodeUrlComponent(requestParameter[1]) : null;
requestParameters.get(requestParameterName).add(requestParameterValue);
}
}
return requestParameters;
}
private static String decodeUrlComponent(final String urlComponent) {
try {
return URLDecoder.decode(urlComponent, CHARSET.name());
} catch (final UnsupportedEncodingException ex) {
throw new InternalError(ex);
}
}
}
On a side note, ['hello world!'] is invalid JSON. Strings must be enclosed in double quotes.

You could :
Install Apache Tomcat, and just drop a JSP into the ROOT project that implements this.
I second #xehpuk. It's not actually that hard to write your own single class HTTP server using just standard Java. If you want to do it in earlier versions you can use NanoHTTPD, which is a pretty well known single class HTTP server implementation.
I would personally recommend that you look into Apache Sling (pretty much THE Reference implementation of a Java REST api). You could probably implement your requirements here using Sling without ANY programming at all.
But as others have suggested, the standard way to do this is to create a java WAR and deploy it into a 'servlet container' such as Tomcat or Jetty etc.

If you are already familiar with servlet you do not need much to create a simple server to achieve what you want. But I would like to emphasize that your needs will likely to increase rapidly and therefore you may need to move to a RESTful framework (e.g.: Spring WS, Apache CXF) down the road.
You need to register URIs and get parameters using the standard servlet technology. Maybe you can start here: http://docs.oracle.com/cd/E13222_01/wls/docs92/webapp/configureservlet.html
Next, you need a JSON provider and serialize (aka marshall) it in JSON format. I recommend JACKSON. Take a look at this tutorial:
http://www.sivalabs.in/2011/03/json-processing-using-jackson-java-json.html
Finally, your code will look similar to this:
public class Func1Servlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String p1 = req.getParameter("param1");
String p2 = req.getParameter("param2");
// Do Something with the params
ResponseJSON resultJSON = new ResponseJSON();
resultJSON.setProperty1(yourPropert1);
resultJSON.setProperty2(yourPropert2);
// Convert your JSON object into JSON string
Writer strWriter = new StringWriter();
mapper.writeValue(strWriter, resultJSON);
String resultString = strWriter.toString();
resp.setContentType("application/json");
out.println(resultString );
}
}
Map URLs in your web.xml:
<servlet>
<servlet-name>func1Servlet</servlet-name>
<servlet-class>myservlets.func1servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>func1Servlet</servlet-name>
<url-pattern>/func1/*</url-pattern>
</servlet-mapping>
Keep in mind this is a pseudo-code. There are lots you can do to enhance it, adding some utility classes, etc...
Nevertheless, as your project grows your need for a more comprehensive framework becomes more evident.

Run main to start the server on port 8080
public class Main {
public static void main(String[] args) throws LifecycleException {
Tomcat tomcat = new Tomcat();
Context context = tomcat.addContext("", null);
Tomcat.addServlet(context, "func1", new HttpServlet() {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Object response = func1(req.getParameter("param1"), req.getParameter("param2"));
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(resp.getWriter(), response);
}
});
context.addServletMappingDecoded("/func1", "func1");
tomcat.start();
tomcat.getServer().await();
}
private static String[] func1(String p1, String p2) {
return new String[] { "hello world", p1, p2 };
}
}
Gradle dependencies:
dependencies {
compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '8.5.28' // doesn't work with tomcat 9
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.4'
}

Related

How to mock Gson in mockito

In my application, there are few REST resources been invoked and I am using Gson as the library for parsing the responses received. While writing unit tests for the above methods I am unable to mock the Gson class as it is a final class.
While doing some research over the internet I found that a file named org.mockito.plugins.MockMaker should be created at src/test/resources/mockito-extensions with the content of the following,
mock-maker-inline
but still am unable to get it worked. What I am doing wrong.
I am getting the following exception when running the above test case (due to the gson object is not being properly mocked)
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176)
at com.google.gson.Gson.fromJson(Gson.java:803)
at com.google.gson.Gson.fromJson(Gson.java:768)
at com.google.gson.Gson.fromJson(Gson.java:717)
at com.google.gson.Gson.fromJson(Gson.java:689)
at org.kasun.sample.client.supportjira.impl.GroupRestClientImpl.addUser(GroupRestClientImpl.java:104)
at org.kasun.sample.client.GroupRestClientImplTest.addUserToAGroup(GroupRestClientImplTest.java:102
Please find my classes as follows,
Class been tested:
import com.google.gson.Gson;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import org.testing.kasun.client.supportjira.dto.SaveResult;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class GroupRestClientImpl{
private Gson gson = new Gson();
#Override
public SaveResult addUser(User user, Group group) {
WebResource resource = client.resource(baseUri + "/" + GROUP_URI_PREFIX + "/user?groupname=" + group.getName());
ClientResponse response = resource.accept(MediaType.APPLICATION_JSON).type(MediaType.APPLICATION_JSON)
.post(ClientResponse.class, user);
String jsonText;
if (response.getStatus() != Response.Status.CREATED.getStatusCode()) {
jsonText = response.getEntity(String.class);
JiraError jiraError = gson.fromJson(jsonText, JiraError.class);
throw new JiraException(jiraError.toString());
}
jsonText = response.getEntity(String.class);
SaveResult saveResults = gson.fromJson(jsonText, SaveResult.class);
return saveResults;
}
}
Test Classes:
class TestBase {
static final String JIRA_API_URL = "http://www.jira.com/jira/rest/api/2";
static final String MEDIA_TYPE_JSON = MediaType.APPLICATION_JSON;
#Mock
Client client;
#Mock
WebResource webResource;
#Mock
WebResource.Builder webResourceBuilder;
#Mock
ClientResponse clientResponse;
#Mock
Gson gson;
void setupMocks(Class<?> postPayloadType) {
initMocks(this);
when(client.resource(anyString())).thenReturn(webResource);
when(webResource.accept(anyString())).thenReturn(webResourceBuilder);
when(webResourceBuilder.type(anyString())).thenReturn(webResourceBuilder);
when(webResourceBuilder.get(eq(ClientResponse.class))).thenReturn(clientResponse);
when(webResourceBuilder.post(eq(ClientResponse.class), any(postPayloadType))).thenReturn(clientResponse);
when(clientResponse.getEntity(eq(String.class))).thenReturn("responseText");
}
#AfterMethod
protected void clearMocks() {
reset(client);
reset(webResource);
reset(webResourceBuilder);
reset(clientResponse);
reset(gson);
}
}
public class GroupRestClientImplTest extends TestBase {
private static final String JIRA_GROUP_API_URL = JIRA_API_URL + "/group";
private static final String JIRA_GROUP_MEMBER_API_URL = JIRA_GROUP_API_URL + "/member?groupname=";
private static final String JIRA_GROUP_MEMBER_ADD_API_URL = JIRA_GROUP_API_URL + "/user?groupname=";
private GroupRestClient groupRestClient;
#BeforeMethod
public void initialize() throws URISyntaxException {
super.setupMocks(Group.class);
groupRestClient = new GroupRestClientImpl(new URI(JIRA_API_URL), client);
}
#Test
public void addUserToAGroup() throws URISyntaxException {
when(clientResponse.getStatus()).thenReturn(Response.Status.CREATED.getStatusCode());
when(webResourceBuilder.post(eq(ClientResponse.class), any(User.class))).thenReturn(clientResponse);
SaveResult saveResult = new SaveResult();
when(gson.fromJson(anyString(), isA(SaveResult.class.getClass()))).thenReturn(saveResult);
// when(gson.fromJson(anyString(), eq(SaveResult.class))).thenReturn(saveResult);
User user = new User();
Group group = new Group();
group.setName("group");
SaveResult result = groupRestClient.addUser(user, group);
// Test if the SaveResult is correct.
Assert.assertEquals(result, saveResult);
}
According to Mockito's documentation, this is feature is built around Java 9.
This mock maker has been designed around Java Agent runtime attachment ; this require a compatible JVM, that is part of the JDK (or Java 9 VM).
If you have a version prior to 9, you can:
When running on a non-JDK VM prior to Java 9, it is however possible to manually add the Byte Buddy Java agent jar using the -javaagent parameter upon starting the JVM.

How to create the version and get the details of any version of specific project using JIRA REST CLIENT JAVA in groovy?

I am trying to create the version in JIRA for specific project.Below is my code.I am able to connect the JIRA successfully through JIRA REST CLIENT JAVA java libraries but now want to achieve the get the information of any version,createversion few more actions.
import com.atlassian.jira.rest.client.api.JiraRestClient
import com.atlassian.jira.rest.client.api.JiraRestClientFactory
//import com.atlassian.jira.rest.client.api.domain.User
import com.atlassian.jira.rest.client.api.domain.Version
//import com.atlassian.jira.rest.client.api.domain.input.VersionInput
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
import com.atlassian.util.concurrent.Promise
class Jira {
private static final String JIRA_URL = "https://jira.test.com"
private static final String JIRA_ADMIN_USERNAME = "ABCDE"
private static final String JIRA_ADMIN_PASSWORD = "xxxxxx"
static void main(String[] args) throws Exception
{
// Construct the JRJC client
System.out.println(String.format("Logging in to %s with username '%s' and password '%s'", JIRA_URL, JIRA_ADMIN_USERNAME, JIRA_ADMIN_PASSWORD))
JiraRestClientFactory factory = new AsynchronousJiraRestClientFactory()
URI uri = new URI(JIRA_URL)
JiraRestClient client = factory.createWithBasicHttpAuthentication(uri, JIRA_ADMIN_USERNAME, JIRA_ADMIN_PASSWORD)
// client.withCloseable {
// it.projectClient.getProject("ABCD").claim().versions.each { println it }
// }
// Invoke the JRJC Client
//Promise<User> promise = client.getUserClient().getUser(JIRA_ADMIN_USERNAME)
//User user = promise.claim()
Promise<Version> promise = client.getVersionRestClient().getVersion(1234)
//Version version = promise.claim()
// Print the result
System.out.println(String.format("Your user's email address is: %s\r\n", user.getEmailAddress()))
}
}
I am able to do some task like to get the email address of any userid.I am trying this in groovy
package com.temp.jira
import com.atlassian.jira.rest.client.api.JiraRestClient
import com.atlassian.jira.rest.client.api.JiraRestClientFactory
import com.atlassian.jira.rest.client.api.domain.BasicProject
import com.atlassian.jira.rest.client.api.domain.Issue
import com.atlassian.jira.rest.client.api.domain.SearchResult
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
import com.atlassian.util.concurrent.Promise
/**
* Created on 7/21/2017.
*/
class Test {
private static final String JIRA_URL = "https://jira.test.com"
private static final String JIRA_ADMIN_USERNAME = "ABCDEF"
private static final String JIRA_ADMIN_PASSWORD = "*****"
private static final String JIRA_PROJECT = "ABCD"
static void main(String[] args) throws Exception
{
// Construct the JRJC client
System.out.println(String.format("Logging in to %s with username '%s' and password '%s'", JIRA_URL, JIRA_ADMIN_USERNAME, JIRA_ADMIN_PASSWORD))
JiraRestClientFactory factory = new AsynchronousJiraRestClientFactory()
URI uri = new URI(JIRA_URL)
JiraRestClient client = factory.createWithBasicHttpAuthentication(uri, JIRA_ADMIN_USERNAME, JIRA_ADMIN_PASSWORD)
for (BasicProject project : client.getProjectClient().getProject(JIRA_PROJECT).claim()) {
System.out.println(project.getKey() + ": " + project.getName())
}
Promise<SearchResult> searchJqlPromise = client.getSearchClient().searchJql("project = $JIRA_PROJECT AND status in (Closed, Completed, Resolved) ORDER BY assignee, resolutiondate");
for (Issue issue : searchJqlPromise.claim().getIssues()) {
System.out.println(issue.getId())
}
// Done
System.out.println("Example complete. Now exiting.")
System.exit(0)
}
}
Stick to the jira rest client api, since you have a typed access provided from the creator o fJira.
Excerpt from my working Groovy code:
/**
* Retrieves the latest unreleased version for the given project.
* #param projectId Given project
* #return latest unreleased version
*/
private Version getVersion(String projectId)
{
def project = restClient.projectClient.getProject(projectId).claim()
def unreleasedVersions = project
.getVersions()
.findAll { version ->
version.released == false
}
if (unreleasedVersions.size() != 1) {
throw new RuntimeException('There are zero or more unreleased versions.')
}
unreleasedVersions.get(0)
}
/**
* Creates a new, undeployed version for the given project.
* #param projectId Given project
*/
private void createVersion(String projectId)
{
def versionInputBuilder = new VersionInputBuilder(projectId)
versionInputBuilder.released = false
versionInputBuilder.name = incrementVersion(capturedVersion.name)
versionInputBuilder.description = 'Bugfixing'
versionInputBuilder.startDate = DateTime.now()
def promise = restClient.versionRestClient.createVersion(versionInputBuilder.build())
trackCompletion(promise)
}
What I don't have in my code is get a certain version, but your commented code should work when you change the data type of the version to string.
I created the script in below way and able to create,update and delete the version
import com.atlassian.jira.rest.client.api.JiraRestClient
import com.atlassian.jira.rest.client.api.VersionRestClient
import com.atlassian.jira.rest.client.api.domain.Version
import com.atlassian.jira.rest.client.api.domain.input.VersionInput
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
import com.atlassian.util.concurrent.Promise
import org.codehaus.jettison.json.JSONException
import org.joda.time.DateTime
import java.util.concurrent.ExecutionException
class VersionClient {
private static final String JIRA_URL = "https://jira.test.com"
private static final String JIRA_ADMIN_USERNAME = "ABCDEF"
private static final String JIRA_ADMIN_PASSWORD = "******"
private static final String JIRA_PROJECT = "TEST"
static void main(String[] args)
throws URISyntaxException, InterruptedException, ExecutionException, JSONException {
// Initialize REST Client
final AsynchronousJiraRestClientFactory factory = new AsynchronousJiraRestClientFactory()
final URI uri = new URI("$JIRA_URL")
final JiraRestClient jiraRestClient = factory.createWithBasicHttpAuthentication(uri, "$JIRA_ADMIN_USERNAME", "$JIRA_ADMIN_PASSWORD")
// Get specific client instances
VersionRestClient versionClient = jiraRestClient.getVersionRestClient()
// Create Version
VersionInput versionInput = new VersionInput("$JIRA_PROJECT", "TEST 1.2.8", "Test Version", new DateTime(), false, false)
Promise<Version> version = versionClient.createVersion(versionInput)
Version versionObj = version.get()
System.out.println("Created Version:" + versionObj.getName())
// Invoke the JRJC Client
Promise<Version> promise = versionClient.getVersion(versionObj.getSelf())
Version versionid = promise.claim()
// Print the result
System.out.println(String.format("Version id is: %s\r\n", versionid.getId() + " and URI:" + versionid.getSelf()))
// Release Version using Update
versionClient.updateVersion(versionid.getSelf(),new VersionInput("$JIRA_PROJECT", "TEST 1.2.8", "Test Version", new DateTime(), false, true))
// Delete the Version
versionClient.removeVersion(versionid.getSelf(), null, null).claim()
System.out.println("Deleted Version")
}
}

Apache CXF client to access multiple resourcess

My question might be stupid but I am new in the world of web services and I found a tutorial but I have some doubts about it. It is using Apache CXF. I have the Calculator class which has all the resources, CalculatorStartUp which will startup the server, and the Client class where I have more clients.
Bellow is the code:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/calc")
public class Calculator {
#GET
#Path("/add/{a}/{b}")
#Produces(MediaType.TEXT_PLAIN)
public String addPlainText(#PathParam("a") double a, #PathParam("b") double b) {
return (a + b) + "";
}
#GET
#Path("/sub/{a}/{b}")
#Produces(MediaType.TEXT_PLAIN)
public String subPlainText(#PathParam("a") double a, #PathParam("b") double b) {
return (a - b) + "";
}
}
Server:
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
public class CalculatorStartUp {
public static void main(String[] args) {
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
sf.setResourceClasses(Calculator.class);
sf.setResourceProvider(Calculator.class,
new SingletonResourceProvider(new Calculator()));
sf.setAddress("http://localhost:9999/calcrest/");
Server server = sf.create();
}
}
Client:
import org.apache.cxf.jaxrs.client.WebClient;
public class Client {
final static String REST_URI = "http://localhost:9999/calcrest";
final static String ADD_PATH = "calc/add";
final static String SUB_PATH = "calc/sub";
final static String MUL_PATH = "calc/mul";
final static String DIV_PATH = "calc/div";
public static void main(String[] args) {
int a = 122;
int b = 34;
String s = "";
WebClient plainAddClient = WebClient.create(REST_URI);
plainAddClient.path(ADD_PATH).path(a + "/" + b).accept("text/plain");
s = plainAddClient.get(String.class);
System.out.println(s);
WebClient plainSubClient = WebClient.create(REST_URI);
plainSubClient.path(SUB_PATH).path(a + "/" + b).accept("text/plain");
s = plainSubClient.get(String.class);
System.out.println(s);
}
My questions are:
why there are two clients? what if I write some resources for the mul and div resources? Do I need to add more resources..why write a client for each resource? there has to be a way to create only one client that can access a certain resource.
I saw that when creating a web client you can pass a provider or a list of providers. Can anyone explain what those providers represent?
I would appreciate any help!
For your questions:
You can re-use the same WebClient. If you want to re-use it you need to call the WebClient.back(true) or WebClient.replacePath(path) to update the path and can re-use the same baseURI.
You can use the WebClient.create(String baseAddress, List<?> providers) where the second argument is to provide JAX-RS provider like JacksonJsonProvider. Providers are used to customize the JAX-RS runtime. WebClient client = WebClient.create(REST_URI,
Collections.singletonList(new JacksonJsonProvider()));
More about JAX-RS providers: What does Provider in JAX-RS mean?

Jackrabbit WebDAV Synchronization Examples?

I'm using the Jackrabbit library for communicating with a cloud storage using the webdav protocol. I need a way to list all files from a specific directory and get the last modified property but I can't seem to find any working examples on this.
I basically need code to synchronize files from the local directory with the webdav url.
import java.io.File;
import java.io.FileInputStream;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.jackrabbit.webdav.client.methods.DavMethod;
import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
import org.apache.jackrabbit.webdav.client.methods.PutMethod;
public class WebDavClient
{
private String resourceUrl;
private HttpClient client;
private Credentials credentials;
private DavMethod method;
public WebDavClient(String resourceUrl, String username, String password)
throws Exception
{
this.resourceUrl = resourceUrl;
client = new HttpClient();
credentials = new UsernamePasswordCredentials(username, password);
client.getState().setCredentials(AuthScope.ANY, credentials);
}
public int upload(String fileToUpload) throws Exception
{
method = new PutMethod(getUpdatedWebDavPath(fileToUpload));
RequestEntity requestEntity = new InputStreamRequestEntity(
new FileInputStream(fileToUpload));
((PutMethod) method).setRequestEntity(requestEntity);
client.executeMethod(method);
return method.getStatusCode();
}
public int createFolder(String folder) throws Exception
{
method = new MkColMethod(getUpdatedWebDavPath(folder));
client.executeMethod(method);
return method.getStatusCode();
}
private String getUpdatedWebDavPath(String file)
{
// Make sure file names do not contain spaces
return resourceUrl + "/" + new File(file).getName().replace(" ", "");
}
}
Usage example for uploading the file Test.txt to the Backup folder:
String myAccountName = "...";
String myPassword = "...";
WebDavClient webdavUploader = new WebDavClient("https:\\\\webdav.hidrive.strato.com\\users\\" + myAccountName + "\\Backup", myAccountName, myPassword);
webdavUploader.upload("C:\\Users\\Username\\Desktop\\Test.txt");
Here's a list of different DavMethods that could be helpful:
http://jackrabbit.apache.org/api/1.6/org/apache/jackrabbit/webdav/client/methods/package-summary.html
Please help, I'm been struggling on this for so long!
Take a look at the AMES WebDAV Client code from Krusche and Partner on the EU portal. It is licensed under GPL, so should it may fit your purpose.
https://joinup.ec.europa.eu/svn/ames-web-service/trunk/AMES-WebDAV/ames-webdav/src/de/kp/ames/webdav/WebDAVClient.java
It works for me, though to access e.g. Win32LastModifiedTime I need to get the custom namespace, e.g.
private static final Namespace WIN32_NAMESPACE = Namespace.getNamespace("Z2", "urn:schemas-microsoft-com:");
and retrieve the custom Property Win32LastModifiedTime from the properties.
/*
* Win32LastModifiedTime
*/
String win32lastmodifiedtime = null;
DavProperty<?> Win32LastModifiedTime = properties.get("Win32LastModifiedTime", WIN32_NAMESPACE);
if ((Win32LastModifiedTime != null) && (Win32LastModifiedTime.getValue() != null)) win32lastmodifiedtime = Win32LastModifiedTime.getValue().toString();

Simple HTTP server in Java using only Java SE API

Is there a way to create a very basic HTTP server (supporting only GET/POST) in Java using just the Java SE API, without writing code to manually parse HTTP requests and manually format HTTP responses? The Java SE API nicely encapsulates the HTTP client functionality in HttpURLConnection, but is there an analog for HTTP server functionality?
Just to be clear, the problem I have with a lot of ServerSocket examples I've seen online is that they do their own request parsing/response formatting and error handling, which is tedious, error-prone, and not likely to be comprehensive, and I'm trying to avoid it for those reasons.
Since Java SE 6, there's a builtin HTTP server in Sun Oracle JRE. The Java 9 module name is jdk.httpserver. The com.sun.net.httpserver package summary outlines the involved classes and contains examples.
Here's a kickoff example copypasted from their docs. You can just copy'n'paste'n'run it on Java 6+.
(to all people trying to edit it nonetheless, because it's an ugly piece of code, please don't, this is a copy paste, not mine, moreover you should never edit quotations unless they have changed in the original source)
package com.stackoverflow.q3732109;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class Test {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/test", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
}
static class MyHandler implements HttpHandler {
#Override
public void handle(HttpExchange t) throws IOException {
String response = "This is the response";
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}
Noted should be that the response.length() part in their example is bad, it should have been response.getBytes().length. Even then, the getBytes() method must explicitly specify the charset which you then specify in the response header. Alas, albeit misguiding to starters, it's after all just a basic kickoff example.
Execute it and go to http://localhost:8000/test and you'll see the following response:
This is the response
As to using com.sun.* classes, do note that this is, in contrary to what some developers think, absolutely not forbidden by the well known FAQ Why Developers Should Not Write Programs That Call 'sun' Packages. That FAQ concerns the sun.* package (such as sun.misc.BASE64Encoder) for internal usage by the Oracle JRE (which would thus kill your application when you run it on a different JRE), not the com.sun.* package. Sun/Oracle also just develop software on top of the Java SE API themselves like as every other company such as Apache and so on. Moreover, this specific HttpServer must be present in every JDK so there is absolutely no means of "portability" issue like as would happen with sun.* package. Using com.sun.* classes is only discouraged (but not forbidden) when it concerns an implementation of a certain Java API, such as GlassFish (Java EE impl), Mojarra (JSF impl), Jersey (JAX-RS impl), etc.
Check out NanoHttpd
NanoHTTPD is a light-weight HTTP server designed for embedding in other applications, released under a Modified BSD licence.
It is being developed at Github and uses Apache Maven for builds & unit testing"
The com.sun.net.httpserver solution is not portable across JREs. Its better to use the official webservices API in javax.xml.ws to bootstrap a minimal HTTP server...
import java.io._
import javax.xml.ws._
import javax.xml.ws.http._
import javax.xml.transform._
import javax.xml.transform.stream._
#WebServiceProvider
#ServiceMode(value=Service.Mode.PAYLOAD)
class P extends Provider[Source] {
def invoke(source: Source) = new StreamSource( new StringReader("<p>Hello There!</p>"));
}
val address = "http://127.0.0.1:8080/"
Endpoint.create(HTTPBinding.HTTP_BINDING, new P()).publish(address)
println("Service running at "+address)
println("Type [CTRL]+[C] to quit!")
Thread.sleep(Long.MaxValue)
EDIT: this actually works! The above code looks like Groovy or something. Here is a translation to Java which I tested:
import java.io.*;
import javax.xml.ws.*;
import javax.xml.ws.http.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
#WebServiceProvider
#ServiceMode(value = Service.Mode.PAYLOAD)
public class Server implements Provider<Source> {
public Source invoke(Source request) {
return new StreamSource(new StringReader("<p>Hello There!</p>"));
}
public static void main(String[] args) throws InterruptedException {
String address = "http://127.0.0.1:8080/";
Endpoint.create(HTTPBinding.HTTP_BINDING, new Server()).publish(address);
System.out.println("Service running at " + address);
System.out.println("Type [CTRL]+[C] to quit!");
Thread.sleep(Long.MAX_VALUE);
}
}
I like this question because this is an area where there's continuous innovation and there's always a need to have a light server especially when talking about embedded servers in small(er) devices. I think answers fall into two broad groups.
Thin-server: server-up static content with minimal processing, context or session processing.
Small-server: ostensibly a has many httpD-like server qualities with as small a footprint as you can get away with.
While I might consider HTTP libraries like: Jetty, Apache Http Components, Netty and others to be more like a raw HTTP processing facilities. The labelling is very subjective, and depends on the kinds of thing you've been call-on to deliver for small-sites. I make this distinction in the spirit of the question, particularly the remark about...
"...without writing code to manually parse HTTP requests and manually format HTTP responses..."
These raw tools let you do that (as described in other answers). They don't really lend themselves to a ready-set-go style of making a light, embedded or mini-server. A mini-server is something that can give you similar functionality to a full-function web server (like say, Tomcat) without bells and whistles, low volume, good performance 99% of the time. A thin-server seems closer to the original phrasing just a bit more than raw perhaps with a limited subset functionality, enough to make you look good 90% of the time. My idea of raw would be makes me look good 75% - 89% of the time without extra design and coding. I think if/when you reach the level of WAR files, we've left the "small" for bonsi servers that looks like everything a big server does smaller.
Thin-server options
Grizzly
UniRest (multiple-languages)
NanoHTTPD (just one file)
Mini-server options:
Spark Java ... Good things are possible with lots of helper constructs like Filters, Templates, etc.
MadVoc ... aims to be bonsai and could well be such ;-)
Among the other things to consider, I'd include authentication, validation, internationalisation, using something like FreeMaker or other template tool to render page output. Otherwise managing HTML editing and parameterisation is likely to make working with HTTP look like noughts-n-crosses. Naturally it all depends on how flexible you need to be. If it's a menu-driven FAX machine it can be very simple. The more interactions, the 'thicker' your framework needs to be. Good question, good luck!
Have a look at the "Jetty" web server Jetty. Superb piece of Open Source software that would seem to meet all your requirments.
If you insist on rolling your own then have a look at the "httpMessage" class.
Once upon a time I was looking for something similar - a lightweight yet fully functional HTTP server that I could easily embed and customize. I found two types of potential solutions:
Full servers that are not all that lightweight or simple (for an extreme definition of lightweight.)
Truly lightweight servers that aren't quite HTTP servers, but glorified ServerSocket examples that are not even remotely RFC-compliant and don't support commonly needed basic functionality.
So... I set out to write JLHTTP - The Java Lightweight HTTP Server.
You can embed it in any project as a single (if rather long) source file, or as a ~50K jar (~35K stripped) with no dependencies. It strives to be RFC-compliant and includes extensive documentation and many useful features while keeping bloat to a minimum.
Features include: virtual hosts, file serving from disk, mime type mappings via standard mime.types file, directory index generation, welcome files, support for all HTTP methods, conditional ETags and If-* header support, chunked transfer encoding, gzip/deflate compression, basic HTTPS (as provided by the JVM), partial content (download continuation), multipart/form-data handling for file uploads, multiple context handlers via API or annotations, parameter parsing (query string or x-www-form-urlencoded body), etc.
I hope others find it useful :-)
Spark is the simplest, here is a quick start guide: http://sparkjava.com/
All the above answers details about Single main threaded Request Handler.
setting:
server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
Allows multiple request serving via multiple threads using executor service.
So the end code will be something like below:
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class App {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/test", new MyHandler());
//Thread control is given to executor service.
server.setExecutor(java.util.concurrent.Executors.newCachedThreadPool());
server.start();
}
static class MyHandler implements HttpHandler {
#Override
public void handle(HttpExchange t) throws IOException {
String response = "This is the response";
long threadId = Thread.currentThread().getId();
System.out.println("I am thread " + threadId );
response = response + "Thread Id = "+threadId;
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
}
It's possible to create an httpserver that provides basic support for J2EE servlets with just the JDK and the servlet api in a just a few lines of code.
I've found this very useful for unit testing servlets, as it starts much faster than other lightweight containers (we use jetty for production).
Most very lightweight httpservers do not provide support for servlets, but we need them, so I thought I'd share.
The below example provides basic servlet support, or throws and UnsupportedOperationException for stuff not yet implemented. It uses the com.sun.net.httpserver.HttpServer for basic http support.
import java.io.*;
import java.lang.reflect.*;
import java.net.InetSocketAddress;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
#SuppressWarnings("deprecation")
public class VerySimpleServletHttpServer {
HttpServer server;
private String contextPath;
private HttpHandler httpHandler;
public VerySimpleServletHttpServer(String contextPath, HttpServlet servlet) {
this.contextPath = contextPath;
httpHandler = new HttpHandlerWithServletSupport(servlet);
}
public void start(int port) throws IOException {
InetSocketAddress inetSocketAddress = new InetSocketAddress(port);
server = HttpServer.create(inetSocketAddress, 0);
server.createContext(contextPath, httpHandler);
server.setExecutor(null);
server.start();
}
public void stop(int secondsDelay) {
server.stop(secondsDelay);
}
public int getServerPort() {
return server.getAddress().getPort();
}
}
final class HttpHandlerWithServletSupport implements HttpHandler {
private HttpServlet servlet;
private final class RequestWrapper extends HttpServletRequestWrapper {
private final HttpExchange ex;
private final Map<String, String[]> postData;
private final ServletInputStream is;
private final Map<String, Object> attributes = new HashMap<>();
private RequestWrapper(HttpServletRequest request, HttpExchange ex, Map<String, String[]> postData, ServletInputStream is) {
super(request);
this.ex = ex;
this.postData = postData;
this.is = is;
}
#Override
public String getHeader(String name) {
return ex.getRequestHeaders().getFirst(name);
}
#Override
public Enumeration<String> getHeaders(String name) {
return new Vector<String>(ex.getRequestHeaders().get(name)).elements();
}
#Override
public Enumeration<String> getHeaderNames() {
return new Vector<String>(ex.getRequestHeaders().keySet()).elements();
}
#Override
public Object getAttribute(String name) {
return attributes.get(name);
}
#Override
public void setAttribute(String name, Object o) {
this.attributes.put(name, o);
}
#Override
public Enumeration<String> getAttributeNames() {
return new Vector<String>(attributes.keySet()).elements();
}
#Override
public String getMethod() {
return ex.getRequestMethod();
}
#Override
public ServletInputStream getInputStream() throws IOException {
return is;
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
#Override
public String getPathInfo() {
return ex.getRequestURI().getPath();
}
#Override
public String getParameter(String name) {
String[] arr = postData.get(name);
return arr != null ? (arr.length > 1 ? Arrays.toString(arr) : arr[0]) : null;
}
#Override
public Map<String, String[]> getParameterMap() {
return postData;
}
#Override
public Enumeration<String> getParameterNames() {
return new Vector<String>(postData.keySet()).elements();
}
}
private final class ResponseWrapper extends HttpServletResponseWrapper {
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
final ServletOutputStream servletOutputStream = new ServletOutputStream() {
#Override
public void write(int b) throws IOException {
outputStream.write(b);
}
};
private final HttpExchange ex;
private final PrintWriter printWriter;
private int status = HttpServletResponse.SC_OK;
private ResponseWrapper(HttpServletResponse response, HttpExchange ex) {
super(response);
this.ex = ex;
printWriter = new PrintWriter(servletOutputStream);
}
#Override
public void setContentType(String type) {
ex.getResponseHeaders().add("Content-Type", type);
}
#Override
public void setHeader(String name, String value) {
ex.getResponseHeaders().add(name, value);
}
#Override
public javax.servlet.ServletOutputStream getOutputStream() throws IOException {
return servletOutputStream;
}
#Override
public void setContentLength(int len) {
ex.getResponseHeaders().add("Content-Length", len + "");
}
#Override
public void setStatus(int status) {
this.status = status;
}
#Override
public void sendError(int sc, String msg) throws IOException {
this.status = sc;
if (msg != null) {
printWriter.write(msg);
}
}
#Override
public void sendError(int sc) throws IOException {
sendError(sc, null);
}
#Override
public PrintWriter getWriter() throws IOException {
return printWriter;
}
public void complete() throws IOException {
try {
printWriter.flush();
ex.sendResponseHeaders(status, outputStream.size());
if (outputStream.size() > 0) {
ex.getResponseBody().write(outputStream.toByteArray());
}
ex.getResponseBody().flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
ex.close();
}
}
}
public HttpHandlerWithServletSupport(HttpServlet servlet) {
this.servlet = servlet;
}
#SuppressWarnings("deprecation")
#Override
public void handle(final HttpExchange ex) throws IOException {
byte[] inBytes = getBytes(ex.getRequestBody());
ex.getRequestBody().close();
final ByteArrayInputStream newInput = new ByteArrayInputStream(inBytes);
final ServletInputStream is = new ServletInputStream() {
#Override
public int read() throws IOException {
return newInput.read();
}
};
Map<String, String[]> parsePostData = new HashMap<>();
try {
parsePostData.putAll(HttpUtils.parseQueryString(ex.getRequestURI().getQuery()));
// check if any postdata to parse
parsePostData.putAll(HttpUtils.parsePostData(inBytes.length, is));
} catch (IllegalArgumentException e) {
// no postData - just reset inputstream
newInput.reset();
}
final Map<String, String[]> postData = parsePostData;
RequestWrapper req = new RequestWrapper(createUnimplementAdapter(HttpServletRequest.class), ex, postData, is);
ResponseWrapper resp = new ResponseWrapper(createUnimplementAdapter(HttpServletResponse.class), ex);
try {
servlet.service(req, resp);
resp.complete();
} catch (ServletException e) {
throw new IOException(e);
}
}
private static byte[] getBytes(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
while (true) {
int r = in.read(buffer);
if (r == -1)
break;
out.write(buffer, 0, r);
}
return out.toByteArray();
}
#SuppressWarnings("unchecked")
private static <T> T createUnimplementAdapter(Class<T> httpServletApi) {
class UnimplementedHandler implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
throw new UnsupportedOperationException("Not implemented: " + method + ", args=" + Arrays.toString(args));
}
}
return (T) Proxy.newProxyInstance(UnimplementedHandler.class.getClassLoader(),
new Class<?>[] { httpServletApi },
new UnimplementedHandler());
}
}
You may also have a look at some NIO application framework such as:
Netty: http://jboss.org/netty
Apache Mina: http://mina.apache.org/ or its subproject AsyncWeb: http://mina.apache.org/asyncweb/
This code is better than ours, you only need to add 2 libs: javax.servelet.jar and org.mortbay.jetty.jar.
Class Jetty:
package jetty;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.mortbay.http.SocketListener;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.servlet.ServletHttpContext;
public class Jetty {
public static void main(String[] args) {
try {
Server server = new Server();
SocketListener listener = new SocketListener();
System.out.println("Max Thread :" + listener.getMaxThreads() + " Min Thread :" + listener.getMinThreads());
listener.setHost("localhost");
listener.setPort(8070);
listener.setMinThreads(5);
listener.setMaxThreads(250);
server.addListener(listener);
ServletHttpContext context = (ServletHttpContext) server.getContext("/");
context.addServlet("/MO", "jetty.HelloWorldServlet");
server.start();
server.join();
/*//We will create our server running at http://localhost:8070
Server server = new Server();
server.addListener(":8070");
//We will deploy our servlet to the server at the path '/'
//it will be available at http://localhost:8070
ServletHttpContext context = (ServletHttpContext) server.getContext("/");
context.addServlet("/MO", "jetty.HelloWorldServlet");
server.start();
*/
} catch (Exception ex) {
Logger.getLogger(Jetty.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Servlet class:
package jetty;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloWorldServlet extends HttpServlet
{
#Override
protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException
{
String appid = httpServletRequest.getParameter("appid");
String conta = httpServletRequest.getParameter("conta");
System.out.println("Appid : "+appid);
System.out.println("Conta : "+conta);
httpServletResponse.setContentType("text/plain");
PrintWriter out = httpServletResponse.getWriter();
out.println("Hello World!");
out.close();
}
}
I can strongly recommend looking into Simple, especially if you don't need Servlet capabilities but simply access to the request/reponse objects. If you need REST you can put Jersey on top of it, if you need to output HTML or similar there's Freemarker. I really love what you can do with this combination, and there is relatively little API to learn.
Starting in Java 18, you can create simple web servers with Java standard library:
class Main {
public static void main(String[] args) {
var port = 8000;
var rootDirectory = Path.of("C:/Users/Mahozad/Desktop/");
var outputLevel = OutputLevel.VERBOSE;
var server = SimpleFileServer.createFileServer(
new InetSocketAddress(port),
rootDirectory,
outputLevel
);
server.start();
}
}
This will, by default, show a directory listing of the root directory you specified. You can place an index.html file (and other assets like CSS and JS files) in that directory to show them instead.
Example (I put these in Desktop which was specified as my directory root above):
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Java 18 Simple Web Server</title>
<link rel="stylesheet" href="styles.css">
<style>h1 { color: blue; }</style>
<script src="scripts.js" defer>
let element = document.getElementsByTagName("h1")[0];
element.style.fontSize = "48px";
</script>
</head>
<body>
<h1>I'm <i>index.html</i> in the root directory.</h1>
</body>
</html>
Sidenote
For Java standard library HTTP client, see the post Java 11 new HTTP Client API.
An example of a very basic HTTP server on TCP sockets level:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class NaiveHttpServer {
public static void main(String[] args) throws IOException {
String hostname = InetAddress.getLocalHost().getHostName();
ServerSocket serverSocket = new ServerSocket(8089);
while (true) {
Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String s = in.readLine();
System.out.println(s);
while ("\r\n".equals(in.readLine()));
if ("GET /hostname HTTP/1.1".equals(s)) {
out.println("HTTP/1.1 200 OK");
out.println("Connection: close");
out.println("Content-Type: text/plain");
out.println("Content-Length:" + hostname.length());
out.println();
out.println(hostname);
} else {
out.println("HTTP/1.1 404 Not Found");
out.println("Connection: close");
out.println();
}
out.flush();
}
}
}
The example serves the hostname of the computer.
checkout Simple. its a pretty simple embeddable server with built in support for quite a variety of operations. I particularly love its threading model..
Amazing!
Check out takes. Look at https://github.com/yegor256/takes for quick info
Try this https://github.com/devashish234073/Java-Socket-Http-Server/blob/master/README.md
This API has creates an HTTP server using sockets.
It gets a request from the browser as text
Parses it to retrieve URL info, method, attributes, etc.
Creates dynamic response using the URL mapping defined
Sends the response to the browser.
For example the here's how the constructor in the Response.java class converts a raw response into an http response:
public Response(String resp){
Date date = new Date();
String start = "HTTP/1.1 200 OK\r\n";
String header = "Date: "+date.toString()+"\r\n";
header+= "Content-Type: text/html\r\n";
header+= "Content-length: "+resp.length()+"\r\n";
header+="\r\n";
this.resp=start+header+resp;
}
How about Apache Commons HttpCore project?
From the web site:...
HttpCore Goals
Implementation of the most fundamental HTTP transport aspects
Balance between good performance and the clarity & expressiveness of
API
Small (predictable) memory footprint
Self contained library (no external dependencies beyond JRE)
You can write a pretty simple embedded Jetty Java server.
Embedded Jetty means that the server (Jetty) shipped together with the application as opposed of deploying the application on external Jetty server.
So if in non-embedded approach your webapp built into WAR file which deployed to some external server (Tomcat / Jetty / etc), in embedded Jetty, you write the webapp and instantiate the jetty server in the same code base.
An example for embedded Jetty Java server you can git clone and use: https://github.com/stas-slu/embedded-jetty-java-server-example
The old com.sun.net.httpserver is again a public and accepted API, since Java 11. You can get it as HttpServer class, available as part of jdk.httpserver module. See https://docs.oracle.com/en/java/javase/11/docs/api/jdk.httpserver/com/sun/net/httpserver/HttpServer.html
This class implements a simple HTTP server. A HttpServer is bound to an IP address and port number and listens for incoming TCP connections from clients on this address. The sub-class HttpsServer implements a server which handles HTTPS requests.
So, apart from its limitations, there is no reason to avoid its use anymore.
I use it to publish a control interface in server applications. Reading the User-agent header from a client request I even respond in text/plain to CLI tools like curl or in more elegant HTML way to any other browser.
Cool and easy.
Here is my simple webserver, used in JMeter for testing webhooks (that's why it will close and end itself after request is received).
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServer {
private static int extractContentLength(StringBuilder sb) {
int length = 0;
String[] lines = sb.toString().split("\\n");
for (int i = 0; i < lines.length; i++) {
String s = lines[i];
if (s.toLowerCase().startsWith("Content-Length:".toLowerCase()) && i <= lines.length - 2) {
String slength = s.substring(s.indexOf(":") + 1, s.length()).trim();
length = Integer.parseInt(slength);
System.out.println("Length = " + length);
return length;
}
}
return 0;
}
public static void main(String[] args) throws IOException {
int port = Integer.parseInt(args[0]);
System.out.println("starting HTTP Server on port " + port);
StringBuilder outputString = new StringBuilder(1000);
ServerSocket serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(3 * 60 * 1000); // 3 minutes timeout
while (true) {
outputString.setLength(0); // reset buff
Socket clientSocket = serverSocket.accept(); // blocking
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
try {
boolean isBodyRead = false;
int dataBuffer;
while ((dataBuffer = clientSocket.getInputStream().read()) != -1) {
if (dataBuffer == 13) { // CR
if (clientSocket.getInputStream().read() == 10) { // LF
outputString.append("\n");
}
} else {
outputString.append((char) dataBuffer);
}
// do we have Content length
int len = extractContentLength(outputString);
if (len > 0) {
int actualLength = len - 1; // we need to substract \r\n
for (int i = 0; i < actualLength; i++) {
int body = clientSocket.getInputStream().read();
outputString.append((char) body);
}
isBodyRead = true;
break;
}
} // end of reading while
if (isBodyRead) {
// response headers
out.println("HTTP/1.1 200 OK");
out.println("Connection: close");
out.println(); // must have empty line for HTTP
out.flush();
out.close(); // close clients connection
}
} catch (IOException ioEx) {
System.out.println(ioEx.getMessage());
}
System.out.println(outputString.toString());
break; // stop server - break while true
} // end of outer while true
serverSocket.close();
} // end of method
}
You can test it like this:
curl -X POST -H "Content-Type: application/json" -H "Connection: close" -d '{"name": "gustinmi", "email": "gustinmi at google dot com "}' -v http://localhost:8081/
I had some fun, I toyed around and pieced together this. I hope it helps you.
You are going to need Gradle installed or use Maven with a plugin.
build.gradle
plugins {
id 'application'
}
group 'foo.bar'
version '1.0'
repositories {
mavenCentral()
}
application{
mainClass.set("foo.FooServer")
}
dependencies {}
FooServer
The main entry point, your main class.
package foo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FooServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(7654);
serverSocket.setPerformancePreferences(0, 1, 2);
/* the higher the numbers, the better the concurrent performance, ha!
we found that a 3:7 ratio to be optimal
3 partitioned executors to 7 network executors */
ExecutorService executors = Executors.newFixedThreadPool(3);
executors.execute(new PartitionedExecutor(serverSocket));
}
public static class PartitionedExecutor implements Runnable {
ServerSocket serverSocket;
public PartitionedExecutor(ServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
#Override
public void run() {
ExecutorService executors = Executors.newFixedThreadPool(30);
executors.execute(new NetworkRequestExecutor(serverSocket, executors));
}
}
public static class NetworkRequestExecutor implements Runnable{
String IGNORE_CHROME = "/favicon.ico";
String BREAK = "\r\n";
String DOUBLEBREAK = "\r\n\r\n";
Integer REQUEST_METHOD = 0;
Integer REQUEST_PATH = 1;
Integer REQUEST_VERSION = 2;
String RENDERER;
Socket socketClient;
ExecutorService executors;
ServerSocket serverSocket;
public NetworkRequestExecutor(ServerSocket serverSocket, ExecutorService executors){
this.serverSocket = serverSocket;
this.executors = executors;
}
#Override
public void run() {
try {
socketClient = serverSocket.accept();
Thread.sleep(19);//do this for safari, its a hack but safari requires something like this.
InputStream requestInputStream = socketClient.getInputStream();
OutputStream clientOutput = socketClient.getOutputStream();
if (requestInputStream.available() == 0) {
requestInputStream.close();
clientOutput.flush();
clientOutput.close();
executors.execute(new NetworkRequestExecutor(serverSocket, executors));
return;
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int bytesRead;
while ((bytesRead = requestInputStream.read(byteBuffer.array())) != -1) {
byteArrayOutputStream.write(byteBuffer.array(), 0, bytesRead);
if (requestInputStream.available() == 0) break;
}
String completeRequestContent = byteArrayOutputStream.toString();
String[] requestBlocks = completeRequestContent.split(DOUBLEBREAK, 2);
String headerComponent = requestBlocks[0];
String[] methodPathComponentsLookup = headerComponent.split(BREAK);
String methodPathComponent = methodPathComponentsLookup[0];
String[] methodPathVersionComponents = methodPathComponent.split("\\s");
String requestVerb = methodPathVersionComponents[REQUEST_METHOD];
String requestPath = methodPathVersionComponents[REQUEST_PATH];
String requestVersion = methodPathVersionComponents[REQUEST_VERSION];
if (requestPath.equals(IGNORE_CHROME)) {
requestInputStream.close();
clientOutput.flush();
clientOutput.close();
executors.execute(new NetworkRequestExecutor(serverSocket, executors));
return;
}
ConcurrentMap<String, String> headers = new ConcurrentHashMap<>();
String[] headerComponents = headerComponent.split(BREAK);
for (String headerLine : headerComponents) {
String[] headerLineComponents = headerLine.split(":");
if (headerLineComponents.length == 2) {
String fieldKey = headerLineComponents[0].trim();
String content = headerLineComponents[1].trim();
headers.put(fieldKey.toLowerCase(), content);
}
}
clientOutput.write("HTTP/1.1 200 OK".getBytes());
clientOutput.write(BREAK.getBytes());
Integer bytesLength = "hi".length();
String contentLengthBytes = "Content-Length:" + bytesLength;
clientOutput.write(contentLengthBytes.getBytes());
clientOutput.write(BREAK.getBytes());
clientOutput.write("Server: foo server".getBytes());
clientOutput.write(BREAK.getBytes());
clientOutput.write("Content-Type: text/html".getBytes());
clientOutput.write(DOUBLEBREAK.getBytes());
clientOutput.write("hi".getBytes());
clientOutput.close();
socketClient.close();
executors.execute(new NetworkRequestExecutor(serverSocket, executors));
} catch (IOException ex) {
ex.printStackTrace();
} catch (InterruptedException ioException) {
ioException.printStackTrace();
}
}
}
}
Run it:
gradle run
Browse to:
http://localhost:7654/

Categories

Resources