| <?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’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’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’s exception |
| model. Spring provides support for automatically translating these exceptions into Spring’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’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’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’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’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> |