我使用Groovy来提高生产率,尤其是在JDBC方面。使用GSQL,我只需要两行就可以以用户友好的格式从数据库中获取数据。
我理想的解决方案将使得可以通过对HTTPS和授权的支持来启动服务器,并以编程方式在单个文件(Groovy脚本)中用几行代码声明URL的处理程序。 (非常类似于下面的Gretty解决方案和安全性内容。)
旁注
注意葡萄
Groovy打包引擎Grape可以通过@Grab批注在运行时下载依赖项。 如果您运行groovy脚本f.ex。 通过/ bin / groovy,它将可以正常工作,因为Groovy与Ivy一起分发,这对于Grape来说是必需的。 (如果使用IntelliJ,则将ivy.jar手动添加到项目的类路径中,然后在@Grab批注上调用意图操作(Mac:Alt + Enter)以将其下载并添加到类路径中。)
关于HTTPS / SSL配置的注意事项
要启用HTTPS,您将需要创建带有密钥对的密钥库,Jetty文档 (步骤1a)对此进行了详细介绍 。
对于急躁的人:
- 跑
keytool -keystore $HOME/.keystore -alias myGroovyServer -genkey -keyalg RSA
- 当系统询问“您的名字和姓氏是什么?”时,请提供将运行服务的主机名,例如“ localhost”或“ myserver.example.com”
- 为密钥库和生成的密钥指定相同的密码(例如“ myKeystorePsw”)
- 运行服务器时,(以服务器特定的方式)提供生成的文件.keystore的(绝对)路径,并将系统属性javax.net.ssl.keyStorePassword设置为密码
1.简单的HTTP请求和响应解决方案
尝试1:贪吃
Gretty是用于Netty(异步Web服务器)的Groovy包装器,是用Groovy ++编写的。 ( 有关Gretty的介绍性文章 。)
优点 :与Groovy集成良好,易于上手,支持服务静态资源,并且Netty很酷。
缺点 :未记录,该项目似乎处于休眠状态,没有添加用户授权和HTTPS的明确方法。
编码:
@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.Sql
class 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,因为Grapes.grab无法下载依赖项org.eclipse.jetty.orbit:javax.servlet,这是因为Ivy被打包与扩展混淆了。 使用org.eclipse.jetty。 聚合 :jetty-server代替( Jetty 聚合包合并了多个较小的JAR)。
示例:安全码头
(基于有关通过自定义处理程序或servlet 嵌入Jetty (包括SSL)进行程序配置和处理请求的文章(确实写得很好)以及有关如何通过嵌入式Jetty配置安全性以编程方式配置身份验证和授权的文章)
import groovy.sql.Sql
import javax.servlet.*
import javax.servlet.http.*
import org.eclipse.jetty.server.*
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector
import 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 (具有访问请求/响应并支持生成HTML的Groovy脚本)
- 文章:一个Groovy + Jetty博客,主要支持命令行参数@Grab和静态资源服务
- 文章: 为嵌入式码头启用HTTPS
温斯顿
Winstone是一个200KB的servlet容器, 可通过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的参考实现,即标准。
缺点 : 对Jersey进行故障排除并不像我所希望的那样容易。 文档应该更好(与Jetty相比),这确实是一个弱点(尝试找到有关使用嵌入式Grizzly保护Jersey的任何内容)。
示例:带有嵌入式Grizzly的球衣,没有安全性
(如果对安全性和身份验证感兴趣,请查看示例项目https-clientserver-grizzly 。对我来说,这似乎并不复杂。)
import groovy.sql.Sql
import javax.ws.rs.*
import javax.ws.rs.core.*
import com.sun.jersey.api.core.*
import com.sun.jersey.api.container.grizzly2.GrizzlyServerFactory
import 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)
def uri = UriBuilder.fromUri("http://localhost/").port(6789).build();
HttpServer httpServer = GrizzlyServerFactory.createHttpServer(uri, resources);
println("Jersey app started with WADL available at ${uri}application.wadl")
System.in.read();
httpServer.stop();
}
}
Main.startServer()
带有嵌入式TJWS的RESTEasy(微型Java Web服务器和Servlet容器)
TJWS确实是微型的,占地100KB,也可在Android上运行,比竞争对手LWS和Jetty小约5倍。
从RESTEasy文档中:
@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 ,我不确定RESTEasy的JBoss TJWS插件(这是Maven中唯一的tjws版本)。 它也可以嵌入,但是无法通过Maven获得,我也不知道它是否支持将请求映射到代码(而不是WAR和JSP)。
带有嵌入式服务器的Restlet
请参阅文章使用Groovy和Restlet构建RESTful Web应用程序,第1部分:启动和运行 (2008)。 由于Restlet在Maven中可用,我们可以@Grab依赖。
更有趣的是GroovyRestlet模块,该模块使您只需几行就可以以编程方式配置授权和请求处理 。 (您也可以在Java中使用更多LoC进行此操作。)
版本2.1的文档: 如何实现授权和HTTPS , 这是Java的约6行中最简单的REST服务器 。
(请注意,Restlet带有一个简单的HTTP服务器,但也可以使用Jetty或Grizzly。)
优点 :RESt(虽然不是标准),与Groovy的集成很好(尽管可能已经过时)
缺点 :从4/2012开始,Restlet仅在其私有Maven存储库中,尽管它们也将在Maven Central中 ,但JAX-RS支持尚未完全实现 (Restlet 2.1-RC3)。 文档可能会更好(更全面,更相互关联,示例更多)。 要使用HTTPS,您必须选择其他服务器而不是内部服务器。
示例:Restlet + SimpleFramework Server + HTTPS和身份验证(无Groovy集成)
import groovy.sql.Sql
import 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')
@GrabResolver(name = 'restlet', root = 'http://maven.restlet.org')
@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()
结论
如果不需要REST,我可能会选择使用Jetty,否则将使用Jersey + Jetty(因为文档要好得多,所以我会选择Jetty而不是Grizzly)。 如果Groovy集成可以工作,并且您不介意使用非标准REST实现,那么Restlet也可能很有趣。
从代码样本的长度来看,尝试Grails或st。 毕竟相似
参考: The Holy Java博客上的JCG合作伙伴 Jakub Holy 公开了Groovy和超轻量级HTTP服务器在HTTP上的功能 。
翻译自: https://www.javacodegeeks.com/2012/04/exposing-functionality-over-http-with.html