Я использовал Groovy для его высокой производительности, особенно в отношении JDBC — с GSQL мне потребовалось всего две строки, чтобы получить данные из БД в удобном для пользователя формате.
Мое идеальное решение позволило бы запустить сервер с поддержкой HTTPS и авторизацией и объявить обработчики для URL программно, в одном файле (скрипт Groovy), всего в нескольких строках кода. (Очень похоже на решение Gretty ниже + материал по безопасности.)
Примечания стороны
Обратите внимание на виноград
Grape , пакетный движок Groovy, позволяет загружать зависимости во время выполнения с помощью аннотаций @Grab. Если вы запускаете свой отличный скрипт f.ex. через / bin / groovy он будет работать, потому что Groovy распространяется вместе с Ivy, что необходимо для работы Grape. (При использовании IntelliJ добавьте ivy.jar вручную в путь к классам проекта, а затем вызовите намеренное действие (Mac: Alt + Enter) в аннотации @Grab, чтобы загрузить его и добавить в путь к классам.)
Примечание по настройке HTTPS / SSL
Чтобы включить HTTPS, вам нужно будет создать хранилище ключей с парой ключей, что хорошо описано в документации Jetty (шаг 1a).
Для нетерпеливых:
- Бег
1
keytool -keystore $HOME/.keystore -aliasmyGroovyServer -genkey -keyalg RSA - В ответ на вопрос «Каковы ваши имя и фамилия?» Укажите имя хоста, на котором будет работать служба, например «localhost» или «myserver.example.com».
- Укажите один и тот же пароль для хранилища ключей и сгенерированного ключа (например, «myKeystorePsw»)
- При запуске сервера укажите (абсолютный) путь к сгенерированному файлу .keystore (для конкретного сервера) и задайте системное свойство javax.net.ssl.keyStorePassword для пароля
1. Простые HTTP-запросы и ответы
Попытка 1: Гретти
Gretty — это Groovy-оболочка для Netty, асинхронного веб-сервера, написанная на Groovy ++. ( Вступительная статья для Гретти .)
Плюсы : Хорошо интегрирован с Groovy, прост в освоении, поддерживает обслуживание статических ресурсов и многое другое, Netty — это круто
Минусы : Недокументированный, проект кажется бездействующим, нет четкого способа добавить авторизацию пользователя и HTTPS.
Код:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
@GrabConfig(systemClassLoader=true)@GrabResolver(name='gretty', root='http://groovypp.artifactoryonline.com/groovypp/libs-releases-local')@Grapes([ @Grab('org.mbte.groovypp:gretty:0.4.279'), @Grab('mysql:mysql-connector-java:5.1.16')])import org.mbte.gretty.httpserver.*import groovy.sql.Sqlclass Main { final def db = [url: 'jdbc:mysql://localhost:3306/user', user: 'dbUser', psw: 'dbPsw' ] def run() { startServer() } def getUser(def code) { println "Connecting to the DB to check '$code'..." def sql = Sql.newInstance( db.url, db.user, db.psw) return sql.firstRow("select * from users where code = $code") ?: "No such code found" } def startServer() { GrettyServer server = [] server.groovy = [ localAddress: new InetSocketAddress(6789), // no host => all defaultHandler: { response.redirect "/" }, "/:code": { get { def user = getUser(it.code) response.text = "The code '${it.code}' refers to $user\n" // => st. like: "The code 'abc' refers to [id:123, name:me@somewhere.no, code:abc]" } } ] server.start() println "Groovy server is ready to serve" }}new Main().run() |
пристань
Плюсы : зрелый, мощный, часто используемый во встроенной форме, поддерживает HTTPS и авторизацию (также программно) .
Ошибка : вы не можете использовать org.eclipse.jetty: jetty-server, потому что Grape.grab не сможет загрузить зависимость org.eclipse.jetty.orbit: javax.servlet из-за того, что Ivy запутался из- за упаковки и расширения. Используйте org.eclipse.jetty. вместо агрегата : jetty-server (пакеты агрегатов Jetty объединяют несколько меньших JAR-файлов).
Пример: Причал с безопасностью
(на основе статей о Embedded Jetty (включая SSL) для программной настройки и обработки запросов с помощью пользовательского обработчика или сервлета (действительно очень хорошо написано) и Как настроить безопасность с помощью Embedded Jetty для программной настройки аутентификации и авторизации)
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
import groovy.sql.Sqlimport javax.servlet.*import javax.servlet.http.*import org.eclipse.jetty.server.*import org.eclipse.jetty.server.ssl.SslSelectChannelConnectorimport org.eclipse.jetty.servlet.*import org.eclipse.jetty.security.*import org.eclipse.jetty.util.security.*@GrabConfig(systemClassLoader = true)@Grapes([ @Grab('org.eclipse.jetty.aggregate:jetty-server:8.1.2.v20120308'), @Grab('org.eclipse.jetty.aggregate:jetty-servlet:8.1.2.v20120308'), @Grab(group='javax.servlet', module='javax.servlet-api', version='3.0.1'), @Grab('mysql:mysql-connector-java:5.1.16')])class Main extends HttpServlet { final def db = [url: 'jdbc:mysql://localhost:3306/user', user: 'dbUser', psw: 'dbPsw' ] protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { final String code = request.pathInfo.substring(1); // skip leading '/' response.setContentType("text/plain"); try { def user = getUser(code) response.setStatus(HttpServletResponse.SC_OK); response.getWriter().println("Usage of the code '${code}': $user\n") } catch (Exception e) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) response.getWriter().println("Connection to the database failed. This may be due to temporary " + "connection problems or due to misconfiguration. Try later.") } } def getUser(def code) { println "Connecting to the DB to check '$code'..." def sql = Sql.newInstance( db.url, db.user, db.psw) return sql.firstRow("select * from users where code = $code") ?: "No such code found" } public static startServer() { Server server = new Server(); server.setHandler(createServletHandlerWithAuthentication( "/", new Main(), createAuthenticationConstraint())) server.setConnectors((Connector[])[createSslConnector()]) server.start(); server.join(); } /** Wrap the servlet in the servlet handler and configure it to run at the given URL, setting its security handler. */ private static createServletHandlerWithAuthentication(String contextPath, Servlet servlet, SecurityHandler securityHandler) { final String pathSpec = "/*" ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS) servletHandler.setContextPath(contextPath) servletHandler.setSecurityHandler(securityHandler) servletHandler.addServlet(new ServletHolder(servlet), pathSpec) return servletHandler } /** Create HTTPS connector running at port 6789 and using key pair from the hard-coded keystore. */ private static Connector createSslConnector() { SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector() ssl_connector.setPort(6789) def cf = ssl_connector.getSslContextFactory() cf.setKeyStore(System.getProperty("user.home") + "/.keystore") cf.setKeyStorePassword("myKeystorePsw") cf.setKeyManagerPassword("myKeystorePsw") return ssl_connector } /** Create a security handler requiring authentication with username/password. */ private static SecurityHandler createAuthenticationConstraint() { Constraint constraint = new Constraint(); constraint.setName(Constraint.__BASIC_AUTH); constraint.setRoles((String[])["user"]); constraint.setAuthenticate(true); ConstraintMapping cm = new ConstraintMapping(); cm.setConstraint(constraint); cm.setPathSpec("/*"); // auth. required for any URL def loginSrv = new HashLoginService() loginSrv.putUser("myLogin", new Password("myPassword"), (String[])["user"]) loginSrv.setName("My App Realm") SecurityHandler sh = new ConstraintSecurityHandler() sh.setLoginService(loginSrv) sh.setConstraintMappings((ConstraintMapping[])[cm]); return sh }}Main.startServer() |
Дополнительные ресурсы:
- Пост: встроенный Groovy, выполняющий Groovlets (скрипты Groovy с доступом к запросу / ответу и поддержкой генерации HTML)
- Пост: блог Groovy + Jetty с поддержкой аргументов командной строки, @Grab и обслуживанием статических ресурсов
- Сообщение: Включение HTTPS для встроенной пристани
Winstone
Winstone — это сервлет-контейнер объемом 200 КБ, доступный через Maven , последний выпуск 2008 года. Похоже, он сосредоточен на обслуживании WAR.
Sun Java 6 HttpServer
Sun JRE 6+ содержит легкий, программно управляемый HTTP-сервер , поддерживающий также HTTPS. Пример кода .
2. Решения на основе REST
Джерси JAX-RS
Jersey, эталонная реализация JAX-RS (он же REST), может работать на встроенном тестовом сервере, таком как Grizzly, GlassFish или Jetty.
Плюсы : эталонная реализация JAX-RS, то есть стандартная.
Минусы : поиск и устранение неисправностей Джерси не так просто, как хотелось бы. Документация должна быть лучше (по сравнению с Jetty), это действительно слабое место (попробуйте найти что-нибудь о защите Джерси с помощью встроенного Grizzly).
Пример: Джерси со встроенным Гризли, без безопасности
(Если вы заинтересованы в безопасности и аутентификации, посмотрите пример проекта https-clientserver-grizzly . Мне кажется, он немного сложен.)
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
import groovy.sql.Sqlimport javax.ws.rs.*import javax.ws.rs.core.*import com.sun.jersey.api.core.*import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactoryimport org.glassfish.grizzly.http.server.HttpServer@GrabConfig(systemClassLoader = true)@GrabResolver(name = 'gretty', root = 'http://groovypp.artifactoryonline.com/groovypp/libs-releases-local')@Grapes([ @Grab('com.sun.jersey:jersey-server:1.12'), @Grab('com.sun.jersey:jersey-core:1.12'), @Grab(group='com.sun.jersey', module='jersey-grizzly2', version='1.12'), @Grab(group='javax.ws.rs', module='jsr311-api', version='1.1.1'), @Grab('mysql:mysql-connector-java:5.1.16')])@Path("/{code}")class Main { final def db = [url: 'jdbc:mysql://localhost:3306/user', user: 'dbUser', psw: 'dbPsw' ] @GET @Produces("text/plain") public Response getUserByCode(@PathParam('code') String code) { try { def user = getUser(code) return Response.ok().entity("Usage of the code '${code}': $user\n".toString()).build(); } catch (Exception e) { Response.serverError().entity("Connection to the database failed. This may be due to temporary " + "connection problems or due to misconfiguration. Try later. Cause: $e".toString()).build(); } } def getUser(def code) { println "Connecting to the DB to check '$code'..." def sql = Sql.newInstance( db.url, db.user, db.psw) return sql.firstRow("select * from users where code = $code") ?: "No such code found" } public static startServer() { ResourceConfig resources = new ClassNamesResourceConfig(Main) HttpServer httpServer = GrizzlyServerFactory.createHttpServer(uri, resources); println("Jersey app started with WADL available at ${uri}application.wadl") System.in.read(); httpServer.stop(); }}Main.startServer() |
RESTEasy с помощью встроенного TJWS (веб-сервер Tiny Java и контейнер сервлетов)
TJWS полностью миниатюрный, занимает 100 КБ, работает также на Android, примерно в 5 раз меньше, чем конкуренты LWS и Jetty .
Из документации RESTEasy:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
@Path("")public class MyResource { @GET public String get() { return "hello world"; } public static void main(String[] args) throws Exception { TJWSEmbeddedJaxrsServer tjws = new TJWSEmbeddedJaxrsServer(); tjws.setPort(8081); tjws.getRegistry().addPerRequestResource(MyResource.class); tjws.start(); }} |
Сам TJWS поддерживает SSL , я не уверен насчет плагина JBoss TJWS для RESTEasy (единственной версии tjws, доступной в Maven). Он также может быть встроен, но не доступен через Maven, и я не знаю, поддерживает ли он отображение запросов в код (вместо WAR и JSP).
Рестлет со встроенным сервером
См. Статью « Создание веб-приложений RESTful с использованием Groovy и Restlet», часть 1. Запуск и работа (2008 г.). Поскольку Restlet доступен в Maven, мы могли бы просто @Grab зависимости.
Еще более интересным является модуль GroovyRestlet, который позволяет программно настраивать авторизацию и обработку запросов , используя всего несколько строк. (Вы можете сделать это также на Java , добавив немного LoC.)
Документ для выпуска 2.1: Как реализовать авторизацию и HTTPS , самый простой из возможных REST-серверов в ~ 6 строках Java.
(Обратите внимание, что Restlet поставляется с простым HTTP-сервером, но также может использовать Jetty или Grizzly.)
Плюсы : RESt (хотя и нестандартный), хорошая интеграция с Groovy (хотя он может быть устаревшим)
Минусы : По состоянию на 4/2012 Restlet находится только в своем частном репозитории Maven, хотя они будут и в Maven Central , поддержка JAX-RS еще не полностью реализована (Restlet 2.1-RC3). Документация может быть лучше (более полные, более взаимосвязанные, более разнообразные примеры). Чтобы использовать HTTPS, вы должны выбрать другой сервер, а не внутренний.
Пример: Restlet + SimpleFramework Server + HTTPS и аутентификация (без интеграции Groovy)
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
import groovy.sql.Sqlimport org.restlet.*import org.restlet.data.*import org.restlet.resource.*import org.restlet.security.*@GrabConfig(systemClassLoader = true)@GrabResolver(name = 'gretty', root = 'http://groovypp.artifactoryonline.com/groovypp/libs-releases-local')@Grapes([ @Grab('org.restlet.jse:org.restlet:2.1-RC3'), @Grab('org.restlet.jse:org.restlet.ext.simple:2.1-RC3'), @Grab('mysql:mysql-connector-java:5.1.16')])class Main extends ServerResource { final def db = [url: 'jdbc:mysql://localhost:3306/user', user: 'dbUser', psw: 'dbPsw' ] @Get public String getUser() { def code = getRequestAttributes().get("code") def user = getUser(code) return "Usage of the code '${code}': $user\n" } def getUser(def code) { println "Connecting to the DB to check '$code'..." def sql = Sql.newInstance( db.url, db.user, db.psw) return sql.firstRow("select * from users where code = $code") ?: "No such code found" } public static startServer() { Component component = new Component(); def userResourceFinder = component.getDefaultHost().createFinder(Main.class); component.getDefaultHost().attach("/{code}" , wrapResourceInAuthenticationCheck(component.getContext(), userResourceFinder)); configureHttpsServer(component, 6789) component.start() } /** * Add a Guard (a filter) that asks the user for username/password and checks it against a map. */ private static Restlet wrapResourceInAuthenticationCheck(Context context, Restlet resource) { MapVerifier verifier = new MapVerifier(); verifier.getLocalSecrets().put("myLogin", "myPassword".toCharArray()); ChallengeAuthenticator guard = new ChallengeAuthenticator(context.createChildContext(), ChallengeScheme.HTTP_BASIC, "My App"); guard.setVerifier(verifier); guard.setNext(resource); return guard; } /** * Create the server, instruct it to use a SslContextFactory, and configure the factory with * our keystore and password. I guess that which server to use is determined by Restlet based on which * package (*.ext.simple.*, *.ext.jetty.* etc.) is available. */ private static void configureHttpsServer(Component component, int port) { def secureServer = component.getServers().add(Protocol.HTTPS, port); // See http://www.restlet.org/documentation/2.1/jse/ext/org/restlet/ext/ssl/DefaultSslContextFactory.html // for params such as keystore path and password System.setProperty("javax.net.ssl.keyStorePassword", "myKeystorePsw") // used for keystorePassword & keyPassword def confg = secureServer.getContext().getParameters() confg.add("sslContextFactory", "org.restlet.ext.ssl.DefaultSslContextFactory") // Beware: keystorePath shall default to ${user.home}/.keystore but doesn't seem to do so => set it explicitly confg.add("keystorePath", "${System.getProperty('user.home')}/.keystore") }}Main.startServer() |
Вывод
Возможно, я бы использовал Jetty, если REST не нужен, и Jersey + Jetty в противном случае (я бы определенно выбрал Jetty вместо Grizzly, так как документация намного лучше). Restlet также может быть интересным при условии, что интеграция Groovy работает, и если вы не возражаете против использования нестандартной реализации REST.
Глядя на длину примеров кода, было бы лучше попробовать Grails или st. в конце концов, похоже
Ссылка: Предоставление функциональности через HTTP с помощью Groovy и сверхлегких HTTP-серверов от нашего партнера JCG Якуба Холи в блоге Holy Java .