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.
Related
We have a project made in Dropwizard version 2.0.0-RC, where we use REST-endpoints. After some issues we decided to use gRPC instead of using REST. There is a couple of 3rd party libraries to connect gRPC to Dropwizard, but we believe they are a bit outdated and not useable. So we are thinking about implementing Armeria and their GRPC solution.
To implement this, I need the Jetty instance to attach the GRPC.
This is how I can solve it (Mix between GRPC and Armeria):
Server server = Server.builder()
.http(8080)
.service(GrpcService.builder()...build())
.serviceUnder("/", JettyService.forServer(jettyServer))
.build();
server.start().join();
So I need the jettyServer to be the instance of Jetty with the type of org.eclipse.jetty.server. The rest of the code is Armerias way of embedding Jetty. Link to embedding jetty.
How can I retrieve the instance of Jetty?
I was able to solve this by using Dropwizard lifecycles to get the server.
// variable server is of type org.eclipse.jetty.server.Server
environment.lifecycle().addServerLifecycleListener(new ServerLifecycleListener() {
#Override
public void serverStarted(Server server) {
// ....
}
});
With the instance, you can use Armeria to attach gRPC
I was able to use the links provided in the comments of the other answer and put this PR together in Armeria project for adding a dropwizard module.
https://github.com/line/armeria/pull/2236
It currently targets 1.3.x rather than 2.0, but once a stable release exists, it'll need upgraded
Edit: PR was accepted and merged
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.
I have a Swing project using Spring for DI and now I am trying to migrate to Eclipse 4 and OSGi.
Using the configuration files of Spring the user could comment/uncomment beans in order to add/remove functionality (offered by these back-end beans).
Now in Eclipse and OSGi I am looking for the best way to do this based on OSGi.
I know that I can make the beans as services and define start levels in OSGi but this does not solve my use case, which is:
The application starts without these beans/modules running and if the user updates the configuration from the running UI these beans/modules start and they are also started on the next start-up of the application.
Is there a nice/clean approach for my problem?
You probably want to use Eclipse Gemini Blueprint to do the management of how everything is integrated between Spring and OSGi (Gemini Blueprint is the successor to Spring Dynamic Modules). In particular, it can handle virtually all the complexity relating to dynamic service registration for you; your beans can remain virtually identical.
Another approach would be to use Declarative Services together with Configuration Admin to let configuration data determine which services to activate. In more detail here.
Like you already found out services are a good aproach to this. Simply install all your modules but do not start them. Then your UI can start and stop modules as the user selects the functionality he wants. THe OSGi framework then remembers the installed and started modules on a restart.
The absolute best approach for this is Declarative Services (DS). DS is integrated with OSGi's Configuration Admin, making it trivial to control the number of service instances as well as their configuration and service properties. For example, the following component (with the bnd annotations [which will resemble similar functionality in the OSGi specs soon]):
#Component(designateFactory=Config.class)
public MyComp implements MyService {
interface Config {
int port();
String host();
}
Config config;
#Activate
void activate(Map<String,Object> map) {
config = Configurable.createConfigurable(Config.class,map);
start();
}
void foo() { ... do the MyService stuff ... }
#Reference
void setDataSource( DataSource ds ) { ... }
}
This component requires a Configuration Admin factory configuration. The best way to see how powerful this is, is to setup a framework with Apache Felix Webconsole. The designateFactory=Config.class tells bnd to create a metatype XML file in the bundle. This is used by the Webconsole to create a pretty nice looking form for the configuration data, derived from the interface and its methods. This form is type aware, i.e. you cannot enter a non-nummeric value for the port number. Through the webconsole you can now instantiate multiple components by creating multiple factory configurations. Deleting these factory configurations, removes the service. In your application, you can manipulate Configuration Admin yourself under the control of the user.
Another advantage is that through Configuration Admin you can control the binding of the component's dependencies. In the aforementioned example, you can set the dataSource.target property to a filter like (db=accounting) to select the accounting database. All configuration properties are added as service properties so you can easily set the 'db' service property on the configuration that creates the Data Source (if it was implemented this way).
This is one of the least understood advantages of DS, and I can tell you it is HUGE. To get started with this this, just create a DS project in bndtools and then a new Run Descriptor and select the Webconsole template.
Another advantage of DS is that it is small and it is not try to hide the dynamics, which in Blueprint can be painful.
OSGi employs a service-oriented architecture: Bundles register service objects, that other bundles consume. Service publishing and binding is managed by the framework. This decouples service providers from service users completely (except for the need to agree on a service interface).
Is there a way to limit (by configuration) what services are visible to what bundles ?
For example, if I have an HttpService, all bundles that feel like doing so can install servlets into it. I would like to make the HttpService not visible to selective bundles.
For extra credits: In addition to just filtering service registrations, the ability to modify registration properties. So that even if a bundle registers a Servlet with alias=/admin, I can change that to alias=/somethingelse for consumption by Pax Web Extender Whiteboard.
Is there a way to limit (by configuration) what services are visible to what bundles?
As you are aware, it is possible to filter on service properties, though this probably doesn't give the sort of control you are asking for: the services are still visible to other bundles deployed in the framework.
In SpringSource's dm Server (an open-source, modular, OSGi-based Java application server) an application can be Scoped when it is deployed. This allows you to deploy multiple applications (in separate scopes) that might include inconsistent versions of dependent bundles, while still allowing common bundles to be shared (by deploying them outside of a scopeāin the so-called global scope).
If a scoped application/bundle registers an OSGi service it is only available to the bundles in the same scope. (The services are 'scoped' as well.)
This is not magic: the server wraps the OSGi services interfaces and uses service properties 'under the covers' to perform the filtering required on-the-fly.
I think this would give you the sort of separation you are looking for.
For information about dm Server (not to be confused with Spring DM) go to the SpringSource.org dmServer page.
Steve Powell
SpringSource; dm Server Development
The upcoming R4.2 of the OSGi specification define a component called Find Hook that allows exactly to:
"inspect the returned set of service references and optionally shrink the set of returned services"
See
http://www.osgi.org/download/r4-v4.2-core-draft-20090310.pdf section 12.5
Please note that R4.2 is not final yet, still I believe the major OSGi implementations (Felix and Equinox) already have the code for this additional functionality in their trunk
Is there a way to limit (by configuration) what services are visible to what bundles ?
There is no way to do that using service properties. You could define your own service property that specifies which bundles should consume the service you are exporting, but there is no way to prevent other bundles from consuming it as well.
For extra credits: In addition to just filtering service registrations, the ability to >modify registration properties. So that even if a bundle registers a Servlet with >alias=/admin, I can change that to alias=/somethingelse for consumption by Pax Web >Extender Whiteboard.
Well... that's a tough one. You could define your own Servlet interface "MyServlet" and export your Servlets using that interface. Then, another bundle could consume those MyServlets and re-export them as Servlets with modified service properties.
Other than that ... no idea.
I haven't tried this, but seems like it may help you...
In the OSGi R4 Component Spec describes the "Configuration Admin Service" which, from a 5 minutes inspection, appears to be able to alter services dynamically.
Ultimately I think it will be up to you to control access to the services based on some agreed upon configuration values
For extra credits: In addition to just filtering service registrations, the ability to modify registration properties. So that even if a bundle registers a Servlet with alias=/admin, I can change that to alias=/somethingelse for consumption by Pax Web Extender Whiteboard.
using iPOJO you can change properties of the exposed services pretty simple. It has bunch of other features, which could be interessting to someone using OSGi a lot.
If you want to limit the visibility of services, your best bet is to enable OSGi security. It is designed to limit what services, packages and other things are visible to what bundles. You can for example only make a certain service available to a bundle that is signed by you (or use various other criteria).
The other option, already mentioned, it to use the 4.2 service hooks, which allow a sort of "do it yourself" security mechanism.
The second question, changing properties like the endpoint under which a service is registered, is something you can do via the ServiceRegistration that you get back when registering your service. Changes can be triggered by becoming a ManagedService and use ConfigurationAdmin to configure yourself.
I'm trying to expose a web service method via JAX-WS annotations. Many examples I've seen reference the EndPoint.publish() method to quickly stand up the service in a standalone app (ex from Java Web Services: Up and Running, 1st Edition):
public class TimeServerPublisher {
public static void main(String[ ] args) {
// 1st argument is the publication URL
// 2nd argument is an SIB instance
Endpoint.publish("http://127.0.0.1:9876/ts", new TimeServerImpl());
}
}
One thing that I'm missing is how to accomplish essentially the same thing but in an existing app. Would I make a servlet to handle this? What is the proper way to publish this service in an existing WAR file?
In a container you don't have to publish like this. The container will do the publish. If you plan to use it in JBoss server try JBossWS otherwise for Tomcat or any other server Axis2 may be the better choice.
Read more from the following links.
http://jbossws.jboss.org/mediawiki/index.php?title=JBossWS
http://ws.apache.org/axis2/
This depends on what WS stack you are using.
If you are using Java 6 then that includes the JAX-WS reference implementation, then you can consult the documentation about JAX-WS RI WAR contents.
As #Jerrish and #andri coments, there are different aproaches and solutions, depending on your concerns.
The idea behind is that you don't need to set the configuration (port, etc) when will be published your web service. The best approach could be to set this via configuration files (XML, properties, etc) or using #Annotations.
For example, if you're accustomed to use frameworks like Guice or Spring, you know that is possible/recommended to start the context of your application publishing or initializing some objects, factories, datasources, etc and publishing webservices is another task that can be done in this time, because will be available when you will start your application, isn't?.
By the way, I've good experiences with CXF and another solution could be Spring Web Services another powerful solution for creating web services.