Multiple persistence.xml files and JPA

When using JPA as the standard for doing object persistence, an important part of its configuration is utilizing a persistence.xml. The persistence.xml file is for configuring a Data Source to the Entities that you want to interact with. The persistence.xml usually will list classes that contain JPA annotations, mapping files that contain binding configuration, or archive files like jars that contain annotated classes or mapping files. One of the downsides of the development approach of trying to compartmentalize service functionality is that there might be multiple persistence.xml files spread through the jar files that are pulled together in a single service war. Without some kind of special merging capabilities, only one of those persistence.xml files will be used and the last one found on the classpath will win.  Therefore, if I have a persistence.xml file that lists all the orm.xml files I have for models and then produce a different persistence.xml file for a service with an its own orm.xml (named query), if the service orm.xml is the last one in the classpath, Hibernate will then complain that it can’t find the orm.xml files for the models that are used in the query.  There are three articles I want to look at in order to resolve this issue:

http://ancientprogramming.blogspot.com/2007/05/multiple-persistencexml-files-and.html

http://labs.bsb.com/2010/10/flexible-jpa-configuration-with-spring/

http://labs.bsb.com/2010/11/configuring-modular-jpa-applications-with-spring/

Based on these articles (specifically the second two), I am going to create a class called MultiConfigAwarePersistenceUnitManager.  One thing to understand is what was mentioned before, I get access to the meta-data for persisting objects via JPA annotations, mapping files, or jar files that contain one or both.  The article deals with merging multiple files from jars and this is not necessarily how the persistence is being done, so the class from the article will be modified to merge multiple the orm.xml files from multiple persistence files.

MultipleConfigAwarePersistenceUnitManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
import java.net.URL;

import javax.persistence.spi.PersistenceUnitInfo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;

/**
 * An extension to the {@link DefaultPersistenceUnitManager} that is able to merge multiple <tt>persistence.xml</tt>
 * associated to the same persistence unit name.
 * <p />
 * If a module persistence unit defines managed classes explicitly, only adds the specified classes. If the module
 * persistence unit does not define any managed classes, module scanning is assumed: any entity classes defined in the
 * module holding the persistence unit will be associated to the main one.
 */

public class MultiConfigAwarePersistenceUnitManager extends DefaultPersistenceUnitManager {

    /** The Constant LOGGER. */
    private static final Logger LOGGER = LoggerFactory.getLogger(MultiConfigAwarePersistenceUnitManager.class);

    /** The strict. */
    private transient boolean strict = false;

    /** The persistence unit name. */
    private transient String persistenceUnitName = "default";

    /**
     * {@inheritDoc}.
     */

    @Override
    protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
        super.postProcessPersistenceUnitInfo(pui);

        // This our template persistence unit that I am post-processing
        // so let's just skip.
        if (isApplicationPersistenceUnit(pui)) {
            return;
        }

        final MutablePersistenceUnitInfo mainPui = getMainPersistenceUnitInfo(pui);

        if (strict) {
            pui.addJarFileUrl(pui.getPersistenceUnitRootUrl());
        }

