blob: aa652ed663d67bd63755f6ba1d5bd580f5b1428e [file]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<section id="middle-tier.applying-best-practices">
<title>Applying best practices to the middle tier</title>
<para>
While the application middle tier now works as required, it does not observe a few Spring-related best practices.
</para>
<section id="middle-tier.applying-best-practices.transactions">
<title>Using transactions</title>
<para>
At the moment, the middle tier does not make any use of transactions. This isn&rsquo;t a problem while the
database access methods are only running single queries, but could lead to problems in the future if the
application is made more complex. Thankfully, adding the use of transactions to the middle tier is simple.
</para>
<para>
Open <literal>module-context.xml</literal> in the <literal>META-INF/spring</literal> folder of
<literal>greenpages.jpa</literal>. Add the following bean definition to create a transaction manager and
associate it with the context&rsquo;s <literal>EntityManager</literal>:
<programlisting language="xml"><![CDATA[ <!--
Transaction manager for a single JPA EntityManagerFactory (alternative to JTA)
-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />]]>
</programlisting>
(Save it, and the <literal>greenpages.jpa</literal> module will be refreshed.)
</para>
<para>
Next, Spring must be told to enable transaction management. In keeping with the use of annotation-based configuration
for the <literal>EntityManager</literal>, annotation-based transaction configuration will also be used. Add the following
to enable AspectJ-powered transaction demarcation for appropriately annotated beans:
<programlisting language="xml"><![CDATA[ <!--
Instruct Spring to perform declarative transaction management
automatically on annotated classes.
-->
<tx:annotation-driven mode="aspectj" />]]>
</programlisting>
</para>
<para>
Save the updated file which will trigger (another) successful refresh of <literal>greenpages.jpa</literal>.
</para>
<para>
Lastly, <literal>JpaDirectory</literal> needs to be annotated so that it is identified as requiring Spring-based
transaction management. Open <literal>JpaDirectory.java</literal> in <literal>greenpages.jpa</literal>.
Annotate the class with
<literal>@Transactional</literal> and add an
import for <literal>org.springframework.transaction.annotation.Transactional</literal>, which Eclipse should suggest:
<programlisting language="java">import org.springframework.transaction.annotation.Transactional;
@Transactional
final class JpaDirectory implements Directory {
</programlisting>
</para>
<para>
Save the updated file triggering another successful refresh: <literal>JpaDirectory</literal> is now
transactional.
</para>
</section>
<section id="middle-tier.applying-best-practices.exception-translation">
<title>Enabling exception translation</title>
<para>
When using JPA, the standard exceptions are somewhat out of keeping with Spring&rsquo;s exception
model. Spring provides support for automatically translating these exceptions into Spring&rsquo;s
<literal>DataAccessException</literal> hierarchy.
</para>
<para>
Open <literal>module-context.xml</literal> for <literal>greenpages.jpa</literal> again and add the
following bean definition to add the exception translator to the application context:
<programlisting language="xml"><![CDATA[ <!--
Post-processor to perform exception translation on @Repository classes
(from native exceptions such as JPA PersistenceExceptions to
Spring&rsquo;s DataAccessException hierarchy).
-->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />]]>
</programlisting>
</para>
<para>
Save the updated file. The translation will only occur on classes that are annotated with
Spring&rsquo;s <literal>@Repository</literal> stereotype annotation.
<literal>JpaDirectory</literal> needs to have this annotation added to it complete the
enabling of the exception translation.
</para>
<para>
Open <literal>JpaDirectory.java</literal> again, annotate the class with
<literal>@Repository</literal> and add an import for
<literal>org.springframework.stereotype.Repository</literal>:
<programlisting language="java">import org.springframework.stereotype.Repository;
@Transactional
@Repository
final class JpaDirectory implements Directory {
</programlisting>
</para>
<para>
Save the updated file.
</para>
<para>
At this point the redeploy of the @greenpages@ application may fail with an error similar to this:
<programlisting><![CDATA[<SPDE0100E> The class with name 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor',
referenced by bean 'org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor#0',
could not be loaded by class loader 'ServerBundleClassLoader: [bundle=greenpages-1-greenpages.jpa_2.3.0]':
…]]>
</programlisting>
which indicates that there is some package (<code>org.springframework.dao.annotation</code>) which is not
available to the <quote><code>BundleClassLoader</code></quote> for bundle <code>greenpages-1-greenpages.jpa_2.3.0</code>.
We should look in the <literal>MANIFEST.MF</literal> file for this bundle, and see that this package is not
imported (in the <literal>Import-Package</literal> header). Since Bundlor generated this file (controlled by the
template file <literal>template.mf</literal>) we should check that the manifest was re-generated on our last change.
</para>
<para>
Open <literal>template.mf</literal> in <literal>greenpages.jpa</literal> and,
in the <emphasis>Overview</emphasis> pane, click on <emphasis>Update MANIFEST.MF</emphasis>
in the <emphasis>Bundle Actions</emphasis> section. The <literal>MANIFEST.MF</literal> file
is updated, and the application is redeployed, this time successfully. It might be worthwhile
checking the option <emphasis>Automatically update MANIFEST.MF in the background</emphasis> on
the <literal>template.mf</literal> <emphasis>Overview</emphasis> pane so that the <literal>MANIFEST.MF
</literal> is kept up to date as the project is changed.
</para>
</section>
<section id="middle-tier.applying-best-practices.versioning-imports">
<title>Versioning imports</title>
<para>
By default, Bundlor generates <literal>Import-Package</literal> entries with no version range
specified. In the absence of a version range, the OSGi default of <quote>any version</quote> is used.
Whilst this is very flexible it&rsquo;s generally a good idea to restrict an import by
specifying a narrower range. This can be achieved by providing Bundlor with some additional
information in the manifest template.
</para>
<para>
Open <literal>template.mf</literal> for <literal>greenpages.jpa</literal> and add the following
<literal>Import-Template</literal> header:
<programlisting>Import-Template: org.springframework.*;version="[3.0,3.1)",
greenpages;version="[2.0,2.1)",
javax.persistence;version="[1.0.0,1.0.0]"
</programlisting>
If there is already an <literal>Import-Template</literal> header in the template, extend it to include the
above package version range specifications.
</para>
<para>
This header tells Bundlor that all imports of <literal>org.springframework</literal> packages
should be in the range <literal>3.0</literal> inclusive to <literal>3.1</literal>
exclusive, that an import of the <literal>greenpages</literal> package should be in the
range <literal>2.0</literal> inclusive to <literal>2.1</literal> exclusive, and that an import of
<literal>javax.persistence</literal> should be at exactly version <literal>1.0.0</literal>.
</para>
<para>
Bundlor has also generated an import for the <literal>javax.sql</literal> package due to
the <literal>greenpages.jpa</literal> module&rsquo;s use of <literal>javax.sql.DataSource</literal>.
This class is provided by the JRE and as such is generally considered to be unversioned, that is it
has the default OSGi version of zero. If version zero is <emphasis>precisely</emphasis> what is required
then add the following to the <literal>Import-Template</literal> header:
<programlisting>,javax.sql;version="[0,0]"
</programlisting>
but if <quote>any</quote> version is acceptable add the following instead:
<programlisting>,javax.sql;version="0"
</programlisting>
Either of these will successfully allow @greenpages@ to deploy and work correctly. The difference
is in the level of flexibility allowed with the external dependency, something which is probably
irrelevant in this case, but with other package sources might be important.
</para>
</section>
<section id="middle-tier.applying-best-practices.congratulations">
<title>Congratulations!</title>
<para>
The @greenpages@ middle tier is now complete and observes some <quote>best practice</quote>
development with Spring and OSGi.
</para>
</section>
</section>