With a bit of luck and glue code, it can.
So first we need a Service Provider Implementation (SPI) from Jetty: J2se6HttpServerSPI
This will make the JAX-WS endpoint use the Jetty server instead its default Sun HttpServer.
To plugin different SPI's you would define this new service in a META-INF/services file, but you can also set a system property, as described here. You can even do this in code which reduces the number of files you need to worry about when refactoring.
So here's an example of a Jetty Server handling a JAX-WS endpoint in combination with a File system directory.
Server jettyServer = new Server(port);
HandlerCollection handlerCollection = new HandlerCollection();
jettyServer.setHandler(handlerCollection);
/** 1) Publish WebService (JettyHttpServerProvider) */
String context = "/web/ws";
// 1.1) register THIS Jetty server with the JettyHttpServerProvider
new JettyHttpServerProvider().setServer(jettyServer);
// 1.2) make sure JAX-WS endpoint.publish will use our new service provider: JettyHttpServerProvider
System.setProperty("com.sun.net.httpserver.HttpServerProvider",
"org.mortbay.jetty.j2se6.JettyHttpServerProvider");
// 1.3) add an empty HandlerCollection to by setup by this provider
handlerCollection.addHandler( new HandlerCollection() );
// 1.4) use JAX-WS API to publish the endpoint (will use a JettyHttpServerProvider)
Endpoint endpoint = Endpoint.create(replayServiceImpl);
endpoint.publish("http://localhost:" + port + "/web/ws", replayServiceImpl);
/** 2) Publish WebGUI (Jetty) */
String context = "/gui";
// 2.1) configure File Resource Handler
ResourceHandler fileResourceHandler=new ResourceHandler();
fileResourceHandler.setWelcomeFiles(new String[]{"index.html"});
fileResourceHandler.setResourceBase(guiPath); // start here
// 2.2) configure 'gui' Context
ContextHandler guiHandler = new ContextHandler();
guiHandler.setContextPath(context);
guiHandler.setResourceBase(".");
guiHandler.setClassLoader(Thread.currentThread().getContextClassLoader());
guiHandler.setHandler(fileResourceHandler);
// 2.3) add this context handler
handlerCollection.addHandler(guiHandler);
/** 3) start JETTY server */
jettyServer.start();
jettyServer.join();
Thanks a lot for your post.
ReplyDeleteI tried to port this to Jetty 7. This doesn't work though
because I cannot register properly. There is no such setServer()
method in Jetty 7 (?).
Ask for code directly.
correction: there is doesn't seems to be a method setServer() in JettyHttpServerProvider, neither in Jetty 6 nor in Jetty 7.
ReplyDeleteHi Axel,
ReplyDeleteyou need to download this service provider package separately, for example from:
http://svn.codehaus.org/jetty-contrib/trunk/j2se6/src/main/java/org/mortbay/jetty/j2se6/JettyHttpServerProvider.java
The current version does have a setServer() method.
If you just want to publish web services with Jetty and don't need servlets you can just copy those jars in your classpath:
ReplyDeletejetty-6.1.x.jar
jetty-util-6.1.x.jar
servlet-api-2.5-6.1.x.jar
j2se6-6.1.x.jar
and ou can then simply publish your endpoints using the JAX-WS API:
Endpoint endpoint = Endpoint.create(replayServiceImpl);
endpoint.publish("http://localhost:" + port + "/web/ws", replayServiceImpl);
The JettyHttpServerProvider.setServer() call is only useful when you want to mix web applications with web service endpoints using the same server.
Hi Ludovic, thanks for feedback.
ReplyDeleteYes, my understanding is that the above construction is only necessary if have both: a web gui and a web service. But if you only have a web service to publish, you wouldn't even need Jetty. You could simply use what's available inside JDK1.6. Something as described in a previous post:
http://tech-eureka.blogspot.com/2009/09/60-seconds-on-soap-based-web-services.html
Axel-- is the mixture of web pages with web services the ONLY reason to use Jetty?
ReplyDeleteI'm looking for a simple, standalone way to expose some Java web services to .Net and the Endpoint.publish() method seems to work.
Well, yes JDK Endpoint publish does work, but if you are looking for a reliable, flexible mechanism to (maybe) add other things in the future, Jetty is the way to go.
ReplyDeleteI really like the architecture of Connectors and the way it scales with complexity: If you want it simple, it is simply an API, 10 lines long. If you want it complex you can add xml configs and such. Also, I think it will be faster and more reliable. Off hand I remember a bug with JDK's HttpServer and some very long byte stream responses over HTTP. Unfortunately I can't find that link anymore ...
Reading books and examples from others, I found that people usually use the Sun HTTP server to show concepts but move on to something else for 'real deployment'.
Hi Ludovic, thanks for this post.
ReplyDeleteI'm trying to publish a standalone JAX-WS service using Jetty, as you describe above.
Jetty seems to start, but every time I'm trying to access the service wsdl, I get this:
HTTP ERROR: 500
INTERNAL_SERVER_ERROR
RequestURI=/TestWs/TestWSService
java.lang.IllegalArgumentException: fromIndex(1) > toIndex(0)
at java.util.SubList.(AbstractList.java:604)
at java.util.RandomAccessSubList.(AbstractList.java:758)
at java.util.AbstractList.subList(AbstractList.java:468)
at org.mortbay.jetty.j2se6.J2SE6ContextHandler.invokeHandler(J2SE6ContextHandler.java:119)
at org.mortbay.jetty.j2se6.J2SE6ContextHandler.handle(J2SE6ContextHandler.java:86)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Looks like some filters (whatever it is) are not configured by default. Do you have an idea, why this happens? May be I need some sort of a config file?
Sorry, I'm a not very experienced Java user. I have problems building the Jetty SPI. Could someone provide step by step instructions on how to build it please?
ReplyDeleteAfter executing mvn I get the following output (J2SE_HOME is set):
C:\WINDOWS\Temp\j2se6>mvn clean install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[ERROR] FATAL ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Error building POM (may not be this project's POM).
Project ID: org.mortbay.jetty:jetty-j2se6:jar:null
Reason: Cannot find parent: org.mortbay.jetty:project for project: org.mortbay.jetty:jetty-j2se6:jar:null for project org.mortbay.jetty:jetty-j2se6:jar:null
[INFO] ------------------------------------------------------------------------
[INFO] Trace
org.apache.maven.reactor.MavenExecutionException: Cannot find parent: org.mortbay.jetty:project for project: org.mortbay.jetty:jetty-j2se6:jar:null for project org.mortbay.jetty:jetty-j2se6:jar:null
at org.apache.maven.DefaultMaven.getProjects(DefaultMaven.java:404)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:272)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
...
------------------------------------------------
[INFO] Total time: < 1 second
[INFO] Finished at: Mon May 24 21:59:55 CEST 2010
[INFO] Final Memory: 1M/4M
[INFO]
Hi Mike, I'm not sure what this problem is, but it looks like a maven POM version? issue.
ReplyDeleteSee for example http://mail-archives.apache.org/mod_mbox/maven-dev/200608.mbox/%3Cecv4eq$orm$1@sea.gmane.org%3E
Were you able to compile and use Jetty inside Eclipse or so ? Once that works, you could try and setup the Maven POM through an Eclipse maven plugin if you really want to use Maven. Or simply use an Ant build.xml to build it. In many cases Ant is more suitable and straight-forward, I think.
Also consider the Jetty or Maven sites to get Jetty/Maven support. (This is just a Java bloke's blog)
Thanks for sharing this! I tried to do the same for Jetty 7. To be sure I was using Jetty and not Sun's built-in server, I monitored HTTP traffic with a sniffer. I made a strange observation: the moment I put jetty-j2sehttpspi-7.1.6.v20100715.jar on my classpath (in addition to jetty-all-7.1.6.v20100715.jar and servlet-api.jar), Jetty seems to be used, see the response headers I got when asking for my wsdl:
ReplyDeleteHTTP/1.1 200 OK
Content-type: text/xml;charset="utf-8"
Transfer-Encoding: chunked
Server: Jetty(7.1.6.v20100715)
I don't need to set System properties, create a new jettyServer, add handlers, call start/join on the server object, etc. I used just 1 line of code:
Endpoint.publish("http://localhost:8080/ts", new TimeServerImpl());
Isn't that weird? Any idea how this is possible?
Like someone mentioned before, I get an error when I use the J2se6HttpServerSPI.
ReplyDeleteI get the following error:
HTTP ERROR: 500
INTERNAL_SERVER_ERROR
RequestURI=/hw
java.lang.IllegalArgumentException: fromIndex(1) > toIndex(0)
at java.util.SubList.(AbstractList.java:604)
at java.util.RandomAccessSubList.(AbstractList.java:758)
at java.util.AbstractList.subList(AbstractList.java:468)
at org.mortbay.jetty.j2se6.J2SE6ContextHandler.invokeHandler(J2SE6ContextHandler.java:119)
at org.mortbay.jetty.j2se6.J2SE6ContextHandler.handle(J2SE6ContextHandler.java:86)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:926)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Anyone who can solve the mystery about why I get this?
How to fix it(if it can)?
Hmm, sorry no idea.
ReplyDeletePlease note that you can browse the source code here:
http://grepcode.com/file/repo1.maven.org/maven2/org.mortbay.jetty/jetty-j2se6/6.1.21/org/mortbay/jetty/j2se6/J2SE6ContextHandler.java
You wouldn't happen to have a complete sample project, where you use this?
ReplyDeleteWell, actually I do.
ReplyDeleteSend me an email to axel.podehl@gmail.com and I'll send you an example...
Thanks a lot for the code Axel, it really helped me out :)
ReplyDeleteOk, I would like to Address the issue mentioned by "Daniel B" and others:
ReplyDeleteHTTP ERROR: 500
INTERNAL_SERVER_ERROR
RequestURI=/hw
java.lang.IllegalArgumentException: fromIndex(1) > toIndex(0)
at java.util.SubList.(AbstractList.java:604)
at java.util.RandomAccessSubList.(AbstractList.java:758)
at java.util.AbstractList.subList(AbstractList.java:468)
at org.mortbay.jetty.j2se6.J2SE6ContextHandler.invokeHandler(J2SE6ContextHandler.java:119)
at org.mortbay.jetty.j2se6.J2SE6ContextHandler.handle(J2SE6ContextHandler.java:86)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:926)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
I was able to fix this problem by modifying the J2SE6 module. The file J2SE6ContextHandler.java needs the following fix on line 117
Original: if (filters != null)
Change to: if (filters != null && (filters.size() > 0))
What was happening is on line 119 there is a call to filters.sublist(1, filters.size()). The only problem is that this assumes filters will always have a size() > 0. This is not the case. Now this could be because I am not using Jetty properly. However, only after the fix above was I able to get this example to work.
how do I configure https
ReplyDeleteI think you would use an org.mortbay.jetty.security.SslSelectChannelConnector and configure that one.
ReplyDelete