        if (mainPui != null) {
            if (strict) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Merging existing jar file urls " + mainPui.getJarFileUrls()
                            + " to persistence unit [" + pui.getPersistenceUnitName() + "]");
                }
                copyPersistenceUnit(mainPui, pui);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Persistence unit[" + pui.getPersistenceUnitName() + "] has now "
                            + "the following jar file urls " + pui.getJarFileUrls() + "");
                }
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Merging information from [" + pui.getPersistenceUnitName() + "] " + "to ["
                            + mainPui.getPersistenceUnitName() + "]");
                }
                mergePersistenceUnit(pui, mainPui);
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Persistence unit[" + mainPui.getPersistenceUnitName() + "] has now "
                            + "the following jar file urls " + mainPui.getJarFileUrls());
                }
            }
        } else if (!pui.getPersistenceUnitName().equals(persistenceUnitName)) {
            LOGGER.debug("Adding persistence unit [" + pui.getPersistenceUnitName() + "] as is (no merging)");
        }
    }

    /**
     * Specifies if the manager should process the persistence units in strict mode. When enabled, only the persistence
     * unit that have the exact same names as an existing one are merged. When disabled (the default), if the name of
     * the persistence unit matches the prefix, it is merged with the persistence unit defined by the prefix.
     *
     * @param strict
     *                        if merging occurs on an exact match or on the prefix only
     */

    public void setStrict(boolean strict) {
        this.strict = strict;
    }

    /**
     * Sets the name of the persistence unit that should be used. If no such persistence unit exists, an exception will
     * be thrown, preventing the factory to be created.
     * <p />
     * When the <tt>strict</tt> mode is disabled, this name is used to find all matching persistence units based on a
     * prefix. Say for instance that the <tt>persistenceUnitName</tt> to use is <tt>pu</tt>, the following applies:
     *
     * pu-base will be merged pufoo will be merged base-pu will <b>not</b> be merged
     *
     * Make sure to configure your entity manager factory to use this name as the persistence unit
     *
     * @param persistenceUnitName
     *                        the name of the persistence unit to use
     */

    public void setPersistenceUnitName(String persistenceUnitName) {
        this.persistenceUnitName = persistenceUnitName;
    }

    /**
     * Merges a persistence unit to another one. Takes care of handling both managed classes and urls. If the
     * persistence unit has managed classes, only merge these and prevents scanning. If no managed classes are
     * available, add the url of the module for entity scanning.
     *
     * @param from
     *                        the persistence unit to handle
     * @param to
     *                        the target (merged) persistence unit
     */

    protected void mergePersistenceUnit(MutablePersistenceUnitInfo from, MutablePersistenceUnitInfo to) {
        if (from.getMappingFileNames().size() != 0) {
            for (String s : from.getMappingFileNames()) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Adding entity [" + s + "]");
                }
                to.addMappingFileName(s);
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Added [" + from.getMappingFileNames().size() + "] mapping file to " + "persistence unit["
                        + to.getPersistenceUnitName() + "]");
            }
        } else if (from.getManagedClassNames().size() != 0) {
            for (String s : from.getManagedClassNames()) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Adding entity [" + s + "]");
                }
                to.addManagedClassName(s);
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Added [" + from.getManagedClassNames().size() + "] managed classes to "
                        + "persistence unit[" + to.getPersistenceUnitName() + "]");
            }
        } else {
            to.addJarFileUrl(from.getPersistenceUnitRootUrl());
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Added [" + from.getPersistenceUnitRootUrl() + "] for entity scanning "
                        + "to persistence unit[" + to.getPersistenceUnitName() + "]");
            }
        }
    }

    /**
     * Copies a persistence unit to another one. Takes care of copying both managed classes and urls.
     *
     * @param from
     *                        the persistence unit to copy
     * @param to
     *                        the target (copied) persistence unit
     */

    protected void copyPersistenceUnit(MutablePersistenceUnitInfo from, MutablePersistenceUnitInfo to) {
        for (String s : from.getMappingFileNames()) {
            to.addMappingFileName(s);
        }
        for (String s : from.getManagedClassNames()) {
            to.addManagedClassName(s);
        }
        // Copy jar file urls
        for (URL url : from.getJarFileUrls()) {
            to.addJarFileUrl(url);
        }
    }

    /**
     * Specifies whether the specified persistence unit is the template one we use to merge.
     *
     * @param pui
     *                        the persistence unit to test
     * @return <tt>true</tt> if the persistence unit is the target, template persistence unit
     */

    private boolean isApplicationPersistenceUnit(MutablePersistenceUnitInfo pui) {
        return (!strict && persistenceUnitName.equals(pui.getPersistenceUnitName()));
    }

    /**
     * Returns the main {@link MutablePersistenceUnitInfo} to use, based on the given {@link MutablePersistenceUnitInfo}
     * and the settings of the manager.
     * <p />
     * In strict mode, returns the declared persistence unit with the specified name. In non strict mode and if the
     * specified <tt>pui</tt> starts with the configured <tt>persistenceUnitName</tt>, returns the template persistence
     * unit info used for the merging.
     *
     * @param pui
     *                        the persistence unit info to handle
     * @return the persistence unit info to use for the merging
     */

    private MutablePersistenceUnitInfo getMainPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
        MutablePersistenceUnitInfo result = null;
        if (strict) {
            return getPersistenceUnitInfo(pui.getPersistenceUnitName());
        }
        if (pui.getPersistenceUnitName().startsWith(persistenceUnitName)) {
            // We have a match, retrieve our persistence unit name then
            result = getPersistenceUnitInfo(persistenceUnitName);
            // Sanity check
            if (result == null) {
                throw new IllegalStateException(
                        "No persistence unit found with name ["
                                + persistenceUnitName
                                + "] "
                                + "so no merging is possible. It usually means that the bootstrap-persistence.xml has not been "
                                + "included in the list of persistence.xml location(s). Check your configuration as it "
                                + "should be the first in the list!");
            }
        }
        // Nothing has been found
        return result;
    }

    /**
     * {@inheritDoc}.
     */

    @Override
    public PersistenceUnitInfo obtainPersistenceUnitInfo(String puName) {
        PersistenceUnitInfo persistenceUnitInfo = super.obtainPersistenceUnitInfo(puName);
        persistenceUnitInfo.getJarFileUrls().remove(persistenceUnitInfo.getPersistenceUnitRootUrl());
        return persistenceUnitInfo;
    }

}

