Перейти к содержимому

Jetty java что это

  • автор:

Встраиваем Jetty-сервер в свой проект

В этой статье я расскажу от такой крутой вещи, как Jetty сервер! Почему Jetty крутой и чем он может быть полезен сферическому java-программисту в вакууме? Всё дело в том что Jetty является одновременно легковесным и хорошо оптимизированным решением, которое можно использовать как в небольших, так и в крупных проектах. Jetty хорошо масштабируется, экономично использует память, но самый жир это то что его можно встроить в своё приложение. Это дико удобно когда нужно отладить работу веб-приложения, так как отпадает необходимость постоянно его пересобирать и заливать на сервер приложений. Да и вообще встроенный сервер может решать кучу полезных задач, например недавно мне понадобилось сделать легковесное веб-приложение со встроенным сервером, которое можно было бы запускать одной командой на любой машине, и для решения этой задачи я использовал Jetty.

Сейчас на простом примере я покажу как можно встроить Jetty сервер в своё приложение. Кроме этого, добавлю ещё пару интересных и полезных фишек:

  1. Наш проект будет собираться в jar-файл и запускаться простой командой java -jar. Все зависимости будут собираться в отдельную папку lib, которая будет лежать в одном каталоге с jar-файлом.
  2. Наш проект будет иметь внешний (не обязательный) файл настроек app.properties

Посвящать этим двум фишкам отдельную статью смысла нет, тем более что в данном примере они будут смотреться очень уместно. Теперь перейдём к разработке:

Для начала создайте веб-приложение в своей любимой IDE (я предпочитаю IDEA IntelliJ) или с помощью команды:

mvn archetype:generate -DgroupId=ru.kutepov
-DartifactId=jetty-example
-DarchetypeArtifactId=maven-archetype-webapp
-DinteractiveMode=false

В результате этих манипуляций должен сгенерироваться проект вот с такой структурой:

Затем надо добавить в файл pom.xml все зависимости, которые будут нужны для дальнейшей работы. Для наглядности я решил прикрутить к проекту Spring MVC:

  org.springframework spring-webmvc $ org.springframework spring-core $ org.springframework spring-web $ 

Ну и конечно же зависимости для Jetty:

  org.eclipse.jetty jetty-server $ org.eclipse.jetty jetty-webapp $ org.eclipse.jetty jetty-jsp $ 

Версии зависимостей я вынес в отдельный блок для красоты:

  UTF-8 4.3.1.RELEASE 9.2.19.v20160908 

Кроме этого, нам потребуются три Maven-плагина, которые так же нужно добавить в pom.xml:

   maven-compiler-plugin 1.7 1.7   org.apache.maven.plugins maven-dependency-plugin  copy-dependencies prepare-package copy-dependencies  $/lib false false true     org.apache.maven.plugins maven-jar-plugin   true lib/ ru.kutepov.launcher.Launcher     

maven-compiler-plugin отвечает за версию Java, которая используется при сборке проекта.

maven-dependency-plugin собирает все зависимости в папку lib и помещает её в каталог рядом с jar-файлом.

maven-jar-plugin делает jar-файл запускаемым и указывает класс (в данном случае ru.kutepov.launcher.Launcher, его мы создадим чуть позже), который вызывается при запуске проекта. Так же данный плагин записывает в манифест ссылку на папку с библиотеками — lib, которая создана предыдущим плагином.

Ещё необходимо задать папку ресурсов и указать местоположение внешнего файла с настройками приложения:

   src/main/webapp  $/src/main/webapp/resources/properties app.properties  ..  

В итоге должен получиться вот такой pom.xml:

 4.0.0 ru.akutepov jetty-example jar 1.0 jetty-example UTF-8 4.3.1.RELEASE 9.2.19.v20160908    org.springframework spring-webmvc $ org.springframework spring-core $ org.springframework spring-web $  org.eclipse.jetty jetty-server $ org.eclipse.jetty jetty-webapp $ org.eclipse.jetty jetty-jsp $    src/main/webapp  $/src/main/webapp/resources/properties app.properties  ..   jetty-example  maven-compiler-plugin 1.7 1.7   org.apache.maven.plugins maven-dependency-plugin  copy-dependencies prepare-package copy-dependencies  $/lib false false true     org.apache.maven.plugins maven-jar-plugin   true lib/ ru.kutepov.launcher.Launcher        

С зависимостями разобрались, продолжаем дальше конфигурировать наше приложение. В папке /src/main/webapp/WEB-INF должен лежать файл web.xml. Если он ещё там не лежит, то его нужно немедленно создать:

 Jetty example mvc-dispatcher org.springframework.web.servlet.DispatcherServlet contextConfigLocation /WEB-INF/beans.xml  1  mvc-dispatcher /  

