Пишем сервер на java. Самый "легкий" Java - сервер
Как вы наверное уже знаете - L2jServer сменил политику обновлений своих кодов и теперь обновления выходят примерно один раз в месяц, но тем не менее, код остается открытым и доступным для всех желающих изучить и установить Freya.
→
L2Teon проделал почти 150 корректировок кода с момента выхода прошлой ревизии java сервера Lineage 2 Interlude. Благодаря этому была повышена безопасность и отказоустойчивость сервера.
Обновлена защита заточки предметов, исправлен спавн-лист.
Исправлен баг с питомцами.
Отключена загрузка AI с ядра, теперь только с «\data\scripts\ai\».
Исправлены умения: Curse of Doom, Anchor, Mirage.
Исправлен баг со складом (не верно указаны данные FloodProtector).
Исправлен баг с исчезновением питомца после смерти.
Исправлены умения Spell Force и Batle Force.
Закрыта возможность чрезмерной заточки предметов.
Исправление записей таблицы NPC благодаря которому нет ошибок в консоли.
→
Разработка Java сервера Lineage 2 Gracia Epilogue или просто Plus продолжается, и сегодня для владельцев серверов на базе L2Open-Team доступно следующее обновление.
Добавлена опция отвечающая за шанс поднятия уровня Soul Crystal.
Исправлен третий этаж в Steel Citadel, точнее возможность на него перейти.
Добавлено восстановление маны Шаманом Орков при ударах.
Исправлено получение магической поддержки новичкам.
Исправление невидимости персонажей.
Были добавлены семена манора по Lineage 2 Gracia Epilogue.
Добавлен новый квест Pailaka Injured Dragon (необходим тест).
В движке реализовано умение "Семь Стрел (Seven Arrow)".
Добавлены Эвенты: Последний Герой и Захват Базы.
Администратору доступен телепорт ко всем Рейд Боссам Gracia.
→
В очередной раз вышло обновление сборки сервера Lineage 2 Epilogue от команды L2jServer с ревизией 4309 у ядра и 7529 у датапака.
Исправлено отображение окна действий при совершении трансформации.
Возможность активации кэширования всех имен существующих персонажей.
Исправление проблемы с отключением авто-сосок на одетом оружии.
Добавлена настройка дальности вещания событий с кораблями.
Доступна новая опция включения Чемпионов для спавна на карту.
Реализация клановых дирижаблей, исправление пути полета в квестах.
Добавлена поддержка более точного поиска двойных окон.
Был исправлена ошибка Геодвига из-за которой выводилось "Can"t see target".
В транспортных средствах теперь невозможно производить торговлю.
Дроплист проверяется при загрузке сервера на предмет "левых" предметов.
Есть ли способ создать очень простой HTTP-сервер (поддерживающий только GET/POST) в Java, используя только API Java SE, без написания кода для ручного анализа HTTP-запросов и отформатирования HTTP-ответов вручную? Java SE API прекрасно инкапсулирует функциональность HTTP-клиента в HttpURLConnection, но существует ли аналоговый для HTTP-сервер функционал?
Просто, чтобы быть ясным, проблема, с которой я сталкиваюсь с множеством примеров ServerSocket, которые я видел в Интернете, заключается в том, что они выполняют собственный алгоритм синтаксического анализа/отклика запросов и обработку ошибок, что является утомительным, подверженным ошибкам и вряд ли быть всеобъемлющим, и я стараюсь избегать этого по этим причинам.
В качестве примера ручной HTTP-манипуляции, которую я пытаюсь избежать:
18 ответов
Начиная с Java SE 6 в Sun Oracle JRE имеется встроенный HTTP-сервер. В сводке пакета com.sun.net.httpserver описаны участвующие классы и приведены примеры.
Вот начальный пример, скопированный из их документов (тем не менее, всем, кто пытается его редактировать, потому что это ужасный кусок кода, пожалуйста, не копируйте, а не мой, более того, вы никогда не должны редактировать цитаты, если они не изменились. в первоисточнике). Вы можете просто скопировать и запустить его на Java 6+.
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(); } } }
Следует отметить, что часть response.length() в их примере плохая, это должна была быть response.getBytes().length . Даже в этом getBytes() метод getBytes() должен явно указывать кодировку, которую вы затем указываете в заголовке ответа. Увы, хотя и вводящий в заблуждение начинающих, в конце концов, это всего лишь базовый пример.
Выполните его и перейдите по адресу http://localhost: 8000/test, и вы увидите следующий ответ:
Что касается использования com.sun.* , Обратите внимание, что это, в отличие от того, что думают некоторые разработчики, абсолютно не запрещено общеизвестными часто задаваемыми вопросами. Почему разработчики не должны писать программы, называющие "солнечные" пакеты . Этот sun.misc.BASE64Encoder часто задаваемых вопросов касается пакета sun.* (Такого как sun.misc.BASE64Encoder) для внутреннего использования Oracle JRE (который, таким образом, уничтожит ваше приложение при запуске его на другом JRE), а не пакет com.sun.* . Sun/Oracle также просто разрабатывает программное обеспечение поверх API Java SE, как и любая другая компания, такая как Apache и так далее. Использование com.sun.* рекомендуется (но не запрещено), когда это касается реализации определенного Java API, такого как GlassFish (Java EE impl), Mojarra (JSF impl), Джерси (JAX-RS impl) и т.д.,
Решение com.sun.net.httpserver не переносится через JRE. Лучше использовать официальный API веб-сервисов в javax.xml.ws для загрузки минимального HTTP-сервера...
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 { def invoke(source: Source) = new StreamSource(new StringReader("
Hello There!
")); } val address = "http://127.0.0.1:8080/" Endpoint.create(HTTPBinding.HTTP_BINDING, new P()).publish(address) println("Service running at "+address) println("Type +[C] to quit!") Thread.sleep(Long.MaxValue)EDIT: это действительно работает! Вышеприведенный код выглядит как Groovy или что-то в этом роде. Вот перевод на Java, который я тестировал:
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 Hello There! Посмотрите на веб-сервер "Jetty" Jetty . Превосходная часть программного обеспечения с открытым исходным кодом, которая, похоже, отвечает всем вашим требованиям. Если вы настаиваете на сворачивании своих собственных, посмотрите на класс "httpMessage". Мне нравится этот вопрос, потому что это область, где постоянно развиваются инновации, и всегда нужно иметь легкий сервер, особенно когда речь идет о встроенных серверах на небольших (er) устройствах. Я думаю, что ответы делятся на две широкие группы. Хотя я мог бы подумать о библиотеках HTTP, таких как: Jetty , Apache Http Components , Netty и другие, чтобы быть более похожими на необработанные средства обработки HTTP. Маркировка очень субъективна и зависит от того, что вы делали для небольших сайтов. Я делаю это различие в духе вопроса, особенно замечание о... Эти исходные инструменты позволяют делать это (как описано в других ответах). Они действительно не поддаются готовому дизайну, создавая легкий, встроенный или мини-сервер. Мини-сервер - это то, что может дать вам аналогичную функциональность полнофункциональному веб-серверу (например, Tomcat) без колоколов и свистов, низкий объем, хорошая производительность в 99% случаев. Тонкий сервер кажется ближе к оригинальной фразе, чуть более сырой, возможно, с ограниченным набором функций, достаточным для того, чтобы вы выглядели неплохо в 90% случаев. Мое представление о сыром будет заставлять меня выглядеть хорошо 75% - 89% времени без дополнительного дизайна и кодирования. Я думаю, что если/когда вы достигнете уровня WAR файлов, мы оставили "маленький" для серверов bonsi, который выглядит как все, что делает большой сервер. Параметры тонкого сервера Параметры мини-сервера: Среди других вещей, которые следует учитывать, я бы включил аутентификацию, валидацию, интернационализацию, используя что-то вроде FreeMaker или другой инструмент шаблона для рендеринга страницы вывод. В противном случае управление редактированием и параметризацией HTML, вероятно, приведет к тому, что работа с HTTP будет выглядеть как noughts-n-crosses. Естественно, все зависит от того, насколько вы гибки. Если это факсимильная машина с меню, она может быть очень простой. Чем больше взаимодействий, тем "более толстой" должна быть ваша структура. Хороший вопрос, удачи! Когда-то я искал нечто похожее - легкий, но полностью функциональный HTTP-сервер, который я мог бы легко встроить и настроить. Я нашел два типа потенциальных решений: Вы можете встраивать его в любой проект в виде одиночного (если довольно длинного) исходного файла или в виде баннера размером ~ 50 КБ (~ 35 КБ) без каких-либо зависимостей. Он стремится быть совместимым с RFC и включает в себя обширную документацию и множество полезных функций при сохранении раздувания до минимума. Возможности включают в себя: виртуальные хосты, файлы с диска, сопоставления типа mime через стандартный файл mime.types, генерацию индекса каталога, приветственные файлы, поддержку всех HTTP-методов, условные ETags и поддержку заголовков If- *, кодирование с кодировкой передачи, gzip/сбрасывать сжатие, базовые HTTPS (как предоставлено JVM), частичный контент (продолжение загрузки), обработку мультифайлов/форм файлов для загрузки файлов, несколько обработчиков контекстов через API или аннотации, разбор параметров (строка запроса или x-www-form- urlencoded body) и т.д. Я надеюсь, что другие считают это полезным:-) Возможно создание httpserver, обеспечивающего базовую поддержку сервлетов J2EE только с JDK и сервлета api всего за несколько строк кода. Я нашел это очень полезным для сервлетов для тестирования модулей, поскольку он запускается намного быстрее, чем другие легкие контейнеры (мы используем причал для производства). Большинство очень легких httpservers не обеспечивают поддержку сервлетов, но мы нуждаемся в них, поэтому я думал, что буду делиться. В приведенном ниже примере представлена базовая поддержка сервлетов или выбрасывается и UnsupportedOperationException для тех вещей, которые еще не реализованы. Он использует com.sun.net.httpserver.HttpServer для базовой поддержки http. 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 Я настоятельно рекомендую изучить , особенно если вам не нужны возможности сервлета, а просто доступ к объектам запроса/ответа, Если вам нужен REST, вы можете положить Джерси поверх него, если вам нужно вывести HTML или аналогичный там Freemarker. Мне очень нравится то, что вы можете сделать с этой комбинацией, и относительно немного API для изучения. Этот код лучше нашего, вам нужно только добавить 2 библиотеки: javax.servelet.jar
и org.mortbay.jetty.jar
. Причал класса: 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);
}
}
}
Заинтересовавшись однажды сетевыми технологиями и, среди прочего, серверами, я пришёл к мысли, что было бы неплохо написать одн такой сервер самому, используя Java. Однако большая часть Java-литературы, что попадалась мне на глаза, если и объясняла нечто по теме, то лишь в самых общих чертах, не идя далее обмена текстовыми сообщениями между клиент-серверными приложениями. В интернете подобная информация встречалась не намного чаще, в основном в виде крупиц знаний на обучающийх сайтах или отрывочных сведений от пользователей разнообразных форумов. Таким образом, собрав эти знания воедино и написав таки удобоваримое серверное прилоение, спобоное обрабатывать браузерные запросы, я решил сделать выжимку из подобных знаний и поэтапно описать процесс создания простейшего web-сервера на Java. Итак, поскольку программа предполагает простейшие функции сервера, она будет состоять из одного класса без графического интерфейса. Этот класс (Server) наследует поток и имеет одно поле – сокет: Class Server extends Thread {
Socket s;
}
Try {
ServerSocket server = new ServerSocket(1025);
while(true) {
new Server(server.accept());
}
}
catch(Exception e) {
System.out.println("Error: " + e);
}
Для того, чтобы сделать возможным создание нового потока подобным образом мы, естественно, должны описать для него соответствующий конструктор. В конструкторе мы маркируем поток как демон и здесь же запускаем: Public Server(Socket socket) {
this.socket = socket;
setDaemon(true);
start();
}
Public void run() {
try {
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
Помимо байт-массива, мы также создаем int переменную, которая будет хранить в себе количество реально принятых буфером байт. Это необходимо для того, чтобы в последствии особыми образом создать строку клиентского запроса из полученных данных: Byte buffer = new byte;
int bytes = input.read(buffer);
String request = new String(buf, 0, r);
Не вдаваясь в подробности структуры http-запроса скажу лишь, что нужная нам информация будет находиться в первой строчке данного запроса приблизительно в таком виде: GET /index.html HTTP/1.1
/index.html
String path = getPath(request);
File file = new File(path);
Boolean exists = !file.exists();
if(!exists)
if(file.isDirectory())
if(path.lastIndexOf(""+File.separator) == path.length()-1) {
path = path + "index.html";
}
else {
path = path + File.separator + "index.html";
file = new File(path);
exists = !file.exists();
}
If(exists){
String response = "HTTP/1.1 404 Not Found\n";
response +="Date: " + new Date() + "\n";
response +="Content-Type: text/plain\n";
response +="Connection: close\n";
response +="Server: Server\n";
response +="Pragma: no-cache\n\n";
response += "File " + path + " Not Found!";
Output.write(response.getBytes());
socket.close();
return;
}
Int ex = path.lastIndexOf(".");
String mimeType = “text/plain”;
if(ex > 0) {
String format = path.substring(r);
if(format.equalsIgnoreCase(".html"))
mimeType = "text/html";
else if(format.equalsIgnoreCase(".jpeg"))
mimeType = "image/jpeg";
else if(format.equalsIgnoreCase(".gif"))
mimeType = "image/gif";
String response = "HTTP/1.1 200 OK\n";
response += "Last-Modified: " + new Date(file.lastModified())) + "\n";
response += "Content-Length: " + file.length() + "\n";
response += "Content-Type: " + mimeType + "\n";
response +="Connection: close\n";
Response += "Server: Server\n\n";
output.write(response.getBytes());
FileInputStream fis = new FileInputStream(path);
int write = 1;
while(write > 0) {
write = fis.read(buffer);
if(write > 0) output.write(buffer, 0, write);
}
fis.close();
socket.close();
}
Catch(Exception e) {
e.printStackTrace();
} }
Вы можете помочь и перевести немного средств на развитие сайта
Последнее обновление: 20.09.2018 Класс Java Bean должен соответствовать ряду ограничений: иметь конструктор, который не принимает никаких параметров определять для всех свойств, которые используются в jsp, методы геттеры и сеттеры названия геттеров и сеттеров должны соответствовать условностям: перед именем переменной добавляется get (для геттера) и
set (для сеттера), а название переменной включается с большой буквы. Например, если переменная называется firstName,
то функции геттера и сеттера должны называться соответственно getFirstName и setFirstName. Однако для переменных типа boolean для функции геттера используется вместо get приставка is. Например, переменная
enabled и геттер isEnabled. реализовать интерфейс Serializable или Externalizable Рассмотрим, как использовать классы JavaBean. Допустим, у нас есть следующая структура: В папке Java Resources/src
расположен класс User со следующим кодом: Import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 2041275512219239990L;
private String name;
private int age;
public User() {
this.name = "";
this.age = 0;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Данный класс представляет пользователя и является классом Java Bean: он реализует интерфейс Serializable, имеет конструктор без параметров, а его методы - геттеры и сеттеры,
которые предоставляют доступ к переменным name и age, соответствуют условностям. В папке WebContent
определена страница user.jsp
. Определим в ней следующий код:
Name: ${user.name} Age: ${user.age}
Лучший способ понять устройство и принцип работы чего-либо – сделать это что-то самому.
Надеюсь, в этой статье найдутся полезные знания для начинающих Java-программистов и других людей, изучающих связанные технологии.
В главном методе создаём новый ServerSocket и задаём для него порт (в данном случае использован порт 1025) и в бесконечном цикле ожидаем соединения с клиентом. При наличии соединения мы создаем новый поток, передавая ему соответствующий сокет. В случае неудачи выводим сообщение об ошибке:
Далее описываем функционал потока в методе run(): в первую очередь, создаем из сокета поток исходящих и входящих данных:
Для считывания входящих данных мы будем применять буфер, представляющий из себя байт-массив определёной размерности. Создание подобного буфера не является обязательным, т.к. возможно принимать входящие данные и другими способами, не лимитируя количество принимаемой информации – но для простейшего web-сервера, описанного здесь, вполне достаточно 64 кбайт для получения необходимых данных от клиента.
В строке request будет содержаться http-запрос от клиента. Среди прочей информации, содержащейся в данном запросе, нас в данный момент интересует адрес запрашиваемого файла и его расширение.
В данном примере запрашивается страница на сервере по адресу
Страница с таким адресом выдается на большинстве серверов по умолчанию. Наша задача – с помощью собственноручно написанного метода getPath() вычленить этот адрес из запроса. Существует множество вариантов подобного метода и здесь их приводить нет смысла. Ключевой момент здесь состоит в том, что получив путь до нужного файла и записав его в строковую переменную path, мы можем попробовать создать на основе этих данных файл и, в случае успеха, вернуть этот файл, а в случае неудачи – вернуть специфическое сообщение об ошибке:
Проверяем, является ли данный файл дирекорией. Если такой файл существует и является директорией, то мы возвращаем упомянутый выше файл по умолчанию – index.html:
Если файла по указанному адресу не существует, то мы создаем http-ответ в строке response с указанием того, что файл не найден, добавляя в нужном порядке следующие заголовки:
После формирования строки response мы отправляем их клиенту и закрываем соединение:
Если же файл существует, то перед формированием ответа необходимо выяснить его расширение и, следовательно, MIME-тип. Для начала мы выясним индекс точки, стоящей перед расширением файла и сохраним его в int-переменную.
Затем вычленим расширение файла, стоящее после неё. Список возможным MIME-типов можно расширить, но в данном случае буде использовать всего по одной из форм 3-х форматов: html, jpeg и gif. По умолчанию будем использовать MIME-тип для текста:
Формируем ответ клиенту:
В конце заголовков обязательно должно быть две пустые строки, иначе ответ не будет корректны образом обработан клиентом.
Для отправки самого файла можно использовать следующую конструкцию:
Наконец, завершаем блок try-catch, указанный в начале.
Поскольку, как уже было сказано, данная реализация web-сервера является одной из простейших, она может быть легко доработана путём добавления графического интерфейса пользователя, количества поддерживаемых расширений, ограничителя подключений и т.п. Одним словом – простор для творчества остаётся огромным. Дерзайте.
Данная страница jsp получает извне объект user и с помощью синтаксиса EL выводит значения его свойств. Стоит обратить внимание, что здесь идет обращение к переменным name и age, хотя они являются приватными.
В папке Java Resources/src в файле HelloServlet.java определен сервлет HelloServlet:
Import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/hello") public class HelloServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { User tom = new User("Tom", 25); request.setAttribute("user", tom); getServletContext() .getRequestDispatcher("/user.jsp") .forward(request, response); } }
Сервлет создает объект User. Для передачи его на страницу user.jsp устанавливается атрибут "user" через вызов request.setAttribute("user", tom) . Далее происходит перенаправление на страницу user.jsp. И, таким образом, страница получит данные из сервлета.
Когда я начинал работать в Java EE, серверная инфраструктура казалась мне загадочным капризным монстром. И это при том, что в программирование я пришёл из админов. Залил ear в Resin, проверь, поднялся ли? Успешно ли распаковался? Однажды мы с админами потратили полночи чтобы понять, почему после успешного вроде бы деплоя на запросы отвечает по-прежнему старая копия приложения. При таких танцах с бубном переход на GlassFish казался хорошим решением, а отказ от ejb - ещё лучшим. Кто-то из моих коллег радовался могуществу Spring, кто-то искал что-то попроще... Я писал сервлеты под tomcat, а когда делал что-то совсем маленькое - просто встраивал jetty.
Чем проще, тем лучше. Понятно, что есть нетривиальные требования, есть сложные задачи. Но признаемся себе, сколько простых ненагруженных сервисов вы реализовали используя слишком мощные и сложные инструменты? "Я привык к этому фреймфорку и не хочу терять скилл" - слышу я обычно. "Ты уйдёшь и тот, кто будет поддерживать этот монстрокод, проклянёт тебя" - думаю я в ответ.
Может я и не убедил вас писать простые вещи простыми средствами, но посмотрите все-таки как можно сделать серверное java-приложение вообще без сервера. Это, в конце концов, интересно.
Как, вообще без сервера? Писать обработку запросов самому?
Ну, нет, что вы. Я за простые решения, но не за велосипеды. Все уже написано и, более того, уже установлено. Java-сервер уже есть в вашем jdk. Просто используйте его)
Import
com.sun.net.httpserver.HttpServer
;
import
java.io.IOException
;
import
java.net.InetSocketAddress
;
import
java.util.concurrent.Executors
;
public
class
Main {
private
static
final
int
PORT =
9090
;
public
static
void
main(String
args)
throws
IOException
{
int
port =
PORT;
if
(args.length
>
0
)
{
try
{
port =
Integer
.parseInt
(args[
0
]
)
;
}
catch
(Exception
e)
{
e.printStackTrace
()
;
}
}
HttpServer server =
HttpServer.create
(new
InetSocketAddress(port)
, 0
)
;
server.createContext
("/publish"
, new
PublishHandler()
)
;
server.createContext
("/subscribe"
, new
SubscribeHandler()
)
;
server.setExecutor
(Executors.newCachedThreadPool
()
)
;
server.start
()
;
System
.out
.println
("Server is listening on port "
+
port)
;
}
}
com.sun.net.HttpServer - вот все что нам нужно. Наше приложение будет слушать запросы на порту 9090 или том, что будет указан java -jar наш.jar
тут. Наши обработчики мы навешиваем на контексты перед стартом сервера. Все предельно просто. Чтобы в обработчиках сосредоточиться на бизнес-логике, сделаем им абстрактный предок, где порешаем все мелочи вроде вычитывания параметров зарпроса и логирования пары запрос-ответ:
Import
com.sun.net.httpserver.HttpExchange
;
import
com.sun.net.httpserver.HttpHandler
;
import
java.io.IOException
;
import
java.io.OutputStream
;
import
java.text.SimpleDateFormat
;
import
java.util.Date
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.UUID
;
public
abstract
class
HttpHandlerBased implements
HttpHandler {
protected
abstract
String
doGet(HttpExchange exchange, Map<
String
, String>
params)
;
@Override
public
void
handle(HttpExchange exchange)
throws
IOException
{
String
requestMethod =
exchange.getRequestMethod
()
;
if
(requestMethod.equalsIgnoreCase
("GET"
)
)
{
String
uriStr =
exchange.getRequestURI
()
.getQuery
()
;
String
id =
UUID.randomUUID
()
.toString
()
.replace
("-"
, ""
)
;
log(" "
+
uriStr)
;
Map<
String
, String>
pars =
new
HashMap<
String
, String>
()
;
String
pairs;
if
(uriStr !=
null
&&
uriStr.contains
("="
)
)
{
if
(uriStr.contains
("&"
)
)
pairs =
uriStr.split
("&"
)
;
else
pairs =
new
String
{
uriStr}
;
for
(String
pair :
pairs)
{
String
p =
pair.split
("="
)
;
if
(p.length
==
2
)
{
pars.put
(p[
0
]
, p[
1
]
)
;
}
}
}
String
resp =
doGet(exchange, pars)
;
log(" "
+
resp)
;
exchange.sendResponseHeaders
(200
, resp.length
()
)
;
OutputStream
body =
exchange.getResponseBody
()
;
body.write
(resp.getBytes
("UTF-8"
)
)
;
body.close
()
;
}
}
public
static
void
log(String
msg)
{
System
.out
.println
(new
SimpleDateFormat
("HH:mm:ss:SSS dd.MM.yy"
)
.format
(new
Date
()
)
+
" - "
+
msg)
;
}
}
Имея "за плечами" такого предка, нам останется реализовать метод doGet, в каждом конкретном "сервлете". Метод POST я тут не обрабатываю потому что он мне не нужен. Но обработать его не сложнее, можете попробовать сами.
Итак, что же мы получаем?
Для "секретного web-сервера", по сути внутреннего API java, мы имеем очень приятные инструменты для работы с данными запроса, заголовками ответа и т.п. Все это не требует никакой установки, настройки, есть прямо "в коробке" и готово к работе. Чем не инструмент для простого web-сервиса? Конечно, когда мы проверим свою идею и задумаемся о продакшене, можно перенести свою бизнес-логику в "честные" сервлеты и использовать тот же tomcat или resin. Почему бы и нет? Но если проект не пойдёт в продакшн мы скажем себе спасибо за то, что не привлекали админов, не тратили дни на развертывание и настройку окружения, а просто взяли и сделали то что было нужно. И не более того.