This is done by creating a bootstrap persistence.xml file and providing a key name for a data sources persistence unit (for this example I will call it common):

common-bootstrap-persistence.xml or common-bootstrap-persistence-test.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
   http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">


    <persistence-unit name="commonDataServiceTestPu">
    </persistence-unit>

</persistence>

commonDataServiceTestPu is the key name for this data source and for doing the merge.  In the Spring Context file, I will set this value to be injected into our persistence merging class:

Common Jdbc Spring Context

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="commonPersistenceUnitManager"
      class="com.foo.MultiConfigAwarePersistenceUnitManager">
    <property name="persistenceXmlLocations">
        <list>
        classpath*:META-INF/jpa/common-bootstrap-persistence.xml
        classpath*:META-INF/jpa/common-persistence.xml
        </list>
    </property>
    <property name="defaultDataSource" ref="commonDataSource"/>
    <property name="persistenceUnitPostProcessors" ref="commonPersistenceUnitPostProcessor"/>
    <property name="persistenceUnitName" value="commonDataServiceTestPu"/>
</bean>

Here I am configuring our multi persistence manager, and I tell it where to find persistence files (the first one always being the bootstrapped file).  persistenceUnitName is where I set that bootstrap key.

So the MultiConfigAwarePersistenceUnitManager loads the common-bootstrap-persistence.xml file into memory and verifies that the persistenceUnitName matches.  Then as it finds other persistence.xml files, if the name of those files starts with this persistenceUnitName, then the orm files from that newly opened persistence.xml file are merged into the bootstrap persistence context.  Once this is complete, all common-persistence.xml will be merged into the context for the common-bootstrap-persistence.xml.

You can see this by running a JUnit where the Spring Context files are loaded:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.foo.AddressSearchServiceTest
2011-03-18 16:18:42,917 [main] DEBUG com.foo.JtaPersistenceUnitPostProcessor - Enriching the persistence unit info with the non-jta aware data source.
2011-03-18 16:18:42,933 [main] DEBUG com.foo.JtaPersistenceUnitPostProcessor - Enriching the persistence unit info with the non-jta aware data source.
2011-03-18 16:18:42,933 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Merging information from [commonDataServiceTestPuModule] to [commonDataServiceTestPu]
2011-03-18 16:18:42,949 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Adding entity [com/foo/AddressSearchService-orm.xml]
2011-03-18 16:18:42,949 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Added [1] mapping file to persistence unit[commonDataServiceTestPu]
2011-03-18 16:18:42,949 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Persistence unit[commonDataServiceTestPu] has now the following jar file urls []
2011-03-18 16:18:42,949 [main] DEBUG com.foo.JtaPersistenceUnitPostProcessor - Enriching the persistence unit info with the non-jta aware data source.
2011-03-18 16:18:42,949 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Merging information from [commonDataServiceTestPuModule] to [commonDataServiceTestPu]
2011-03-18 16:18:42,949 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Adding entity [com/foo/Address-orm.xml]
2011-03-18 16:18:42,949 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Adding entity [com/foo/Phone-orm.xml]
2011-03-18 16:18:42,949 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Adding entity [com/foo/Person-orm.xml]
2011-03-18 16:18:42,949 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Adding entity [com/foo/Email-orm.xml]
2011-03-18 16:18:42,964 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Adding entity [com/food/Codes-orm.xml]
2011-03-18 16:18:42,964 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Added [5] mapping file to persistence unit[commonDataServiceTestPu]
2011-03-18 16:18:42,964 [main] DEBUG com.foo.MultiConfigAwarePersistenceUnitManager - Persistence unit[commonDataServiceTestPu] has now the following jar file urls []