Файл web.xml определяет соответствие между путями URL и сервлетами, которые эти URL будут обрабатывать. Веб-сервер использует эту конфигурацию, чтобы определить сервлет для обработки данного запроса и вызвать метод класса, который соответствует методу запроса.

Обратите внимание что в init-param мы указали местоположение конфига для Spring, поэтому создайте файл beans.xml в папке /src/main/webapp/WEB-INF/ со следующим содержимым:

В property-placeholder я указал расположение внешнего файла конфига app.properties, затем с помощью component-scan инициализирую все классы из пакета ru.kutepov с аннотацией @Component. В конце инициализирую InternalResourceViewResolver, который отвечает за отображение .jsp страницы.

Теперь откройте файл index.jsp, который располагается в папке /src/main/webapp/WEB-INF/pages/ и вставьте туда такой код:

Как вы видите, у нас будет простая страница, которая должна отображать значения двух переменных, первую мы будем задавать в контроллере, а вторую возьмём из файла пропертей.

Создайте в папке /src/main/webapp/resources/properties/ файл app.properties и добавьте туда строку:

value=Have a nice day!

Затем создайте в папке /src/main/java пакет ru.kutepov.controller и добавьте в него класс MainController.java:

package ru.kutepov.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller @RequestMapping("/") public class MainController < @Value("$") public String value; @RequestMapping(method = RequestMethod.GET) public String printWelcome(ModelMap model) < model.addAttribute("message", "Hello world!"); model.addAttribute("property_value", value); return "index"; >>

Тут тоже всё довольно просто: метод printWelcome обрабатывает все GET-запросы по адресу «/» и возвращает в ответ страницу html, которая генерируется на основе .jsp шаблона. В шаблон передаются 2 переменные message и property_value. В messageмы передаём произвольную строку а в property_value значение переменной value из файла app.properties.

Завершающим этапом будет создание загрузчика, который будет запускать jetty-сервер. добавьте в пакет ru.kutepov пакет launcher и создайте в нём класс Launcher.java:

package ru.kutepov.launcher; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import java.net.URL; import java.security.ProtectionDomain; /** * Starts jetty-server on the specified port */ public class Launcher < public static void main(String[] args) throws Exception < int port = 12135; try < if (args.length >0) < port = Integer.parseInt(args[0]); >> catch (Exception e) < e.printStackTrace(); >Server server = new Server(port); ProtectionDomain domain = Launcher.class.getProtectionDomain(); URL location = domain.getCodeSource().getLocation(); WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); webapp.setWar(location.toExternalForm()); server.setHandler(webapp); server.start(); server.join(); > >

Данный загрузчик запускает jetty-сервер на порту 12135 по умолчанию, либо на том порту, который был передан в качестве аргумента командной строки. Рассмотрим код подробнее:

В начале переменной port присваиваем номер порта по умолчанию — 12135, затем проверяем был ли передан номер порта в качестве аргумента командной строки. Если в args[0] находим номер порта, то записываем его в переменною port. Это позволит запускать приложение на любом порту, и у нас всегда будет значение по умолчанию — очень удобно!

Далее создаём класс сервера (Server) и передаём этого ему номер порта.

Затем нужно проинициализировать контекст веб-приложения — WebAppContext. Для этого объекту WebAppContext необходимо передать путь строки запроса, который будет обрабатывать веб-приложение, а так же указать расположение папки проекта с исходным кодом. Со строкой запроса всё понятно: я просто передаю путь «/» в метод setContextPath. А вот с исходным кодом немного сложнее:

В начале я получаю объект класса ProtectionDomain, который содержит в себе все характеристики домена. У ProtectionDomain есть замечательный метод getCodeSource(), который возвращает объект класса CodeSource. Класс CodeSource содержит в себе информацию о расположении ресурса (URL ресурса или ссылка на локальный ресурс), а так же информацию о цепочки сертификатов, которые использовались, чтобы проверить подписанный код, происходящий из того расположения. Чтобы получить информацию о расположении ресурсов, я вызываю метод getLocation() объекта класса CodeSource и получаю объект URL, который как раз содержит то что нужно. Так как в WebAppContext необходимо передать путь в виде строки, вызываем метод toExternalForm() объекта класса URL и передаём полученную строку в метод setWar() объекта класса WebAppContext.

В конце передаём готовый объект класса WebAppContext в метод setHandler()объекта класса Server и запускаем наш сервер.

Всё, приложение готово! Соберите проект командой:

mvn clean install

Перейдите в сгенерированный каталог /target и выполните команду:

java -jar jetty-example.jar

или если хотите запустить приложение ну другом порту, то команду:

java -jar jetty-example.jar 8800

Откройте в браузере ссылку http://localhost:12135/ или http://localhost:8800/, если ввели вторую команду. Вы должны увидеть такую страницу:

Есть и альтернативный способ запуска приложения, который особенно удобно использовать во время отладки — запуск из IDE. В IntelliJ IDEA достаточно открыть Launcher.java, нажать правую кнопку мыши и выбрать пункт Run ‘Launcher.main()’. Как такой трюк повторить в других IDE разбирайтесь сами 🙂

