开发者

Maven beginner questions - though somewhat a complex project setup

So I would classify myself as a senior developer with little java experience. I am working on a little pet project that is basically a desktop app with and sdk for plugins, and a set of plugins that the app loads as long as they implement the interfaces defined in the sdk. It looks something like this:

Project Structure

main-app
|---pom.xml (depends on <sdk>)
|---src
    |---[com.etc…]
|---target
    |---main-app.jar (does this include sdk.jar?)


sdk
|---pom.xml
|---src
    |---[com.etc…]
        |---IPlugin (public interface IPlugin)
|---target
    |---sdk.jar

(sdk - defines the interface that plugins need to implement)


plugins
|---plugin1
    |---pom.xml (depends on <sdk>)
    |---src
        |---[com.etc…]
    |---target
        |---plugin1.jar

plugin1 is a plugin that implements IPlugin interface and has the following classpath
<com.foo.plugin1>

deploy
|---app.folder
    |---main-app.jar
    |---sdk.jar
    |---plugins.folder
        |---plugin1.jar

On application sta开发者_StackOverflow社区rtup the main.app parses the plugins.folder and opens each jar file - looks for an element in the manifest which specifies the class-path of the plugin that implements the IPlugin interface.

There are several maven/jar concepts I am having trouble with...

  • My deploy folder is currently fantasy. It does not exist. I do not have the knowledge to make it exist. Currently I have a separate JAR file in each sub-project (or module as they are called in IntelliJ)
  • Does the main-app.jar have to include the sdk classes in it?
  • If not, how do I deploy main-app.jar so that it knows about sdk.jar, and where do I put sdk.jar?
  • Is there a way to add a custom field to the plugin1 jar manifest file where I can specify its classpath for the main-app to associate it with IPlugin interface?

So far my pom.xml files are super simple. They basically define the groupID, artifactID, and packaging type (JAR) as well as their dependencies. thats all.

Currently main-app (sans plugins) successfully builds and runs in IntelliJ - but this does not help me at all with deployment packaging.

Edit:

Just to be clear... main-app and sdk are built and deployed during development. plugin1 is added by a third party to the plugins.folder. How do third party developers specify their class path in the plugin.jar manifest file using maven?


If main_app is not a web application and depends on other jars and if you need to distribute it, one way to do this would be to create an assembly (say a .zip or .tar.gz file) which includes main_app.jars and the dependant jars. Maven Assembly Plugin can help you do this.

You can do manifest customization using Maven Jar Plugin


I may not have understood your question. But it looks like what you are trying to achieve is trivial.Your main app is dependent on two other modules. So you just need to add dependency tag in your main app project referencing the child modules. ( I will leave the scope empty).

Lets say your pom for Plugins module reads like this.

<groupId>om.foo.plugin1</groupId>
<artifactId>plugin1</artifactId>
<version>1.0</version>

Just add the corresponding dependency tag in main module.

<dependency>
    <groupId>om.foo.plugin1</groupId>
    <artifactId>plugin1</artifactId>
    <version>1.0</version>
</dependency>

Just one little thing, dependencies needs to be build with mvn clean install, before you can build the parent.


Truthfully, this is a fairly substantial build, and there are some best practices that I use to keep all this stuff organized.

First, I would partition your code into several modules, and lay it out as follows:

pom.xml
/src
    /app
        /app-name
            pom.xml
            /src ...
        /app-name-sdk
            pom.xml
            /src ...
        /deployment
            pom.xml
            /src
                /assembly
                    image.xml
                /main
                    /app-folder
                        /bin
                            startup-script.sh
                            startup-script.bat
                        /etc
                            config.conf
                            ...
    /plugins
        /plugin-A
            pom.xml
            /src ...
        /plugin-B
            pom.xml
            /src ...

An this point, you've logically separated out all the various component pieces, leaving it clear where everything is, packaged into logical groups. (Now it doesn't look like folder vomit when I check this out again, since I can now keep /doc and /bin directories at the top level and know that the sub-directories under /src describe what's in each associated submodule.)