One benefit of utilizing this concept, is that I can rename the persistence.xml files to something unique and change their location in the classpath.  Why is this important?  As one of the articles states above, WebLogic Server uses Kodo as it’s default JPA provider which does not support schemas of version=”2.0″, and will cause errors if it finds orm.xml files or persistence.xml files listed with the version=”2.0″.  Kodo/WebLogic Server look for files called persistence.xml that are in the root of the META-INF directory.  So I have renamed the files and put them into a different directory.  This alleviates those issues and helps in our usage of Hibernate with more advanced JPA 2.0 features than WebLogic Server supports.

http://download.oracle.com/docs/cd/E12840_01/wls/docs103/ejb30/using_kodo.html

http://wiki.eclipse.org/EclipseLink/Development/JPA_2.0/weblogic

http://forums.oracle.com/forums/thread.jspa?threadID=2150287&amp;tstart=45

If you are interested in learning more about JPA, wikibooks has a really good online book for this information.

RESOURCES

http://en.wikibooks.org/wiki/Java_Persistence/Runtime

Share and Enjoy

Use Apache Shade to Create Spring Full

In my current environment, I have quite a few jars on the classpath that started exiting the Windows limit and producing the Classpath 87 error in Eclipse:

Eclipse CreateProcess Error 87

So after evaluating some quick/simple solutions, I wanted to ability to jar up some of the larger frameworks like Spring (which is 10+ jars) and CXF (which is 5+ jars). Jarjar plugin for Maven is a great solution for this, except the concern of simply jarjar’ing multiple jars together is losing the transitive dependencies that you are inheriting via the pom files of the third party pom files.

That is where the Apache Shade plugin came in and saved the day: Apache Shade.

Apache Shade has great features similar to the Jarjar concept, but the most valuable piece of this plugin is the ability to produce a new shaded pom file that will get installed into your Maven (or Nexus) repository, along with your new shaded jar. This would essentially allow me to build a Spring Full jar and output a pom file that included all the transitive dependencies in the shaded pom file. This way I could include only Spring jars into the full (shaded) jar and exclude dependency jars to allow easier upgrade of those individual jars.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<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>
    <groupId>org.springframework</groupId>
    <artifactId>spring-full</artifactId>
    <version>3.0.5.RELEASE</version>
    <packaging>jar</packaging>
    <name>Spring Full Jar</name>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <createDependencyReducedPom>true</createDependencyReducedPom>
                            <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
                            <artifactSet>
                                <includes>
                                    <include>org.springframework:*</include>
                                </includes>
                                <excludes>
                                    <exclude>org.springframework:spring-test</exclude>
                                </excludes>
                            </artifactSet>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.handlers</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.schemas</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.tooling</resource>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-instrument</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>3.0.5.RELEASE</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

An important piece to understand (that caused me a few issues until I included it) was:

1
2
3
4
5
6
7
8
9
10
11
<transformers>
    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
        <resource>META-INF/spring.handlers</resource>
    </transformer>
    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
        <resource>META-INF/spring.schemas</resource>
    </transformer>
    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
        <resource>META-INF/spring.tooling</resource>
    </transformer>
</transformers>

The reason is that when you are essentially converging multiple jars into a single jar, config files are going to get overridden. This is more or less the last configuration file (with the same name) wins and is included in the new single jar. So I need a mechanism to append these config files together into one config file. So with Spring, I have files such as spring.handlers/spring.schemas/spring.tooling that are included in many of the Spring Jars. So when using Apache Shade, I need to use the AppendingTransformer to append the multiple configuration files together. If you forget to do this, your Spring context files will throw errors complaining that a spring schema doesn’t exist that has been defined in the context file.

Another important piece of this to understand are the two configuration options createDependencyReducedPom and promoteTransitiveDependencies. The promoteTransitiveDependencies helps to determine what goes into the reduced pom dependency, whether transitive dependencies get promoted up one level into the pom. If this is false, the the reduced pom will not include any transitive dependencies, which would require me to include those in another pom and manage them (which I don’t want to do).