Быстрая разработка веб-приложений на Java

Как вы разрабатываете веб-приложение на Java?
После каждого изменения, как вы его запускаете и проверяете? Сколько времени занимает редеплой приложения и рестарт контейнера?

Мне довелось видеть разные варианты: от полной пересборки WAR-файла до использования плагинов для IDE типа MyEclipse, WTP и «коннекторов» для сервлет-контерйнеров. У некоторых из них есть явные недостатки, другие вполне работают — но есть способ проще!

Запускалка

Этот способ разработки позволяет максимально просто и гибко настроить приложение с минимальным временем редеплоя. Вам надо всего лишь написать один простенький Java-класс с main-методом, который запустит сервер Jetty сразу с нужными приложениями (т.н. Embedded Mode).

Вот как выглядит запускалка в минимальной комплектации:

import org.mortbay.jetty.*; import org.mortbay.jetty.nio.SelectChannelConnector; import org.mortbay.jetty.webapp.WebAppContext; public class Launcher < public static void main(String[] args) throws Exception < Server server = new Server(); Connector connector = new SelectChannelConnector(); connector.setPort(8080); server.addConnector(connector); WebAppContext root = new WebAppContext("root/src/main/webapp", "/"); WebAppContext reports = new WebAppContext("reports/src/main/webapp", "/reports"); WebAppContext petclinic = new WebAppContext("petclinic/src/main/webapp", "/petclinic"); server.setHandlers(new Handler[]); server.start(); > > 

Этот код запускает сервер (сервлет-контейнер), слушающий порт 8080, с тремя веб-приложениями, код для которых берётся прямо из папок проекта ( root/src/main/webapp , reports/src/main/webapp и petclinic/src/main/webapp ), то есть любые изменения в файлах вступают в силу сразу, без необходимости что-то пересобирать и передеплоить.

Понятное дело, при добавлении новых методов придётся рестартовать, но и в этом случае рестарт происходит максимально быстро, буквально в течение секунды (конечно, если ваши приложения не делают чего-то сложного при запуске). Если к этому ещё и прикрутить JRebel, будет вообще шоколадно.

Вот и вся хитрость.
Наверняка в продакшине вы используете не Jetty, а что-нибудь типа Tomcat, JBoss или WebLogic. Неважно, мы ведь сейчас говорим о разработке, где скорость, стабильность и т.д. неважны, а важна лёгкость настройки, скорость запуска и редеплоя. И тут Embedded Jetty — то, что доктор прописал.

Вы можете просто запускать этот main-класс из своей любимой IDE, отлаживать, тестировать, рестартовать; и не нужны никакие плагины, не нужно искать файлы конфигурации, не нужно копаться в XML’ах. Все настройки под рукой. Вот это жизнь!

Для запускали в минимальной комплектации достаточно всего трёх jar-файлов: servlet-api.jar, jetty.jar, jetty-util.jar.

[UPD] Как подсказывает kblcuk, приведённый код запускалки работает только для версии Jetty 6. Начиная с седьмой версии Jetty, названия пакетов и классов были поменяны с org.mortbay.jetty.* на org.eclipse.jetty.*, так что придётся подправить import.

Тюнинг

Рассмотрим некоторые дополнительные возможности, которые могут пригодиться в зависимости от проекта.

Загрузчики классов

Приведённый выше код загружает классы и зависимости всех веб-приложений в одном загрузчике (ClassLoader). Если по какой-то причине вам важно, чтобы у веб-приложений были разные наборы классов и зависимостей (jar’ов), это тоже можно сделать, дописав немножко кода:

 WebAppContext petclinic = new WebAppContext("petclinic/src/main/webapp", "/petclinic"); WebAppClassLoader classLoader = new WebAppClassLoader(petclinic); classLoader.addClassPath("petclinic-core/target/classes"); classLoader.addClassPath("petclinic-services/target/classes"); petclinic.setClassLoader(classLoader); 

Я сам активно это использовал, когда мне надо было запускать сразу много веб-приложений, каждое со своими (конфликтующими) зависимостями. Очень просто, очень удобно.

JSP

Старожилы помнят, что в незабвенные времена для формирования HTML часто использовалась такая штука, как JSP. Если в вашем проекте JSP тоже используется, то придётся добавить ещё несколько зависимостей: eclipse-jdtcore.jar, jsp-api-2.1-glassfish.jar, jsp-2.1-glassfish.jar

Конфигурация JDBC-ресурсов

Возможно, есть решение попроще, но вот такой вариант у меня заработал:

import import javax.naming.InitialContext; import com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean; static void setupDataSources() < System.setProperty("java.naming.factory.initial", "org.mortbay.naming.InitialContextFactory"); AtomikosNonXADataSourceBean dataSource = new AtomikosNonXADataSourceBean(); dataSource.setUniqueResourceName("jdbc/portal"); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mydb"); dataSource.setUser("myusername"); dataSource.setPassword("mypassword"); dataSource.setMaxPoolSize(10); new InitialContext().createSubcontext("jdbc").bind("portal", dataSource); >
Нельзя ли это ещё немножко автоматизировать?

Можно. Но только если нужно.
Если адрес или пароль базы данных у вас часто меняется, возможно, вы захотите считывать их из какого-нибудь файла. Это можно. Но стоит ли? Главное, что все настройки сосредоточены в одном файле, а что это за файл — java-класс или *.properties — не всё ли равно?

Если список веб-приложений постоянно меняется, возможно, имеет смысл написать плагин для Eclipse или IDEA, который бы запускал Jetty со всеми проектами, которые есть в данный момент в Eclipse. Один такой плагин описан здесь — он находит все проекты, в которых есть файл web.xml, а остальные проекты добавляет к ним в classpath.

Минусы

    (Мнимый минус) Контейнер Jetty уступает в производительности Tomcat, JBoss, Resin и др.
    Ерунда. Мы сейчас говорим о разработке — тут производительность совершенно не важна.
    Кстати, это ещё вопрос, уступает ли.

Альтернативы

  • Полная пересборка WAR-файла и копирование в Tomcat/webapps.
    Самое надёжное и самое медленное решение. Представьте себе, ждать 1-15 минут после каждого изменения CSS! Нет уж, увольте.
    Минус: ужасно долго.
    Минус: дебаг придётся отдельно настраивать (remote debug). Пусть это не очень сложно, но всё-таки лишние усилия.
  • MyEclipse
    Платная IDE на базе Eclipse. При компиляции проекта копирует все ресурсы в отдельную папку — у меня это работало ужасно долго. Также у меня остались неприятные воспоминания от заморочек с настройкой.
  • Eclipse WTP (Web Tools Platform) — бесплатный, но тяжёлый плагин для Eclipse
    Я несколько раз пытался, но так и не смог настроить его так, как мне надо.
    Плюс: умеет работать с несколькими разными контейнерами (Tomcat, JBoss и т.д.)
    [UPD] Впрочем, вот тутAkvel рассказывает, как он безболезненно использует Eclipse WTP.
  • Application Server integrations
    аналог Eclipse WTP, доступный только в платной версии Intellij IDEA.
    Минус: платный.
  • JRebel
    Помимо своей основной задачи, JRebel позволяет конфигурировать ClassPath приложений прямо из проектов.
    Минус: JRebel платный.
    Плюс: если ваш работодатель на него раскошелится, JRebel может сильно упростить и ускорить работу разработчика.
  • Play! framework
    Умеет запускать приложение прямо из проекта и перегружать изменённый код на лету. Очень удобно.
    Минус: вы ограничены одним фреймворком, у которого есть свои особенности и ограничения.
  • Run Jetty Run
    Плагин для Eclipse, позволяющий запускать Jetty с приложением прямо из проекта. По сути, он делает в точности то же, что описано в этой статье.
    Плюс: содержит в себе все необходимые jar’ы, вам не придётся ничего дополнительно скачивать, кроме самого плагина.
    Минус: умеет запускать только один проект.
    Минус: не умеет настраивать соединение с базой данных. Придётся по-любому делать это руками.
  • [UPD] Jetty Maven plugin
    Как подсказывает 1nd1go, если в вашем проекте используется Maven, вы можете запускать веб-приложения командой » mvn jetty:run «.
    Плюс: если используете плагин для Maven в своей IDE, можете запускать эту команду прямо из IDE; при этом можно и дебажить, т.к. jetty плагин запускается в том же процессе, что и мавен.
    Минус: вы привязаны к системе сборки (maven) и к IDE.
  • [UPD] Jetty Gradle plugin
    Если в вашем проекте используется Gradle, вы можете запускать веб-приложения командой » gradle jettyRun «.
    Плюс: эту команду прямо из IDE (даже без всяких плагинов), при этом можно и дебажить.
    Минус: вы привязаны к системе сборки (gradle).
  • [UPD] Sysdeo Eclipse Tomcat Launcher plugin
    Как подсказывает helions8, для Eclipse есть хороший плагин Sysdeo Tomcat launcher, который позволяет совместить контекст томката и проекта, потому не нужно собирание war-файла и подкладывание его контейнеру, всякий css, html и прочий js правится сразу, на горячую. В большинстве случаев работает hot replace. По сути, этот плагин делает то же самое, что и наш Launcher
    Минус: вы привязаны к Eclipse.
    Минус: требует больше суеты. Надо и Tomcat самому скачать, и какой-то новый «tomcat project» создать — в общем, вполне неплохое средство, но запускалка проще.
  • [UPD] Spring source tool suite
    Как подсказывает alexeygrigorev, для Eclipse есть плагин Spring source tool suite, который имеет встроенный «навороченный томкат», умеющий редеплоить при сохранении файлов
    Минус: вы привязаны к Eclipse и STS.