At this point, the top level pom.xml should look approximately like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <artifactId>app-name-parent</artifactId>
    <groupId>com.mycompany.my-app</groupId>
    <version>1.3.2-SNAPSHOT</version>
    <packaging>pom</packaging>

    <name>[Parent]  My App</name>

    <modules>
        <!-- App -->
        <module>src/app/app-name</module>
        <module>src/app/my-app-sdk</module>

        <!-- Plugins -->
        <module>src/plugins/plugin-A</module>
        <module>src/plugins/plugin-B</module>

        <!-- Packaging -->
        <module>src/app/distribution</module>
    </modules>

    <properties>
        <!-- Useful properties to define here -->
        <scm.revision>USER</scm.revision>
        <compiler.debug>true</compiler.debug>
        <compiler.optimize>false</compiler.optimize>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- If you're not going to version your plugins with the parent,
             then you should pick out the specific version info here 
             for each plugin.  That way, you could just update individual plugins.
             If this is to be packaged up and shipped together, it's probably not necessary.
        -->
        <plugin-A.version>1.1</plugin-A.version>
        <plugin-B.version>1.3</plugin-B.version>
    </properties>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.3</version>
                    <configuration>
                        <source>1.6</source>
                        <target>1.6</target>
                        <debug>${compiler.debug}</debug>
                        <optimize>${compiler.optimize}</optimize>
                        <fork>true</fork>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.5</version>
                    <configuration>
                        <includes>
                            <include>**/*Test.java</include>
                        </includes>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

    <dependencyManagement>
        <dependencies>

            <!-- Test Dependencies -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.3.1</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <profiles>
        <profile>
            <id>release</id>

            <properties>
                <compiler.debug>false</compiler.debug>
                <compiler.optimize>true</compiler.optimize>
            </properties>
        </profile>

        <profile>
            <id>ci-server</id>

            <activation>
                <property>
                    <name>env.SVN_REVISION</name>
                </property>
            </activation>

            <properties>
                <scm.revision>${env.SVN_REVISION}</scm.revision>
            </properties>
        </profile>
    </profiles>
</project>

Then, all of your child modules need to have defined the link to the parent, like so:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>app-name-parent</artifactId>
        <groupId>com.mycompany.my-app</groupId>
        <version>1.3.2-SNAPSHOT</version>
        <relativePath>../../..</relativePath>
    </parent>

    <groupId>com.mycompany.my-app</groupId>
    <artifactId>app-name</artifactId>

    <name>[App]    My App</name>
    <description>
        TODO
    </description>

    <dependencies>
         <!-- System dependencies -->
        <dependency>
            <groupId>${project.parent.groupId}</groupId>
            <artifactId>app-name-sdk</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!-- Test Dependencies -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

For the love of all that is holy and right in this world, do not try to package up everything in one of the modules associated with a jar output. This is wrong, and all those who do this are shunt.

I'm kidding.

But seriously: break out the packaging process into its own separate module, here the src/app/deployment module to package up the other artifacts of the build. Then, it really only has dependencies on other components in the system, and can look something like:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>app-name-parent</artifactId>
        <groupId>com.mycompany.my-app</groupId>
        <version>1.3.2-SNAPSHOT</version>
        <relativePath>../../..</relativePath>
    </parent>

    <groupId>com.mycompany.my-app</groupId>
    <artifactId>deployment</artifactId>
    <packaging>pom</packaging>

    <name>[App]     Deployment Image</name>
    <description>
        TODO
    </description>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <id>image</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <tarLongFileMode>gnu</tarLongFileMode>
                            <descriptors>
                                <descriptor>src/assembly/image.xml</descriptor>
                            </descriptors>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>${project.parent.groupId}</groupId>
            <artifactId>my-app</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <!-- Notice the separate group namespace element 'plugins', for clarity -->
            <groupId>${project.parent.groupId}.plugins</groupId>
            <artifactId>plugin-A</artifactId><!-- Or project.version, depending... -->
            <version>${plugin-A.version}</version>
        </dependency>
        <dependency>
            <groupId>${project.parent.groupId}.plugins</groupId>
            <artifactId>plugin-B</artifactId>
            <version>${plugin-B.version}</version><!-- Or project.version, depending... -->
        </dependency>
    </dependencies>
</project>

Then you can use an assembly descriptor file (here, image.xml) to package up everything into one coherent whole, like so:

<assembly>
    <id>image</id>
    <formats>
        <format>tar.gz</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>

    <dependencySets>
        <!--  Everything  -->
        <dependencySet>
            <outputDirectory>/app-folder/jars</outputDirectory>
            <useProjectArtifact>false</useProjectArtifact>
            <fileMode>0644</fileMode>
            <directoryMode>0644</directoryMode>
            <!-- This includes all transitive, <scope>compile</scope> dependencies -->

            <includes>
                <include>${project.parent.groupId}:*</include>
            </includes>
        </dependencySet>
    </dependencySets>

    <!--  Packaged scripts, config files, etc.  -->
    <fileSets>
        <fileSet>
            <outputDirectory>/app-folder/bin</outputDirectory>
            <lineEnding>unix</lineEnding>
            <fileMode>0755</fileMode>
            <directoryMode>0744</directoryMode>
            <filtered>false</filtered>

            <directory>src/main/app-folder/bin</directory>
            <includes>
                <include>**/*</include>
            </includes>

            <!--  Some weird files are added here, for some reason  -->
            <excludes>
                <exclude>*.*.formatted</exclude>
            </excludes>
        </fileSet>
        <fileSet>
            <outputDirectory>/app-folder/etc</outputDirectory>
            <lineEnding>unix</lineEnding>
            <fileMode>0640</fileMode>
            <directoryMode>0744</directoryMode>
            <filtered>true</filtered>

            <directory>src/main/app-folder/etc</directory>
            <includes>
                <include>**/*</include>
            </includes>

            <!--  Some weird files are added here, for some reason  -->
            <excludes>
                <exclude>*.*.formatted</exclude>
            </excludes>
        </fileSet>
    </fileSets>
</assembly>

You can get a lot fancier with the packaging, but this is the bare bones that gets you 90% of the way there.

Sorry for the length of this, but I do this a lot, and following this practice really makes everything much cleaner and easier to figure out, particularly if I have to come back to it months afterwards.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