I've built an application that loads multiple plugins over SPI. Each plugin has to implement the FrameworkPlugin interface.
public interface FrameworkPlugin {
...
ContextHandler getWebContent();
String getPluginID();
...
}
An example implementation would be
...
ContextHandler getWebContent() {
WebAppContext wac=new WebAppContext();
wac.setBaseResource(new ResourceCollection(new String[] {"./src/main/webapp"}));
return wac;
}
...
The Main-Project runs a single Jetty instance, which should be used by all plugins providing web-based services. A plugin has to be able to configure it's individual environment within its own context, except the base URL, which is managed by the main project.
...
for (FrameworkPlugin p : PLUGINS) {
ContextHandler h= p.getWebContent();
h.setContextPath("/plugin/"+p.getPluginID().toString());
collection.addHandler(h);
}
jettyInstance.setHandler(collection);
...
So in theory, plugins should be accessible under /plugin/<id>. Unfortunately Jetty throws an IllegalArgumentException, because plugins set ./src/main/webapp as resource base, which does not exist in the main project.
So how to supply a context handler via the plugin interface?
Deploying WARs is not an option, as the loading mechanism can't be changed. Everything has to be done by using the plugin interface.
If this isn't going to be a WAR based project, then skip the use of WebAppContext entirely, and just use the Handler system in Jetty itself.
The WebAppContext is a specialized Handler meant to be used with a full blown and complete Web Application that follows the servlet spec. It mandates behavior that is mandated by the servlet spec, from the mundane (how url mapping order behaves), past the black art (per element descriptor order lookup and override), to the complex (webapp classloader isolation).
The Handler system in Jetty can be 1..n Handlers, in a Tree (via HandlerCollections), sometimes with Context, mutable Handler collections, and all exists at the same classloader as the server (if you want it to). Feel free to url map with regex, or your own implementation of level 3 URI templates. Completely up to you what you want to do with it. You have many pre-built Handlers and base Handler to build up from.
You'll still have access to the standard HttpServletRequest and HttpServletResponse, along with the async processing and I/O that those provide. But you'll also have access to the raw internal Request object which offers even more features over the HttpServletRequest.
Incidentally, many servlet based applications support plugins now. Most accomplish this using Controller Servlets, or Filters, or manipulation of the ServletContext's registrations.
Related
I am looking for a way to configure during runtime (say, via a properties file, or deployment descriptor) which services in my Jersey-based application are available, i.e. so that services can be enabled or disabled by an administrator at the container level.
Our app presently exposes 15 different endpoints. It has 1 Application annotated with #ApplicationPath with 3 classes annotated with #Path, and within those 3 classes are 15 different methods annotated with the usual #Path/#/#Produces.
My app has a few different techniques for reading its runtime configuration, e.g. settings to connect to database resources, watching a properties file for changes, etc. What I want to do is add some configuration values so that administrators can enable/disable the 3 classes or any of the individual endpoints within those classes at the Jersey level. Could someone suggest the best way to do this?
Taking that a step further, we want to control this configuration in runtime, so if the configuration changes, we can update the jersey configuration to enable/disable the changed services without having to restart our container (which is Tomcat in this case).
Any advice appreciate! Thanks!
I don't know if this is possible natively with Jersey, but one thing I can think of is to just use a Jersey prematching filter to determine if the endpoints are disabled. You can use a service that can also act as a listener and update the disabled endpoints accordingly. If the endpoint is disabled, then just return a 404.
#Provider
#PreMatching
public class DisabledEndpointsFilter implements ContainerRequestFilter {
#Inject
private ConfigurationService configuration;
#Override
public void filter(ContainerRequestContext request) throws IOException {
final List<String> disabledEndpoints = this.configuration.getDisabledEndpoints();
final String path = stripLeadingSlash(request.getUriInfo().getPath());
for (String endpoint: disabledEndpoints) {
endpoint = stripLeadingSlash(endpoint);
if (path.startsWith(endpoint)) {
request.abortWith(Response.status(404).build());
return;
}
}
}
}
I was playing around with this and put together a working POC. You can checkout the Github Repo.
I would recommend use apache camel for this. With camel you can control the http requests combined with a embedded jetty server as proxy.
http://camel.apache.org/jetty.html
I do have a Java web application which has several servlets, I want to turn it into an optional module of a Spring mvc application. Ideally I would like to add the module as a "dependency" to make it available and add the proper link in my primary UI.
Is there a popular way to achieve this?
Many thanks in advance!
Convert all projects to maven projects, then Servlet specific projects are modules specified in the overarching MVC parent project.
That way, at build time, all required Servlet projects are automatically built by maven whenever you build the parent... And you can optionally exclude Servlets at build time.
To convert a normal java servlet project to spring mvc you would need to do the following
1) In your web.xml you would have to specify your dispatcher servlet with and the url pattern for which it would get invoked. Specify the servlet class as the dispatcher servlet class and servlet name as a custom name for this servlet
2) Create an xml file as servletname-servlet.xml
3) Convert your custom servlets to controllers by having them implement the Controller interface and specify then in the xml file created in the second step
4) Define the mapping of these controllers to the requests
5) Define a view resolver for the requests
6) Deploy
These are broadly the steps you would need to perform to convert your servlet project to Spring MVC
Depending on your servlet container(*), you could try to put the small application in its own servlet context on same container: that means that it would be a totally independant web application.
Then you install in main application a special controller mapped to a full hierarchy that would act as a forwarding relay for the secondary application. Something like :
#Autowired
String extContext;
#RequestMapping("/optapt/app1/**")
public void relay(HttpServletRequest req, HttpServletResponse resp) {
ServletContext ext = req.getServletContex().getContext(extContext);
String extUrl = req.getServletPath();
if (req.getPathInfo() != null) {
extUrl += req.getPathInfo();
}
ext.getDispatcher(extUrl).forward(req, resp);
}
That way, provided you have a global proxy that fordibs external clients to access the second application, all security can be managed by the main, and the second will only receive forwarded request from the main.
If for any reason, you have no proxy to hide the servlet container, you will still (but only) have security by obfuscation, since the direct addresses to the secondary application will never be published, provided you consistently only write addresses relative to first application in links and form actions
(*) ServletContext.getContext() may return null for security reasons on some servlet containers.
Is it possible with Guice (and even in Java in general) to iterate over all classes in a particular package and add them to Guice?
The underlying problem: I'd like to be able to route all traffic to /admin/* to a single servlet which redirects accordingly. Then I'd like to be able to just add servlets to the same package and have them get picked up automatically. E.g. If I navigate to /admin/showCompanyDetails, I'd like that to redirect to a servlet called showCompanyDetails.java in the admin package.
Furthermore, I'd like this to work in such a way that all I have to do to add further admin functions is to drop a new class into the admin package. I.e. No factory methods to update and no containers to add to.
So far, the closest I've come is to have the redirect servlet create a Guice injector with a module that contains all the admin servlets. But as I said, I'd like to avoid having to update a Guice module.
Also, I'd like this to be possible in AppEngine.
And I want a pony.
There are some possibilities:
Use Servlet 3 #WebServlet annotations on your servlet classes, so they get picked up by a Servlet 3 web container. Then you can use Guice to inject dependencies, see here for an example.
Use guice-automatic-injection to bind your servlet classes in your classpath (they must contain their path similar to Servlet 3 via annotations or provide an accessor for it). Then you can create a Guice servlet module which retrieves all those servlets from Guice and registers them as servlets to their provided paths.
Both ways may be usable in AppEngine, but I haven't got experience with it.
Getting a pony is easy if you use just Object as its base class ;p
I have an OSGi bundle (that is not owned by me - so I cannot change it!) that exposes (exports) a service EchoService, and I want to attach an aspect to methods of this service (so as to perform some pre/post processing around it). These are deployed on the Apache Felix container.
I've written my own OSGi bundle (that obviously imports the EchoService), and attaches Spring aspects to it using standard Spring AOP. However, looks like the aspects are not attached and my interceptor is not being invoked.
I suspect that this is because I'm trying to intercept a service that does not belong to my bundle (which seems reasonable). Is that correct? How can I overcome this?
Here's what my interceptor/aspect looks like:
#Before("serviceOperation()")
public void before(JoinPoint jp) {
logger.debug("Entering method: " + jp.toShortString());
}
#AfterReturning("serviceOperation()")
public void after(JoinPoint jp) {
logger.debug("Exiting method: " + jp.toShortString());
}
I'm not an AOP nor a Spring expert, but maybe I could give you some ideas. As far as I see Spring use standard J2SE dynamic proxies for AOP proxies. Hence your clients should use the proxy instead of the original EchoService object. This is also true when you're using CGLIB proxies because "the proxies are created by sub-classing the actual class".
If your client bundles asking for an EchoService you have to pass them the proxy somehow. For this inside an OSGi container you should also export an EchoService (proxy) and make sure that the clients use the proxied service/bundle, not the original. You can accomplish this by setting a different version number for the (proxied) package and set this version as an import requirement in your client bundles. (I suppose you can modify the clients of EchoService.) For services you can set a property when you're registering it and modify the clients to query only for services which have this property.
If you are not able to modify the client bundles another solution could be wrapping the original bundle as an internal jar in your bundle. You can call the wrapped bundle's activator from your activator and pass them a modified BundleContext. This BundleContext should catch the registering service calls and register the proxy object instead of the original EchoService. You can use simple delegate pattern since BundleContext, ServiceListener etc. are usually interfaces. I suppose it could work but it maybe has other challenges.
As a bit of background - I'm using JBoss 5.1, for web applications. The applications are vertically deployed so each feature ends up in a separate WAR file. It has a jar file for dao and business logic. This is fine so far however I need to deploy another app which doesn't have much logic in itself - its basically a view like say dashboard. The dashboard needs to aggregate data from different data providers(usually they are other apps/features). Right now the dashboard knows way too much about other features. So everytime a new feature is added this dashboard gets redeployed as well with relevant code additions.
It would be great if there is a common interface for this dashboard that few other features implement and whenever a new feature(WAR) is deployed the dashboard can dynamically get data from the new provider as well. Is this possible? If not what is the closest I can get to without manipulating classloaders for the apps? It would be good to know if first of all this is possible inside jboss.
Please let me know if you need more info.
There are a couple of ways to do what you're talking about, so I'll propose two types of solutions and I can give you more info about whichever fits your needs best.
A relatively quick solution is to use a portal server like GateIn. Your WARs could be displayed on the same page, but they'd be in seperate places and not integrated. You'd have to turn your WAR's into portlets and have an administrator add them to the portal's UI, but the portal would be able to scan and detect all available portlets.
A more flexible solution would be to have one of your classes for each deployment implement a common MBean interface. Your dashboard could then use JMX, specifically javax.management.MBeanServerConnection's queryMBeans method to obtain all MBeans (or a subset of MBeans belonging to a particular package, which you can specify as a query parameter). Then you can execute interface methods through javax.management.MBeanServerConnection's invoke method. To obtain the MBeanServerConnection with JBoss, call org.jboss.mx.util.MBeanServerLocator.locateJBoss().
Some additional detail as requested (Note, the following is JBoss-specific):
1) Turn your deployments into MBean's
For each of your JAR files, add a jboss-service.xml and *-xmbean.xml file to the META-INF directory (where * is a name of your choosing). Follow this example for those files.
Implement the MBean at whatever path you specified in the jboss-service.xml mbean element's code attribute (org.jboss.chap2.xmbean.JNDIMap in the example). Specify a consistent namespace and parameter for the jboss-service.xml mbean element's name attribute (chap2.xmbean:service= in the example). The operations and attributes you specify in the *-xmbean.xml file should map exacly to your interface.
2) Create the dashboard and in one of it's classes poll the services (this code hasn't been tested, but should provide a good outline)
//Get the connection
MBeanServerConnection connection = org.jboss.mx.util.MBeanServerLocator.locateJBoss();
//Query for MBeans in the chap2.xmbean namespace
Set<ObjectInstance> mbeans = connection.queryMBeans(null, new ObjectName("chap2.xmbean:service=*"));
//Loop over each MBean and invoke an interface method
for (ObjectInstance mbean : mbeans)
{
//Invoking 'put' method from example. If this were an info method, this would return the result of the MBean operation
connection .invoke(mbean.getObjectName(), "put", new Object[] {"TestKey", "TestValue"}, new String[] {Object.class.getName(), Object.class.getName()});
}