Остаётся добавить, что в embedded режиме можно запускать не только Jetty, но и Tomcat, Glassfish и др. Конкретный контейнер неважен, важен принцип: никакой длительной пересборки, установки, распаковки и настройки. Всё запускается быстро и просто из одного класса.

Я сам разрабатываю веб-приложения таким образом уже несколько лет и всем остальным советую. Так вы будете тратить меньше времени на суету вокруг сборки и инсталляции и сможете сосредоточиться на собственно разработке.

Jetty

Jetty — свободный контейнер сервлетов, написанный полностью на Java. Может использоваться как HTTP-сервер или в паре со специализированным HTTP-сервером (к примеру, с Apache HTTP Server). Первоначально распространялся под лицензией Apache 2.0 License, но после перехода в 2009 году в число приложений, разрабатываемых в рамках проекта Eclipse стал доступен и под Eclipse Public Licence (EPL).

Jetty начиная с версии 7 поддерживает спецификацию Servlet 2.5 API. Поддержка Servlet API 3.0 добавлена в Jetty 8.

См. также

  • Контейнер сервлетов
  • Apache Tomcat

Ссылки

Eclipse Foundation
AspectJ · BIRT · Buckminster · Eclipse · Equinox · EclipseLink · SWT · Mylyn · Jetty · JFace · Rich AJAX Platform · Virgo
Лицензия: Eclipse Public License · Вебсайт: http://www.eclipse.org/
  • Программное обеспечение по алфавиту
  • Программное обеспечение с лицензией Apache Software License
  • Программное обеспечение с лицензией Eclipse Public License
  • Свободное программное обеспечение, написанное на Java
  • Свободные веб-серверы

Wikimedia Foundation . 2010 .

Полезное

Смотреть что такое «Jetty» в других словарях:

  • Jetty — Archivo:Jetty logo.png Desarrollador Eclipse Foundation eclipse codehaus Información general … Wikipedia Español
  • Jetty — Développeur Mort Bay Consulting / eclipse.org Dernière version 7.1.6 (15 juillet 2010) … Wikipédia en Français
  • Jetty — Entwickler Eclipse Foundation Aktuelle Version 7.4.5 (25. Juli 2011) Aktuelle Vorabversion 8.0.0.M3 (27. Mai 2011) Betriebssystem Windows, Linux … Deutsch Wikipedia
  • Jetty — Jet ty, n.; pl. . [F. jet[ e]e a pier, a jetty, a causeway. See a shooting forth, and cf. .] [1913 Webster] 1. (Arch.) A part of a building that jets or projects beyond the rest, and overhangs the wall below. [1913 Webster]… … The Collaborative International Dictionary of English
  • Jetty — Jet ty, a. Made of jet, or like jet in color. [1913 Webster] The people . . . are of a jetty. Sir T. Browne. [1913 Webster] … The Collaborative International Dictionary of English
  • jetty — (n.) early 15c., from O.Fr. jetee a jetty, a projecting part of a building, also a throw, noun use of fem. pp. of jeter to throw (see JET (Cf. jet) (v.)). Notion is of a structure thrown out past what surrounds it … Etymology dictionary
  • jetty — jetty1 [jet′ē] n. pl. jetties [ME gete < OFr jetée, jetty, orig. pp. of jeter: see JET1] 1. a kind of wall built out into the water to restrain currents, protect a harbor or pier, etc. 2. a landing pier 3. an overhanging part of a building vi … English World dictionary
  • Jetty — Jet ty, v. i. To jut out; to project. [Obs.] Florio. [1913 Webster] … The Collaborative International Dictionary of English
  • jetty — *wharf, dock, pier, quay, slip, berth, levee … New Dictionary of Synonyms
  • jetty — [n] pier barrier, breakwater, dock, groin, landing, quay, seawall, slip, wharf; concepts 439,443,479 … New thesaurus
  • Обратная связь: Техподдержка, Реклама на сайте
  • �� Путешествия

Экспорт словарей на сайты, сделанные на PHP,
WordPress, MODx.

  • Пометить текст и поделитьсяИскать в этом же словареИскать синонимы
  • Искать во всех словарях
  • Искать в переводах
  • Искать в ИнтернетеИскать в этой же категории

Простая веб-служба со встроенным Jetty

Развитие информационных технологий все более и более вовлекает использование инфраструктуры Интернет. Распределенные и мобильные приложения все чаще используют обмен информацией по протоколу HTTP. При этом архитектура Клиент-Сервер остается самой распространённой и простой для освоения, создания и эксплуатации. Принцип архитектуры Клиент-Сервер прост — сервер предоставляет ресурс, а клиент использует этот ресурс.

