Adam - Like it. Please comments inline.../Thomas ________________________________
From: Adam Murdoch [mailto:[email protected]] Sent: Tuesday, March 08, 2011 2:45 AM To: [email protected] Subject: Re: [gradle-dev] RE: Gradle's OSGi plugin On 04/03/2011, at 11:44 PM, Glaeser, Thomas wrote: Niclas - Well. The current OSGi plugin is very simple as it basically only invokes BND to generate the manifest. As of now, the OSGi plugin itself does not apply the Java plugin. Let's discuss the following cases (1) Apply the OSGi plugin together with the Java Plugin (2) Apply the OSGi plugin together with the War Plugin I guess over here we are in a similar situation like you guys from Qi4j. We have mostly non OSGi-aware applications but moving more and more into the direction of OSGi. More and more projects we build need to run in the two different execution environments. Right now, case (1) works reasonable well for simple scenarios where you don't want to embed any dependencies into your bundle and don't work with fragments. Here we use an extension to the Gradle OSGi plugin in order to handle embedded jar files. I have not invested anything in case (2), however would expect some surprises. But what should happen is that with case (2) all your runtime dependencies will be embedded into the bundle under path WEB-INF/lib while with case (1) it is expected that your runtime dependencies are provided as part of the application classpath. So we already have different behavior here when it comes to runtime dependencies. This is not good, one would expect the same handling of dependencies no matter what kind of OSGi bundle gets build. The reason for this is, that the current OSGi plugin does not care about dependency configurations at all. This has to change as an OSGi classpath will be different from a plain old Java classpath. Now I would argue that an OSGi bundle is very similar to a WAR. I believe that Peter Kriens documentation about WAB confirms this. Both are container applications. I agree that we should think of OSGi bundles as similar to WARs. We could also think of a jar published to a repository in a similar way, where the ivy.xml/pom.xml descriptor provides meta-data about what must be provided by the execution container at runtime. More generally, we might do something like: * A Gradle project bundles bytecode for the JVM into a number of different archives. These archives might be a JAR, WAR, OSGi bundle, and so on, or some combination of these. We might also model GWT/Android/GAE applications and Gradle plugins, and so on, the same way. * At build time, a subset of runtime dependencies for the project must be embedded in the archive. For example, a WAR generally embeds (all runtime dependencies - those dependencies provided by the web container). A 'fat' JAR might embed all runtime dependencies. An OSGi bundle might embed (all runtime dependencies - those provided by the container itself - those provided as services in other bundles). <TG>I'm confused. Why must the container provided dependencies be embedded in the archive? E.g. in the case of an WAR everything from the providedRuntime configuration will not get embedded into the archive. I also would rather say that runtime dependencies for the project might be embedded in the archive.</TG> * At build time, some meta-data is generated to describe to the target container which runtime dependencies the archive now requires. This meta-data is included either in the archive, in the case of an OSGi bundle, or published with the archive, such as in the case of a JAR + pom.xml. The key point here is that it is the target environment that determines which dependencies will be provided at runtime, and those which need to be embedded. That is, there is no single 'provided' classpath. Instead, there are potentially several. <TG>I like that. This is very much true. No matter what target environment gets shipped with the product, for testing the different projects will require a subset of the provided classpath only.</TG> I think it would be useful to model the target environments/containers, so that: * A target environment can define which dependencies will be provided by that environment. * A target environment can contribute to the compile classpath for the project. For example, we might have a 'generic servlet 3.0 container', which adds the appropriate servlet api, and so on to the compile classpath. Or we might have a 'jetty 7 container' which adds the jetty-specific api to the compile classpath. Similarly for 'generic OSGi container' and 'equinox OSGi container' * A target environment can influence generation of the archive. For example, we might have a 'tomcat 7.0 container' which adds, say, some tomcat-specific descriptor to the WAR. <TG>Agreed.</TG> This would have some consequences for the new OSGi plugin - It would need to apply the Java plugin internally the same way the WAR plugin does - The meaning of the provided* configurations are the same as with the WAR plugin (- There would be additional supply* configurations; that's something the WAR doesn't have) <TG>I like the following design. Still have couple questions.</TG> I would do this slightly differently: 1. Split the Java plugin into 3 pieces: * A 'java' plugin which knows how to compile Java source into bytecode and how to run unit tests for it. * A 'jar-library' plugin which knows how to build and publish a plain jar. * A 'java-runtime' base plugin which provides the minimal pieces to define the runtime meta-data for the project: the set of classes, and the set of runtime dependencies. The 'jar-library' plugin would: * Implicitly apply the 'java-runtime' plugin. * Add the jar task. * Configure the publications so that the jar is published, along with the runtime dependencies in the dependency descriptor. 2. Change the 'osgi' plugin to: * Implicitly apply the 'java-runtime' plugin. It would not apply the 'java' plugin. * Add an osgiBundle task. It would not modify the jar task. It would create a separate bundle task. * Configure the publications so that the bundle is published. * Introduce the concept of target OSGi container. <TG>We still need to compile the Java sources into byte code and run unit tests. I guess we either apply the 'java', 'groovy', or 'scala' plugin in addition, right?</TG> 3. Change the 'war' plugin to: * Implicitly apply the 'java-runtime' plugin. It would not apply the 'java' plugin. * Add the war task. * Configure the publications so that the war is published. * Introduce the concept of target web container. * We would remove the provided* configurations. <TG>We still need to compile the Java sources into byte code and run unit tests. I guess we either apply the 'java', 'groovy', or 'scala' plugin in addition, right?</TG> 4. Change the 'application' plugin to: * Implicitly apply the 'java-runtime' plugin. It would not apply the 'java' plugin. * Add the distZip and install tasks. * Configure the publications so that the application zip is published. <TG>We still need to compile the Java sources into byte code and run unit tests. I guess we either apply the 'java', 'groovy', or 'scala' plugin in addition, right?</TG> This approach allows: * Creating any combination of stand-alone jar, OSGi bundle and WAR. * Multiple jars, bundles or WARS, each targeting a different environment. * A project which contains no Java source code. For example, a parent project which aggregates the classes of its children. Or a Groovy/Scala/Clojure project. <TG>To me this seems to be a very good design. However it introduces a couple breaking changes. How do we want to proceed?</TG> When migration your project from POJO to OSGi the following would need to be done - Apply the OSGi plugin +instead+ of the Java plugin - Replace most, if not all, of your compile/runtime dependencies by providedCompile/providedRuntime I would consider this a minor migration effort. One has to divide the project dependencies into three buckets anyway when moving to OSGi. When running with Equinox, the feature descriptor would list additional dependencies (supply*) that need to be injected into the execution environment in order to run your bundle. Everything build with the new OSGi plugin could be executed in an OSGi container +or+ as normal Java application, the only difference is how you would create the classpath from the three sets of configurations. Does this make sense? /Thomas -- Adam Murdoch Gradle Developer http://www.gradle.org Co-Founder and VP of Engineering, Gradleware Inc. - Gradle Training, Support, Consulting http://www.gradleware.com
