aol / micro-server

Microserver is a Java 8 native, zero configuration, standards based, battle hardened library to run Java Rest Microservices via a standard Java main class. Supporting pure Microservice or Micro-monolith styles.
http://micro-server.io/
Apache License 2.0
940 stars 212 forks source link

Generating a war instead of executable jar #152

Closed marcocast closed 8 years ago

marcocast commented 8 years ago

We are running a server containing other .war services and we would like to include services created with micro-server as well. Is it possible to generate .war instead of .jar? I know it's possible from spring-boot (https://spring.io/guides/gs/convert-jar-to-war/) , so maybe there is a way to do it using the microboot plugin ?

Thanks guys!

johnmcclean commented 8 years ago

Hi Marco,

You want to deploy a micro-server application inside an existing Tomcat (or other webserver) along side other war files?

At the moment the micro-boot plugin creates a Microserver front end and a spring-boot backend, so it probably won't give you what you need here.

In order to deploy as a .war file inside a pre-existing container wrap the jar file up in a war file you would need to take the following steps.

  1. Create a custom version of JerseySpringIntegrationContextListener. Remove the constructor that takes a ServerData object (you need to construct that yourself). This is the bridge between Spring and Jersey.
  2. In contextInitialized, before the JerseyRestApplication calls Create a MicroserverApp instance don't call run or start (that tries to start a Grizzly or Tomcat), just call the constructor. This will create your SpringContext with all the Microserver beans as well as setting up your Jersey app.

    Module m = ()->()->"my-app";
    MicroserverApp app = new MicroserverApp(module);
  3. Extract the Spring context via getSpringContext

     ApplicationContext springContext = app.getSpringContext();
  4. Extract your Rest Resources from the context

      List resources = springContext.getBeansOfType(RestResource.class); //or getBeans with Annotation if you are using the Rest annotation.
  5. Create a ServerData object passing in the resources, Spring Context, port, url and Module.
  6. Run the rest of the initializeContext section (that creates your Jersey application)
  7. Configure Jersey in the web.xml as follows

    add org.glassfish.jersey.servlet.ServletContainer as the Jersey Rest Web Servlet

    set

    javax.ws.rs.Application to com.aol.micro.server.rest.jersey.JerseyRestApplication context to your apps contect providers to your apps provides

  8. Let us know how you get on / ask any questions
  9. Create a micro-war module with the code to help anyone else do it :)
marcocast commented 8 years ago

Thanks John,

I will give it a go and let you know how it goes.

johnmcclean commented 8 years ago

Cool @kewangie can probably help out here too, he did a lot of the initial work of removing xml while we were still deploying into wars. Some of that info is probably not needed by the way (like setting port on ServerData, but set it on the first pass anyway to keep things simple).

johnmcclean commented 8 years ago

You may also need to create a custom version of com.aol.micro.server.rest.jersey.JerseyRestApplication that accepts the jersey config information from your ServletContextListener directly into a set of static variables. It currently uses ThreadLocal variables which helps with running multiple embedded Jersey apps on a server (via Grizzly).

Almost certainly won't work out of the box for you because of the ThreadLocals. In Grizzly started programmatically, it is called on the app initializer thread. This doesn't happen with Tomcat when use programmatically, so there is a wrapper class which is used instead ( com.aol.micro.server.rest.jersey.CustomJerseyServlet ). There is some chance that might work out of the box, but if not - pass the resources etc into a simple static maps in a custom JerseyRestApplication. We can merge the changes back into the core later.

tobwiens commented 8 years ago

Hi John,

thank you for your thorough explanation. I am following your steps to understand what we need to do for the customization.

From the explanation I got a few questions:

1) I found in the createApp Method in the GrizzlyApplicationFactory class, a very similar code.

PStack resources = extractor.getRestResources(rootContext);

        Environment environment = rootContext.getBean(Environment.class);

        environment.assureModule(module);
        String fullRestResource = "/" + module.getContext() + "/*";

        ServerData serverData = new ServerData(environment.getModuleBean(module).getPort(), 
                resources,
                rootContext, fullRestResource, module);

The difference I see to your code is, that the resources are extracted by the ModulaDataExtractor and you extract a subset of resource with:

List resources = springContext.getBeansOfType(RestResource.class); //or getBeans with Annotation if you are using the Rest annotation.

Did I get that right?

So the ServerData instances only differs in the resources it contains?