Данная статья представляет собой попытку понятного описания создания простой веб-службы. Простой, практичный и детально описанный пример часто приносит больше пользы в изучении технологии нежели усердное чтение литературы. В статье рассматривается создание веб-службы простого калькулятора на основе REST, JSON, используя Eclipse и встроенной сервер Jetty.

Задача

Рассмотрим создание калькулятора как веб-службу, реализующую простые арифметические действия с двумя числами. Веб-службу можно рассматривать так же как и удалённую функцию, принимающую входные параметры и выдающую результат. Поэтому её функциональность можно описать следующим образом:

  • a – первый аргумент;
  • b – второй аргумент;
  • op – арифметический оператор, выражаемый одним из знаков +, -, /, *.
  • error – первый аргумент;
  • result – второй аргумент;

Пример запроса/ответа — сумма

Пример запроса/ответа — разность

Пример запроса/ответа — произведение

Пример запроса/ответа — частное

Пример запроса/ответа – ошибка «деление на 0»

Пример запроса/ответа – ошибка «неверный формат числа»

Установка библиотек Jetty

Jetty очень удобен для создания веб приложений. Использование его как встроенного сервера освобождает разработчика от развёртывания веб приложения на внешний сервер при каждом запуске. Также это не требует установку внешнего сервера приложений.

Для большинства случаев достаточно загрузить библиотеки сервера, зарегистрировать их в Eclipse как библиотеку пользователя и далее использовать ссылку на эту библиотеку. Этот подход прост для начинающих Java программистов так как не требует наличия и навыков инструментария автоматизации сборки, такого как Maven или Gradle.

Установить необходимые библиотеки Jetty в Eclipse можно следующим образом:

1. Загрузим сжатый файл по ссылке http://download.eclipse.org/jetty/ и распакуем его;

2. В корневой папке проектов ( обычно это Workspace ) создадим папку jars, а в ней папку jetty;

3. Скопируем содержимое папки lib из распакованного ранее файла в созданную папку jetty;

4. В меню Window/Preferences выберем раздел Java/Build Path/User Libraries.

5. Кликнем кнопку New…, введём имя библиотеки jetty и кликнем кнопку ОК.

6. Далее при выделенной только что созданной библиотеке jetty в окошке Preferences кликнем кнопку Add External JARs…. В окне JAR Selection выберем все JAR-файлы из ранее созданной папки jars/jetty.

7. В итоге JAR-файлы будут загружены в пользовательскую библиотеку jetty. Хотя файлы, находящиеся в под-папках не будут загружены, для большинства случаев в них нет необходимости.

Создание проекта веб сервера

В меню File/New выберем Dynamic Web Project. В поле Project name введём SCalculator. Нажмём кнопку Finish.

Добавление ссылки на библиотеку jetty

Сразу после создания проект не содержит ссылку на библиотеку jetty. Подключённые библиотеки можно просмотреть в Project Explorer во вкладке Java Resources, в под-вкладке Libraries.

Кликнем правой кнопкой мыши на метку проекта и в контекстном меню выберем Build Path и далее Configure Build Path…. Во вкладке Java Build Path на страничке Libraries кликнем кнопку Add Library….

Выберем User Library и кликнем Next. Выберем jetty и кликнем Finish.

В итоге после подтверждения включения пользовательской библиотеки jetty, наличие ссылки на нее можно увидеть в Project Explorer.

Создание сервлета калькулятора

Создание файла сервлета

Сервлет калькулятора будет содержать весь код декодирования входных данных, вычисления, и формирования ответа. Для создания сервлета кликнем правой кнопкой мыши на наименование проекта в панели Project Explorer, в контекстном меню выберем New и далее Servlet. В название класса введём SrvltCalculator и кликнем кнопку Finish.

В панели Project Explorer можно увидеть созданный файл SrvltCalculator.java. Его содержимое автоматически открывается в редакторе.

Удаление лишнего кода

Для упрощения дальнейшего редактирования файлов удалим неиспользуемые конструктор сервлета SrvltCalculator и метод doPost.

Добавление импортируемых модулей

Код, который будет добавлен в файл сервлета потребует добавления следующих ниже строк кода включения модулей. Добавим эти строки.

import java.io.IOException; import java.io.UnsupportedEncodingException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 

Добавление кода в метод doGet

Метод doGet содержит код обработки GET–запросов. В этом методе последовательно добавим приведённые ниже фрагменты кода.

Приём параметров в соответствующие строковые переменные.

String str_a = request.getParameter("a"); String str_b = request.getParameter("b"); String str_op = request.getParameter("op"); 

Объявление переменных для принятия декодированных из строковых переменных числовых параметров a и b.

double value_a = 0; double value_b = 0; 

Объявление переменной контроля возникновения ошибки noError.

boolean noError = true; 

