I need to hot-deploy and -undeploy resources in a Jersey ServletContainer.
There seems to be no way to 'unregister' resources on a ResourceConfig, so the route I'm following is to replace all resources with a new set.
Although the documentation says registerResources on ResourceConfig replaces all resources, browsing through the source code seems to contradict this.
The solution I found was to reload the ServletContainer with an entirely new ResourceConfig.
Set<Class<?>> classes = ...
ResourceConfig config = new ResourceConfig(classes);
container.reload(config);
This works fine until I deploy a resource that results in a ModelValidationException. After that I cannot get the ServletContainer back in a proper state.
If I take a look at the source code:
public void reload(final ResourceConfig configuration) {
try {
containerListener.onShutdown(this);
webComponent = new WebComponent(webComponent.webConfig, configuration);
containerListener = webComponent.appHandler;
containerListener.onReload(this);
containerListener.onStartup(this);
} catch (final ServletException ex) {
LOGGER.log(Level.SEVERE, "Reload failed", ex);
}
}
The ModelValidationException is thrown from the WebComponent constructor.
After that any call to reload results in an exception from the onShutdown method, caused by checkState in the preDestroy method of the ServiceLocatorImpl.
I can avoid the exception by ignoring validation errors
ResourceConfig config = new ResourceConfig(classes);
config.property(ServerProperties.RESOURCE_VALIDATION_IGNORE_ERRORS,
Boolean.TRUE);
container.reload(config);
There is no way now however to find out if there were any errors but to explore the logging, which is just as bad, really.
Per heenenee's comment I tried subclassing ServletContainer, but something like this gives problems because the ResourceConfig cannot be put in two WebComponents.
I tried creating the WebComponent before shutting down, to get an early exit, but this fails the actual reload if there is no error in the resources (because the resourceconfig cannot be modified after the webcomponent has been created)
#Override
public void reload(ResourceConfig configuration) {
try {
new WebComponent(new WebServletConfig(this), configuration);
} catch (ServletException e) {
LOGGER.log(Level.SEVERE, "Reload failed", e);
List<ResourceModelIssue> resources = Collections.emptyList();
throw new ModelValidationException(e.getMessage(), resources);
}
super.reload(configuration);
}
Is there another way to hot-undeploy resources? Is there a way to reset the ServletContainer after a failed reload?
I don't think this can be achieved without the use of a servlet container that supports hot deployments. In my experience, a good way of doing this is using a container that supports OSGi. You can take a look at Eclipse Virgo or Apache Karaf.
For example, in an OSGi environment, you can create modules (called bundles) that can be dropped to a scanned folder to enable features at runtime, or removed from a folder, to disable some features. This is similar to how plugins work in Eclipse IDE, where a new plugin install/uninstall doesn't necessarily require a restart.
Jersey is not technically a servlet container, it is a REST/JaxB framework running on a servlet container.
Most embeddable servlet containers, Tomcat, Jetty, Grizzly allows you to redeploy application and servlets at runtime. But redeployment is typically not a feature you use when you embed the container in code.
Hot redeployment is most useful in production, allowing you to continuously deploy new versions.
On Tomcat you can have new and old version of an application deployed on the same server, and tomcat ensures that new sessions are started on the newest version on the application, but older versions will continue to use the application version they were started with. When an application is no loger used it is automatically undeployed.
Related
So I have a Spring app running on Tomcat. While deploying initialization I sometimes may run into some errors (i.e. that some JDNI value is not found or that the app is not able to connect to some service). So when this happen I throw and exception and catch it in the #Configuration file. In the catch block I try to close the app with a System.exit(-1). But it doesn't seem to be the right way to do it. Cause Intellij is no capable or turning off the server, I even have seen that resources are not released till server is OFF.
I have also tried:
#Autowired
public ApplicationContext application context
#Bean
public IServerDataCache serverDataCache() {
try {
return new ServerDataCache(args);
} catch(InitializationError initializationError) {
log.error("Unable to load configuration for Server Data Cache. Closing application.");
System.exit(1); OR SpringApplication.exit(applicationContext)
}
return null;
}
Both System.exit(1); OR SpringApplication.exit(applicationContext) seem to have the same effect. Any idea of a better way to enforce application finalization.
BTW it is a spring boot application.
Just let your #Bean method throw the exception. It will be caught by the container and it'll then take care of tidying things up.
Although Andy Wilkinson's answer is correct, I'd just like to add that, since you're running your application on Tomcat (and not an embedded Tomcat), the Tomcat server doesn't shutdown when the application fails to start.
This happens by design, since Tomcat is, like all JavaEE containers, meant to run several applications at once.
If you want the whole process to stop when the application fails, you need to switch to using an embedded container.
I spent quite a few days now trying to figure out how to add a website in OSGi.
I hava Restlet web service running with Jetty extension to use Jetty as a connector. This feature provides different resources under multiple URLs.
But I would also like to have a small website running on the system that can be accessed by the user. I wanted to use some HTML,Javascript,CSS and provide the current data status with some graphs and pictures.
I assume since Jetty is running in the background I would be able to deploy this website on Jetty and maybe call the server resources provided by Restlet in Javascript.
Apparently nothing worked except the restlet services.
My question would be is it possible to add a WAB bundle and expect it to work(Since Jetty is running in background)? Or is there any better way to add a website in OSGi?
Or
The only option I have now is, since it is possible to return an HTML form as a representation, add all my javascript code inside the HTML form and send it as a response to GET request(Which I believe is a mess).
Everything will run in Raspberry pi so I can only have a very small footprint. I am using Equinox, Restlet 2.3.0 and Jetty 9.2.6.
I would really appreciate if someone knows a link where i could get info on getting at least a sample page running in OSGi. I have tried many with no luck.
I recommend you to have a look at how it is done in Apache Karaf (https://github.com/apache/karaf). More on Apache Karaf and WebContainers here: http://karaf.apache.org/manual/latest/users-guide/webcontainer.html
In fact, Jetty is internally used by Restlet under the hood through its connector feature. This way, it's not convenient (and not the correct approach) to register dynamically applications.
That said, Restlet is really flexible and dynamic. This means that you can dynamically handle bundles that contain Restlet applications in a similar way than WAB bundles, i.e. attach them to virtual hosts of a component.
Here is the way to implement this:
Create a bundle that makes available the Restlet component into the OSGi container. You should leverage the FrameworkListener listener to be able that all connectors, converters, and so on... are registered into the Restlet engine:
private Component component;
public void start(BundleContext bundleContext) throws Exception {
bundleContext.addFrameworkListener(new FrameworkListener() {
component = new Component();
(...)
component.start();
});
}
public void stop(BundleContext bundleContext) throws Exception {
component.stop();
}
When the component is started, you can look for bundles that are present in the container and contained Restlet applications. For each bundle of this kind, you can register a dedicated OSGi service that make available the internal Restlet application you want to register against the component.
ServiceReference[] restletAppRefs = bundleContext.getServiceReferences(
"restletApplication",
null);
if (restletAppsRefs != null) {
for (ServiceReference restletAppRef : restletAppsRefs) {
RestletApplicationService descriptor
= (RestletApplicationService) bundleContext
.getService(serviceReference);
String path = descriptor.getPath();
Application restletApplication = descriptor.getApplication();
// Register the application against the component
(...)
}
}
Registering applications against the component is
try {
VirtualHost virtualHost = getExistingVirtualHost(
component, hostDomain, hostPort);
if (virtualHost == null) {
virtualHost = new VirtualHost();
virtualHost.setHostDomain(hostDomain);
virtualHost.setHostPort(hostPort);
component.getHosts().add(virtualHost);
}
Context context = component.getContext().createChildContext();
virtualHost.setContext(context);
virtualHost.attachDefault(application);
component.updateHosts();
application.start();
} catch(Exception ex) {
(...)
}
You also need to take into account the dynamics of OSGi. I mean bundles can come and go after the start of the OSGi container itself. You can leverage
bundleContext.addServiceListener(new ServiceListener() {
public void serviceChanged(ServiceEvent event) {
if (isServiceClass(event, RestletApplicationService)) {
int type = event.getType();
if (type == ServiceEvent.REGISTERED) {
// Register the Restlet application against the component
} else if (type == ServiceEvent.UNREGISTERING) {
// Unregister the Restlet application
}
}
}
});
Hope it helps you,
Thierry
There are many options available to you - OSGi doesn't really impose many restrictions on what you can do. If you want to use OSGi's capabilities then here's a couple of ideas:
One option would be to deploy a WAB. You'll need to ensure that your framework has the necessary OSGi Services running though. Just because some bundle is using Jetty internally it doesn't follow that the necessary OSGi services are running.
The bundle org.apache.felix.http.jetty does provide the necessary services to deploy a WAB. Version 2.2.2 is 1.3MB on disk, and embeds its own copy of Jetty. Other implementations are available (e.g. Pax-Web, as used in Karaf - which also embeds Jetty)
Another option would be to use the OSGi Http service directly (again you'd need to include a bundle which implements this service (like the Felix one mentioned). A call to org.osgi.service.http.HttpService.registerResources() will serve up static content from within your bundle.
If the this additional footprint is a real concern then you might want to look at how you can get Restlet to use the OSGi http service, rather than providing it's own via embedded Jetty.
Yet another option would be to take a Jetty centric view of things. Restlet's embedded Jetty is probably not configured to serve arbitrary content from disk. You could look at either re-configuring the embedded Jetty to do this, or consider deploying Restlet to a 'standard' Jetty install. Personally, I'd try the latter.
I have spent a few days on this and I still did not find an answer (i did find questions asking for this)
I am coding with java, using a grizzly server (2.3 version) and I've managed to work with many kind of resources (restful classes, java servlets etc).
URI uri = new URI("http://localhost....");
ResourceConfig rc = new ResourceConfig();
rc.registerClasses(aResource.class);
GrizzlyHttpServerFactory.createHttpServer(uri, rc);
My goal though, is to load a whole war file and not individual classes but i have not find a way to do it.
So the question is 'how can i deploy and run a war file inside a grizzly server?'
According to the Grizzly Javadoc you do it like this:
Synchronous Web Server servicing a Servlet
GrizzlyWebServer ws = new GrizzlyWebServer("/var/www");
try {
ServletAdapter sa = new ServletAdapter();
sa.setRootFolder("/Path/To/Exploded/War/File");
sa.setServlet(new MyServlet());
ws.addGrizzlyAdapter(sa);
ws.start();
} catch (IOException ex) {
// Something when wrong.
}
However, as you can see, you first have to "explode" the WAR file; i.e. unpack it into the file system.
You seem to be using the Jersey ResourceConfig class. To make your approach work, I think you would need to do the following:
Unpack the WAR file.
Create a URLClassloader instance that loads from "/WEB-INF/classes" the JARs etc in "/WEB-INF/lib".
Register it by calling ResourceConfig.setClassLoader
For the record, classloading from a packed WAR file is more effort, and apparently gives poor performance. (If you want to see how to do it, Tomcat has this functionality ... disabled by default.)
It's not possible to deploy WAR files to Grizzly 2.x at this point in time.
I have a standard Maven webapp structure defined, and it uses Spring MVC.
I am using an embedded Jetty server (java class) for testing the application in development.
The code used to create the Jetty server is outlined below. If I make changes to any JSP files, the changes are immediately visible in the browser.
However if I change any class files, e.g Controllers, the changes are not hot deployed?
What do I have to do get this to work?
I have searched this and I think I need to use the class org.eclipse.jetty.util.Scanner and specifically the setScanInterval method, but not sure how to wire this up?
Here is the code to create the Server
String webAppDir = "src/main/webapp/";
Server server = new Server(8080);
WebAppContext webApp = new WebAppContext();
webApp.setContextPath("/");
webApp.setDescriptor(webAppDir + "/WEB-INF/web.xml");
webApp.setResourceBase(webAppDir);
webApp.setParentLoaderPriority(true);
HandlerCollection hc = new HandlerCollection();
ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection();
hc.setHandlers(new Handler[] { contextHandlerCollection });
hc.addHandler(webApp);
server.setHandler(hc);
return server;
Thanks in advance
For hot deployment you need to use the WebAppProvider and the DeploymentManager. Those you can configure to manage the scanning for changes and the reloading of the webapp. So it is clear, the WebappContext is not what manages the deployment of a webapp, it is merely the container class that is gets deployed so there is another mechanism that works outside of that which can handle the concepts of deploy/redeploy.
http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/jetty-deploy/src/test/resources/jetty-deploy-wars.xml
You can take that chunk of xml there and convert into the java calls you need to do this embedded.
Or use something like the jrebel jvm plugin which provides for automatic class reloading.
In development I use jetty as the servlet container. I have the following development configuration:
master project which has wabapp directory
derived project which overrides some of the files in webapp directory
The master project webapp can be started in development mode thanks to providing appropriate WebAppContext to jetty.
Now I want to start derived project analogously, assuming that when request is made, there is an attempt to:
get resource from webapp directory of derived project
if it does not exists, get it from webapp directory of master project
I know that it is possible to override WebAppContext#getResource() method, however some libraries we use in the project seem to perform IO operations on wabapp directory on their own. For example by calling ServletContext#getRealPath("/"), and then reading files without use of ServletContext#getResource() method. The problem could be solved on lower level by some virtual file system on top of File, however it does not seem to be supported in JDK 1.6, any suggestions?
It seems that using something like ResourceCollection is sufficient:
http://download.eclipse.org/jetty/stable-7/apidocs/org/eclipse/jetty/util/resource/ResourceCollection.html
Unfortunately the GWT's DevMode which I use is based on jetty 6, where ResourceCollection is unavailable. I extended the Resource class myself, and together with own GWT JettyLauncher, and thanks to small trick with setting resourceBase on DefaultServlet via reflection, I was able to serve webapp from two directories simultaneously.
protected void doStart() throws Exception {
setClassLoader(new LauncherWebAppClassLoader());
super.doStart();
ServletHolder holder = getServletHandler().getServlet("default");
Servlet servlet = holder.getServlet();
Field field = servlet.getClass().getDeclaredField("_resourceBase");
field.setAccessible(true);
field.set(servlet, combinedResourceBase);
}