When I created a Spring Batch full, promoteTransitiveDependencies is important because the transitive dependency that I am inheriting is an older version of Spring. So for example, I are utilizing Spring 3.0.5.Release and the latest Spring Batch pulls in Spring 2.5.6. So the problem with using with the dependency reduced pom from Spring Batch, is that it will include a dependency to Spring 2.5.6 and I have the Spring now merged into our own custom Spring Full Jar (3.0.5.Release). So I need to exclude the Spring transitive dependency from the Spring Batch Full Jar.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<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>
    <groupId>org.springframework.batch</groupId>
    <artifactId>spring-batch-full</artifactId>
    <version>2.1.8.RELEASE</version>
    <packaging>jar</packaging>
    <name>Spring Batch Full Jar</name>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <createDependencyReducedPom>true</createDependencyReducedPom>
                            <promoteTransitiveDependencies>false</promoteTransitiveDependencies>
                            <artifactSet>
                                <includes>
                                    <include>org.springframework.batch:*</include>
                                </includes>
                                <excludes>
                                    <exclude>org.springframework:spring-test</exclude>
                                </excludes>
                            </artifactSet>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.handlers</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.schemas</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.tooling</resource>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-core</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>
    </dependencies>

</project>

You can see that the promoteTransitiveDependencies configuration is set to false, because I am using our Spring Full Jar.

To add one final piece to this, I need to create a CXF Full Jar. This also needs some customization because CXF is based on Spring and I need to promote our Full Jar. Also, versions of CXF can bring in dependencies like Bouncycastle (which caused issues with Digital Signatures in a JDK 1.6 environment), and I also want to promote a newer version of WSS4J. And since CXF is built with the strategy of Spring, I have configuration files that I need to do the custom appending too as well. This is what the CXF Full Jar pom looks 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>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-full</artifactId>
    <version>2.4.1</version>
    <packaging>jar</packaging>
    <name>CXF Full Jar</name>
    <properties>
        <project.version>compile</project.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <createDependencyReducedPom>true</createDependencyReducedPom>
                            <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
                            <artifactSet>
                                <includes>
                                    <include>org.apache.cxf:*</include>
                                </includes>
                                <excludes>
                                    <exclude>org.springframework:*</exclude>
                                </excludes>
                            </artifactSet>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.handlers</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.schemas</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/cxf/bus-extensions.txt</resource>
                                </transformer>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.XmlAppendingTransformer">
                                    <resource>META-INF/wsdl.plugin.xml</resource>
                                    <!-- Add this to enable loading of DTDs
<ignoreDtd>false</ignoreDtd>
-->
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>2.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>2.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-ws-security</artifactId>
            <version>2.4.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.bouncycastle</groupId>
                    <artifactId>bcprov-jdk16</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.0.5.RELEASE</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.ws.security</groupId>
            <artifactId>wss4j</artifactId>
            <version>1.6.3</version>
        </dependency>
        <dependency>
            <artifactId>bcprov-jdk16</artifactId>
            <groupId>org.bouncycastle</groupId>
            <version>1.43</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

Once this was complete, I just include the Spring Full, Spring Batch Full, and CXF Full as dependencies that our child poms inherited as a dependency. This helped take about 25+ jars down to 3 jars and helped resolve our initial Classpath 87 Errors.

Share and Enjoy

Spring Default Values – Lessons Learned

Spring uses a class it called PropertyPlaceholderConfigurer in order to provide property values within the Spring Context File. There are ways to add default values incase the property key does not exist. For instance, in the following scenario I have two bean ids and I want to use property substitution to determine which bean to inject into another bean:

Spring Context File

1
2
3
<bean id="addressSearchDao"class="com.foo.AddressSearchDaoImpl"/>
<bean id="addressDaoMock"class="com.foo.MockAddressDaoImpl"/>
<bean id="addressSearchService"class="com.foo.AddressSearchServiceImpl" p:baseDao-ref="addressSearch${addressSearchService.dao:Dao}"/>

Properties File

1
addressSearchService.dao=DaoMock

The reason for me posting this is because I could not find clear documentation for the default values and so I was incorrectly doing the property default values. The separator is very important because you can set it to be any character you want, but by default it is : (colon).

Here are the resources I found including my post that refers to the handling of default property values.

RESOURCES

http://stackoverflow.com/questions/5410017/property-placeholder-for-imports-bean-refs

https://jira.springsource.org/browse/SPR-4785

http://java.dzone.com/articles/using-default-values

http://gordondickens.com/wordpress/2010/12/06/using-default-values-for-properties-in-spring/

Share and Enjoy