Попытка декодирования числовых параметров a и b из соответствующих строковых переменных. При ошибке декодирования переменная noError принимает значение “ложь”.

try < value_a = Double.parseDouble(str_a); value_b = Double.parseDouble(str_b); >catch ( Exception ex )

Открытие секции кода для случая, если при декодировании числовых параметров ошибка не возникла.

if ( noError )  

Объявление числовой переменной result для хранения результата.

double result = 0; 

Открытие секции try для включения кода вычисления и контроля ошибок. Секция необходима, так как при арифметических операциях может возникнуть ошибка операции с плавающей запятой.

Для случая операции сложения, вызываем функцию functionSum, которую опишем позднее.

if (str_op.equals("+")) result = functionSum( value_a, value_b ); else 

Для случая операции вычитания, вызываем функцию functionDif, которую опишем позднее.

if (str_op.equals("-")) result = functionDif( value_a, value_b ); else 

Для случая операции умножения, вызываем функцию functionMul, которую опишем позднее.

if (str_op.equals("*")) result = functionMul( value_a, value_b ); else 

Для случая операции деления, вызываем функцию functionDiv, которую опишем позднее. Так как для типа double ошибка деления на ноль на современных платформах не возникает, ситуацию в которой делитель равен нулю мы контролируем вручную.

if (str_op.equals("/") && (value_b!=0)) result = functionDiv( value_a, value_b ); else 

После проверки всех четырёх операций устанавливаем флажок отсутствия ошибки в “ложь”. Это делается для идентификации того, что арифметическая операция не идентифицирована.

noError = false; 

Закрываем блок try с установлением флажка отсутствия ошибки в “ложь” в случае возникновения исключительной ситуации.

> catch ( Exception ex )

В случае если ошибки не возникло, отсылаем результат методом doSetResult, который опишем ниже. Так как работа метода doGet на этом завершается, возвращаемся оператором return.

if ( noError )

Закрываем секцию, начатую оператором “if ( noError )

Так как при обработке запроса где-то произошла ошибка и функция doGet не возвратила управление с успешным вычислением, возвращаем сообщение об ошибке методом doSetError, который опишем ниже.

doSetError( response ); 

Междоменные запросы

Междоменные запросы ( также такие запросы называются кроссдоменными / cross domain ) имеют место при запросах с веб страниц, расположенных вне сетевого домена обслуживающего сервера. Ответы на подобные запросы обычно блокируются для противостояния меж-доменным атакам. Для отключения блокировки в ответах сервера можно установить заголовок Access-Control-Allow-Origin:*.

Метод doSetResult

  • Первая строка формирует JSON ответ. Так как структура ответа проста, специализированная библиотека JSON не используется;
  • Во второй строке JSON ответ кодируется в тело HTTP ответа в двоичный вид посредством кодировки UTF-8;
  • В третьей строке указывается тип содержания тела ответа HTTP;
  • В четвёртой строке устанавливается разрешение на междоменные запросы;
  • В пятой строке устанавливается флажок OK HTTP ответа.
protected void doSetResult( HttpServletResponse response, double result ) throws UnsupportedEncodingException, IOException < String reply = ""; response.getOutputStream().write( reply.getBytes("UTF-8") ); response.setContentType("application/json; charset=UTF-8"); response.setHeader("Access-Control-Allow-Origin", "*"); response.setStatus( HttpServletResponse.SC_OK ); > 

Метод doSetError

  • Первая строка формирует JSON ответ. Так как структура ответа проста, специализированная библиотека JSON не используется;
  • Во второй строке JSON ответ кодируется в тело HTTP ответа в двоичный вид посредством кодировки UTF-8;
  • В третьей строке указывается тип содержания тела ответа HTTP;
  • В четвёртой строке устанавливается разрешение на междоменные запросы;
  • В пятой строке устанавливается флажок OK HTTP ответа. Следует учесть, что сообщение содержит ошибку, связанную с арифметическими вычислениями. Так как эта ошибка не связана с протоколом HTTP, флажок статуса устанавливается в ОК.
protected void doSetError( HttpServletResponse response ) throws UnsupportedEncodingException, IOException < String reply = ""; response.getOutputStream().write( reply.getBytes("UTF-8") ); response.setContentType("application/json; charset=UTF-8"); response.setHeader("Access-Control-Allow-Origin", "*"); response.setStatus( HttpServletResponse.SC_OK ); > 

Методы реализации арифметических операций

Архитектура рассматриваемого простого примера подразумевает разделение кода на функциональные части. Ввиду этого арифметические операции реализованы в виде отдельных функций, а не включены в тело метода doGet. Так как функции простые, их код комментировать не будем.

protected double functionSum( double a, double b ) < return a + b; >protected double functionDif( double a, double b ) < return a - b; >protected double functionMul( double a, double b ) < return a * b; >protected double functionDiv( double a, double b ) < return a / b; >