2) The JerseyPlugin class returns the JerseySpringIntegrationContextListener, which I need to customize. So I could replace the JerseyPlugin or add another plugin?

3) interface Module has the method

default List<ServletContextListener> getListeners(ServerData data)

Which I could override in a customized (WarModule) class which applies map(fn->fn.apply()) instead of map(fn->fn.apply(data))? Did I get that right?

Thank you John. Please push me on the right track if I am on the wrong one.

Cheers, Tobias

johnmcclean commented 8 years ago

@tobwiens

Hi Tobias,

1) Yes that's right, eventually for the micro-war plugin we would want to extract both types, but to get a working prototype you can just extract Resources by the marker you are using (@Rest or implements RestResource). The GrizzlyApplicationFactory is also extracting property information (port / hostname overrides) and setting up the filters / servlets which we can ignore for now (i.e. use web.xml & server.xml)

2) I would import it for now, so the same classes are available. It won't be triggered as jax-rs configuration is triggered inside the programmatic Grizzly / Tomcat builder code. See my other comment above about the way JerseyRestApplication finds it's resources (I suspect a bit of customization will be required there too).

3) I don't think that code will be called (at least in a first-pass micro-war plugin). As it's a lot simpler to configure Servlets / Filters & Listeners directly in the web.xml. Once we have a working micro-war plugin, we could add a delegating instance of each to allow fuller Microserver configuration to work.

If you create a fork on github - I can take a look at the code you are writing and help out that way too!

Thanks! John

johnmcclean commented 8 years ago

@tobwiens @marcocast

Hey guys -

A completely unorthodox (slightly crazy :) approach would be to try starting Microserver on a different port inside a ServletContextListener, i.e. just move your main method inside the war file. Grizzly especially with NIO can be pretty lightweight, and this would keep your application thread pools separate (I haven't looked at multi-tenant Tomcat in a long time, but looking at the docs I think all apps share an Executor configured per connector).

marcocast commented 8 years ago

I like this pragmatic approach :), but our main problem is that our clients sometimes, do not want to open new ports on their servers for security reasons :(

On Mon, Jan 18, 2016 at 11:15 AM, johnmcclean-aol notifications@github.com wrote:

@tobwiens https://github.com/tobwiens @marcocast https://github.com/marcocast

Hey guys -

A completely unorthodox (slightly crazy :) approach would be to try starting Microserver on a different port inside a ServletContextListener, i.e. just move your main method inside the war file. Grizzly especially with NIO can be pretty lightweight, and this would keep your application thread pools separate (I haven't looked at multi-tenant Tomcat in a long time, but looking at the docs I think all apps share an Executor configured per connector).

— Reply to this email directly or view it on GitHub https://github.com/aol/micro-server/issues/152#issuecomment-172487944.

Marco Castigliego

On the web : technical blog http://rdafbn.blogspot.co.uk/ , opensource http://code.google.com/p/grep4j/ , linkedin http://www.linkedin.com/pub/marco-castigliego/0/797/a26

johnmcclean commented 8 years ago

See : https://github.com/aol/micro-server/pull/153

johnmcclean commented 8 years ago

As of Microserver v0.80 it is possible to use Microserver to configure Spring Boot Jersey applications via the micro-spring-boot plugin (note this is a new plugin, micro-boot covers Spring Boot backends and Microserver front-ends).

marcocast commented 8 years ago

Fantastic! I will test it and I will let you know.

marcocast commented 8 years ago

Is swagger UI embedded as well in this plugin?

johnmcclean commented 8 years ago

Actually the swagger plugin one is that still needs a bit of work to correctly identify the resources, as I didn't integrate in Microserver ContextListeners / Servlets or Filters. I think adding the ContextListener required for the Swagger plugin should be trivial though - I'll do that tomorrow (or possibly next day, but sometime this week for sure) and release v0.80.1.

johnmcclean commented 8 years ago

Also - I need to document this - but if you want to override some of the properties on the Module, you need to make your module a spring bean (this is because I'm passing it to some Spring classes) . Easiest way is to make the main class implement Module and just pass that in to the Microserver app.

johnmcclean commented 8 years ago

Got it working, will release it tomorrow :)

By the way - nice new feature in Cyclops made this a lot easier too

SequenceM.fromIterable(listeners)
                .forEachWithError(l->l.contextDestroyed(sce),
                        e->logger.error(e.getMessage(),e));
johnmcclean commented 8 years ago

micro-spring-boot swagger support released in 0.80.1 - should be in maven central shortly.