Исходный код программы можно найти в репозитории GitHub.

Создание основного класса

Основной класс приложения будет содержать функцию main – так называемую точку входа, с которой начинается работа программы. Функция main включит инициализацию, настройку и запуск встроенного сервера Jetty.

Для создания основного класса приложения кликнем правой кнопкой на наименовании проекта в панели Project Explorer, в контекстном меню выберем New и далее Class. В название класса введём Main. Установим флажок для создания статической функции main и кликнем кнопку Finish.

Так же как и в случае сервлета создаётся и открывается в текстовом редакторе соответствующий файл.

Добавление импортируемых модулей

Код, который будет добавлен в файл основного класса приложения потребует добавления следующих ниже строк кода включения модулей. Введём эти строки.

import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; 

Добавление кода в метод main

Код метода main начинается с объявления переменной port и присваивания ей номера порта, который будет слушать сервер. Такой подход позволит быстро и легко изменить порт в случае необходимости в случае дальнейшего роста программы.

int port = 8080; 

Создаем класс сервера.

Server server = new Server(port); 

Указываем параметры, которые свяжут путь строки запроса с созданным выше сервлетом.

ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS ); context.setContextPath( "/" ); // http://localhost:8080/func context.addServlet(new ServletHolder( new SrvltCalculator( ) ),"/func"); 

Указываем серверу обработчик запросов.

HandlerList handlers = new HandlerList( ); handlers.setHandlers( new Handler[] < context >); server.setHandler( handlers ); 

Пробуем запустить сервер. Для того, чтобы работа программы не прекратилась, ждём завершения процесса сервера главным потоком посредством вызова server.join(). В случае возникновения ошибки печатается соответствующее сообщение.

try < server.start(); System.out.println("Listening port : " + port ); server.join(); >catch (Exception e)

Исходный код программы можно найти в репозитории GitHub.

Доступ к сервису из браузера

Запуск сервера

При запуске сервера Eclipse может предложить два варианта. Так как сервер содержит полноценный сервлет, то программа может быть запущена на сервере приложений, таком как к примеру Tomcat или самостоятельный Jetty. Однако так как мы встроили jetty в приложение, оно может работать самостоятельно – как Java Application.

После запуска приложение выдаёт соответствующие уведомления и строку Listening port: port, указывающую что наш сервер запущен и ждёт запросов.

Посылка запросов посредством браузера

Наиболее простой способ проверить функциональность сервера – обратиться к нему посредством браузера.

При посылке строки запроса, такой как http://localhost:8080/func?a=8.78&b=4.15&op=+ напрямую, сервер выдает ошибку. Дело в том, что строка не соответствует стандарту запросов и должна быть кодирована как URL ( символ + не допустим ).

После кодирования все работает без ошибки. Символ + кодирован URL как %2B, что делает запрос соответствующим стандарту. В интернете имеется множество он-лайн кодировщиков/де-кодировщиков URL, которыми можно воспользоваться для этой цели.

Аналогичным способом можно проверить ответы сервера на другие запросы.

Клиенты сервера

  • специализированной веб страницы с автоматическим формированием строки запроса и форматированием ответа посредством JavaScipt;
  • мобильным приложением;
  • другим сервером, потребляющим созданный ресурс для своих внутренних нужд.

Клиент – веб страница

Специализированная веб страница – простой тип клиентского приложения.

HTML код страницы можно найти в репозитории GitHub.

Создание запускаемого модуля

Созданный сервер можно оформить как единый независимый запускаемый JAR-файл. Такой файл будет требовать только наличия установленной среды выполнения Java и запускаться из любой папки файловой системы. Для создания такого файла кликнем правой кнопкой мыши на наименовании проекта в панели Project Explorer, в контекстном меню выберем Export и далее Export…. В секции Java выберем Runnable JAR file и кликнем кнопку Next.

В настройках создаваемого JAR-файла указываем Launch configuration как Main-SCalculator, полное имя экспортируемого файла и флажок упаковки необходимых модулей в этот файл.

Запуск правильно созданного JAR-файла с именем SCalculator осуществляется простой командой (при запуске из той же папки, где он находится):

java -jar SCalculator.jar 

Также возможен запуск сервера двойным кликом мыши на JAR-файле.

Итоги

Многие описанные в этом выпуске элементы были практически использованы при создании высоконагруженных серверов. Несомненно были использованы и более продвинутые приёмы, позволившие достигнуть высокого быстродействия и надёжности, такие как использование сервера NGINX в режиме обратного прокси. Однако все начинается с простого и я надеюсь что смог просто и понятно описать приёмы, которые пригодятся при практической разработке.

Ссылки

Подробнее о встраивании Jetty в приложение можно почитать по ссылке http://docs.codehaus.org/display/JETTY/Embedding+Jetty

Представленный материал основан на использовании Eclipse Luna for Java EE Developers и Ubuntu 14.04.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *