6 Replies Latest reply on Jan 28, 2003 10:54 AM by Elmar Henne

    PetStore 1.3.1 Admin Client

    Elmar Henne Newbie

      This topic is a successor to "Running PetStore 1.3 on JBoss 3.0" (http://www.jboss.org/modules/bb/index.html?module=bb&op=viewtopic&t=forums/ where I explained how to run the core of the Petstore example. Here I want to explain the changes needed to run the rest of Petstore.


      1. PetStore deployment structure

      Before building the other Petstore applications we have to take a look at the overall structure of Petstore. The following gives an overview of the deployment structure. I have only listed the packages and enterprise beans, elements with multiple occurrences are marked bold and their content is only listed once:

      petstore.ear
      ....asyncsender-ejb.jar
      ........AsyncSenderEJB
      ....asyncsender-ejb-client.jar
      ....cart-ejb.jar
      ....cart-ejb-client.jar
      ....catalog-ejb.jar
      ....catalog-ejb-client.jar
      ....customer-ejb.jar
      ........AccountEJB
      ........AddressEJB
      ........ContactInfoEJB
      ........CreditCardEJB
      ........CustomerEJB
      ........ProfileEJB
      ....customer-ejb-client.jar
      ....petstore.war
      ....petstore-ejb.jar
      ....po-ejb-client.jar
      ....servicelocator.jar
      ....signon-ejb.jar
      ....signon-ejb-client.jar
      ....tracer.jar
      ....uidgen-ejb.jar
      ....uidgen-ejb-client.jar
      ....xmldocuments.jar

      opc.ear
      ....mailer-ejb.jar
      ........MailerMDB
      ....mailer-ejb-client.jar
      ........MailerMDB
      ....opc.war
      ....opc-ejb.jar
      ........OPCAdminFacadeEJB
      ........InvoiceMDB
      ........MailCompletedOrderMDB
      ........MailInvoiceMDB
      ........MailOrderApprovalMDB
      ........OrderApprovalMDB
      ....po-ejb.jar
      ........AddressEJB
      ........CreditCardEJB
      ........ContactInfoEJB
      ........LineItemEJB
      ........PurchaseOrderEJB
      ....po-ejb-client.jar
      ....processmanager-ejb.jar
      ........ProcessManagerEJB
      ........ManagerEJB
      ....processmanager-ejb-client.jar
      ........ManagerEJB
      ....servicelocator.jar
      ....xmldocuments.jar

      petstoreadmin.ear
      ....asyncsender-ejb.jar
      ....asyncsender-ejb-client.jar
      ....opc-ejb.jar
      ....opc-ejb-client.jar
      ........OPCAdminFacadeEJB
      ........MailCompletedOrderMDB
      ........MailInvoiceMDB
      ........MailOrderApprovalMDB
      ....petstoreadmin.war
      ........AdminApp.jar
      ........crimson.jar
      ........jaxp.jar
      ........asyncsender-ejb-client.jar
      ........opc-client-ejb.jar
      ........servicelocator.jar
      ........xmldocuments.jar
      ....xmldocuments.jar

      supplier.ear
      ....processmanager-ejb.jar
      ....processmanager-ejb-client.jar
      ....servicelocator.jar
      ....supplier.war
      ........supplierpo-ejb-client.jar
      ........processmanager-ejb-client.jar
      ........servicelocator.jar
      ........xmldocuments.jar
      ........InventoryEJB
      ........SupplierOrderMDB
      ....supplier-ejb.jar
      ........OrderFulfillmentFacadeEJB
      ........InventoryEJB
      ........SupplierOrderMDB
      ....supplierpo-ejb.jar
      ........AddressEJB
      ........ContactInfoEJB
      ........LineItemEJB
      ........SupplierOrderEJB
      ....supplierpo-ejb-client.jar
      ....xmldocuments.jar


      Now let us build and deploy opc.ear.


      2. Naming conflicts

      From the structure you can see that most of the EJBs in po-ejb.jar are also used in other applications. So we have to give them unique JNDI names in src\components\purchaseorder\src\jboss.xml. Because we use the local interfaces their JNDI names have to be defined via <local-jndi-name>. I have used the original names from src\apps\opc\src\sun-j2ee-ri.xml:


      <enterprise-beans>

      <ejb-name>AddressEJB</ejb-name>
      <local-jndi-name>ejb/opc/po/Address</local-jndi-name>


      <ejb-name>ContactInfoEJB</ejb-name>
      <local-jndi-name>ejb/opc/po/ContactInfo</local-jndi-name>


      <ejb-name>LineItemEJB</ejb-name>
      <local-jndi-name>ejb/opc/po/LineItem</local-jndi-name>


      <ejb-name>CreditCardEJB</ejb-name>
      <local-jndi-name>ejb/opc/po/CreditCard</local-jndi-name>

      </enterprise-beans>


      To include jboss.xml in the build process we need also an additional entry in the "ejbjar" target of src\components\purchaseorder\src\build.xml:




      3. Queues

      The message queues for opc have to be defined in src\apps\opc\src\jboss.xml. Here is the start of it together with the first MDB declarations:


      <enterprise-beans>

      <ejb-name>OPCAdminFacadeEJB</ejb-name>
      <jndi-name>ejb/opc/opc/OPCAdminFacadeEJBRemote</jndi-name>


      <message-driven>
      <ejb-name>PurchaseOrderMDB</ejb-name>
      <!--jndi-name>jms/opc/OrderQueue</jndi-name-->
      <destination-jndi-name>queue/A</destination-jndi-name>
      <resource-ref>
      <res-ref-name>jms/QueueConnectionFactory</res-ref-name>
      <jndi-name>ConnectionFactory</jndi-name>
      </resource-ref>
      <resource-ref>
      <res-ref-name>url/EntityCatalogURL</res-ref-name>
      <res-url>http://localhost:8080/opc/EntityCatalog.jsp</res-url>
      </resource-ref>
      <resource-env-ref>
      <resource-env-ref-name>jms/OrderApprovalQueue</resource-env-ref-name>
      <!--jndi-name>jms/opc/OrderApprovalQueue</jndi-name-->
      <jndi-name>queue/B</jndi-name>
      </resource-env-ref>
      </message-driven>
      ...


      As you can see I am reusing the existing queues (queue/A, queue/B, etc.) but you could also define new ones inside jbossmq-destinations-service.xml. You have only to map the message sender (from <resource-env-ref>) to its MDB receiver (in <destination-jndi-name>). The above jboss declarations contain additional entries for QueueConnectionFactory and for an URL reference.

      We need similar declarations for the other MDBs, look in src\apps\opc\src\sun-j2ee-ri.xml for the corresponding sun declarations.

      Again we need the entry for jboss in src\apps\opc\src\build.xml:




      4. Problem with createQueueSession

      To let the queues work we have to make a small change in the source code. The value of the first parameter of
      QueueConnection.createQueueSession (boolean transacted, int acknowledgeMode)
      should be ignored according to EJB 2.0 but setting it to true will in Jboss just silently ignore all sent messages. So we have to change its value to false in the files ...\AsyncSenderEJB.java and ...\opc\transitions\QueueHelper.java

      AsyncSenderEJB is in petstore.ear, so you have to rebuild it.

        • 1. Re: PetStore 1.3.1 Admin Client
          Elmar Henne Newbie

          To run opc.ear we need to solve two more problems:

          5. Primary keys in opc

          First we have a new unknown primary key in LineItemEJB and we have also to define our primary keys for the reused beans AddressEJB, ContactInfoEJB and CreditCardEJB in src\components\purchaseorder\src\ejb-jar.xml.

          The simple method to generate the key values as described in the last topic under step 5. works no longer very well, so I will later show a better way to do this. But for the moment you can stay with the old method. To avoid duplicate keys you might sometimes need to destroy all tables before restarting Jboss server. To do this you could just delete and recreate your Oracle user.


          6. Unidirectional relationships

          PetStore uses mostly unidirectional relationships but when we look at the generated database tables we will see foreign keys on both sides.

          Let us take a closer look at ContactInfoEJB:
          It has a relation with AccountEJB where AccountEJB has a cmr field contactInfo, implemented as the foreign key column contactInfo, pointing to ContactInfoEJB. We will see also a column AccountEJB_contactInfo in ContactInfoEJB pointing backward to AccountEJB, but without a corresponding cmr field in the bean. Up to now this was no big problem but with opc.ear ContactInfoEJB gets another relationship with PurchaseOrderEJB and this introduces a second unwanted foreign key PurchaseOrderEJB_contactInfo. Since in petstore.ear (specifically in src\components\customer\src\ejb-jar.xml) we have only a declaration for the first relationship and in opc.ear (src\components\purchaseorder\src\ejb-jar.xml) only for the second one, the CMP generated table will always have only one of the two unwanted foreign keys depending on the deployment order of the ear files.

          So we have to get rid of those unnecessary backward foreign keys. To do this we need files jbosscmp-jdbc.xml in src\components\customer\src\ and src\components\purchaseorder\src\. Here is the entry for the relation between ContactInfoEJB and AccountEJB:

          <jbosscmp-jdbc>

          <ejb-relation>
          <ejb-relation-name>RelAccountEJBContactInfoEJB</ejb-relation-name>
          <foreign-key-mapping/>
          <ejb-relationship-role>
          <ejb-relationship-role-name>AccountEJB</ejb-relationship-role-name>
          <key-fields/>
          </ejb-relationship-role>
          <ejb-relationship-role>
          <ejb-relationship-role-name>ContactInfoEJB</ejb-relationship-role-name>
          <key-fields>
          <key-field>
          <field-name>id</field-name>
          </key-field>
          </key-fields>
          </ejb-relationship-role>
          </ejb-relation>
          ...


          The important part is <key-fields/> under AccountEJB which tells CMP not to generate a foreign key on the other side ContactInfoEJB. As you see the empty key declaration must be on the primary key ("destination") side not on the foreign key ("pointing") side.

          We need such entries for following relationships:

          RelCustomerEJBAccountEJB, RelContactInfoEJBAddressEJB, RelCustomerEJBProfileEJB, RelAccountEJBContactInfoEJB and RelAccountEJBCreditCardEJB in src\components\customer\src\jbosscmp-jdbc.xml

          PurchaseOrder-ContactInfo, RelContactInfoEJBAddressEJB and PurchaseOrder-Card in src\components\purchaseorder\src\jbosscmp-jdbc.xml

          The relation names and role names must match the corresponding declarations from ejb-jar.xml.

          To include jbosscmp-jdbc.xml in the build process we need additional entries in the "ejbjar" target of the build.xml files:



          and




          7. XML parser problem

          When placing a PetStore order with opc.ear deployed I got an internal java error inside the crimson XML parser. It disappeared only after I had installed the latest version of crimson. You can find it under http://xml.apache.org/dist/crimson/crimson-1.1.3-bin.zip. To install it you must replace crimson.jar in your <jboss-home>/lib directory with the corresponding file from the above zip file. I hope this has no negative effects on other jboss parts.

          • 2. Re: PetStore 1.3.1 Admin Client
            Elmar Henne Newbie

            8. Persistent primary key generator

            Now I will present a better method to generate the primary key values. This is also a good exercise on how to add a new component to PetStore.

            The new key generator is basically a single global method in the file src\components\pkgen\src\com\sun\j2ee\blueprints\pkgen\PKGen.java. The idea behind this is to store the last used primary key value in a map for every table. At the first access to a table the start value is retrieved from the database by executing "SELECT MAX(ID) FROM ". Later on we have only to increment this value for every new insert. This will work as long as the tables are accessed only from a single server, which is certainly true for PetStore which depends heavily on local interfaces.

            Here is the new package, to preserve the indentation I have replaced all leading spaces by "_". When copying the text you can just replace all underlines with the empty string, "_" is not used elsewhere in the file:

            package com.sun.j2ee.blueprints.pkgen;

            import java.util.Collections;
            import java.util.Map;
            import java.util.HashMap;
            import java.sql.Connection;
            import java.sql.ResultSet;
            import java.sql.PreparedStatement;
            import javax.sql.DataSource;
            import javax.naming.InitialContext;
            import javax.naming.Context;

            /**
            _* This class has just a single static method for creating unique
            _* primary key values for the given database table
            _*/
            public final class PKGen {

            ____private static final String dsName = "java:/OracleDS";
            ____private static DataSource ds = null;
            ____/**
            _____* The last counter values for the tables are cashed in this map
            _____*/
            ____private static Map map = null;

            ____private PKGen() {}

            ____/**
            _____* This method returns the next available number for the
            _____* the primary key ID of the given table.
            _____* The table is accessed via datasource dsName.
            _____* @param String tableName name of the database table
            _____* @return Integer next value for primary key
            _____*/
            ____public static Integer getNextKeyValue (String tableName) {
            ______Integer returnValue = null;

            ______if (map == null) {
            ________// Create map for synchronized access
            ________map = Collections.synchronizedMap (new HashMap(20));
            ______}

            ______// Get last counter value from map
            ______returnValue = (Integer) map.get (tableName);

            ______if (returnValue == null) {
            ________// Table is accessed for the first time
            ________PreparedStatement stmt = null;
            ________Connection con = null;
            ________ResultSet rs = null;

            ________if (ds == null) {
            __________// Get datasource
            __________try {
            ____________Context context = new InitialContext();
            ____________ds = (DataSource) context.lookup (dsName);
            __________} catch (Exception e) {
            ____________System.err.println ("Error in PKGen.getNextKeyValue (): " + e.getMessage());
            ____________e.printStackTrace ();
            ____________return null;
            __________}
            ________}

            ________// Get the maximal used PK from the table
            ________try {
            ____________con = ds.getConnection();
            ____________stmt = con.prepareStatement ("SELECT MAX(ID) FROM " + tableName);
            ____________rs = stmt.executeQuery();

            ____________if (rs.next()) {
            ________________returnValue = new Integer (rs.getInt (1));
            ____________} else {
            ________________// table is empty, start with 1 as PK
            ________________returnValue = new Integer (0);
            ____________}
            ________} catch (Exception e) {
            ____________System.err.println ("Error in PKGen.getNextKeyValue (): " + e.getMessage());
            ____________e.printStackTrace ();
            ____________return null;
            ________} finally {
            ____________try {
            ________________rs.close();
            ____________} catch (Exception ignored) { }
            ____________try {
            ________________stmt.close();
            ____________} catch (Exception ignored) { }
            ____________try {
            ________________con.close();
            ____________} catch (Exception ignored) { }
            ________}
            ______}

            ______// Increment the counter and store it in our map
            ______returnValue = new Integer (returnValue.intValue() + 1);
            ______map.put (tableName, returnValue);
            ______// Some test output
            ______System.out.println ("PKGen.getNextKeyValue (" + tableName + ") = " + returnValue);
            ______return returnValue;
            __}

            }


            In the calling entity beans AddressEJB, ContactInfoEJB, CreditCardEJB and LineItemEJB we have only to add the import
            import com.sun.j2ee.blueprints.pkgen.PKGen;

            and to change the setId calls in ejbCreate to:
            setId (PKGen.getNextKeyValue ("AddressEJB"));

            The parameter must match the according table name which is identical to the class name as long as it is not changed via jbosscmp-jdbc.xml.


            To build the new PKGen component with ant we need src\components\pkgen\src\build.xml. This just is a copy of src\components\util\tracer\src\build.xml with following changes:
            - Replace the string "tracer" by "pkgen"
            - Add the following as first line in target "ejbjar" (this is an omission in the original, too):


            But we have also to modify the build.xml files of the depending components. In their "init" targets we include the following property definitions:

            <!-- PKGen Component -->
            <property name="contactinfo.pkgen.home"
            value="${contactinfo.components.basedir}/pkgen"/>
            <property name="contactinfo.pkgen.client"
            value="${contactinfo.pkgen.home}/build/pkgen.jar"/>

            The classpath property must be extended to include pkgen.jar:

            <property name="contactinfo.classpath"
            value="${contactinfo.classbindir}:${contactinfo.pkgen.client} ... "/>

            The bold names must be replaced by the respective component name and this has to be done for the following components: address, contactinfo, creditcard and lineitem

            The last thing to do is to include a call to ant for PKGen in src\apps\petstore\src\build.xml, src\apps\opc\src\build.xml and src\components\src\build.xml.

            First we define again a property in the "init" targets:

            <!-- PKGen Component -->
            <property name="petstore.pkgen.home"
            value="${petstore.components.basedir}/pkgen"/>

            The ant calls go into the "components" targets:

            <ant dir="${petstore.pkgen.home}/src" target="core"/>

            and into the "clean_all" targets:

            <ant dir="${petstore.pkgen.home}/src" target="clean"/>

            To make it perfect we should also include respective pathelements in the "docs" targets and this should also be applied to the master build file src\build.xml.

            • 3. Re: PetStore 1.3.1 Admin Client
              Elmar Henne Newbie

              9. petstoreadmin.ear

              Now we want to deploy petstoreamdin.ear to be able to use the administrator client.

              In file src\apps\admin\src\docroot\WEB-INF\jboss-web.xml we have to declare the JNDI name of OPCAdminFacadeRemote from src\apps\opc\src\jboss.xml:

              [pre]<jboss-web>
              <ejb-ref>
              <ejb-ref-name>ejb/OPCAdminFacadeRemote</ejb-ref-name>
              <jndi-name>ejb/opc/opc/OPCAdminFacadeEJBRemote</jndi-name>
              </ejb-ref>
              </jboss-web>
              [/pre]There is a problem with the asyncsender component which is both used in petstore.ear and petstoreamdin.ear but with different deployment information. With Jboss the deployment information has to be inside asyncsender.jar, so we need a second jar file for admin.

              We create src\components\asyncsender\src\jboss-admin.xml which looks like src\components\asyncsender\src\jboss.xml but with another queue name:

              [pre]
              <enterprise-beans>

              <ejb-name>AsyncSenderEJB</ejb-name>
              <local-jndi-name>ejb/admin/asyncsender/AsyncSender</local-jndi-name>

              <!--Queues for petstoreadmin.ear -->
              <resource-ref>
              <res-ref-name>jms/QueueConnectionFactory</res-ref-name>
              <jndi-name>ConnectionFactory</jndi-name>
              </resource-ref>
              <resource-env-ref>
              <resource-env-ref-name>jms/AsyncSenderQueue</resource-env-ref-name>
              <!--jndi-name>jms/opc/OrderApprovalQueue</jndi-name-->
              <jndi-name>queue/B</jndi-name>
              </resource-env-ref>

              </enterprise-beans>

              [/pre]In src\components\asyncsender\src\build.xml we need a new property in the "init" target:

              [pre]
              [/pre]The following lines at the end of the "ejbjar" target will create our new jar file asyncsender-admin-ejb.jar:

              [pre]




              [/pre]In both src\apps\admin\src\build.xml and src\apps\admin\src\application.xml we have to replace the references to "asyncsender-ejb.jar" by our new "asyncsender-admin-ejb.jar".

              Now we are able to build and deploy petstoreamdin.ear and can use Petstore's administrator client (the link from the start page) to see our placed orders. Orders above $ 500.00 must be approved from this client. But the order processing is not yet complete, the last application supplier.ear is still missing.

              P.S.: If you are wondering about the above code formatting, it can be done with the tag &#91pre]...&#91/pre], see http://www.jboss.org/modules/bb/index.html?module=bb&op=viewtopic&t=forums/ for more explanation.

              • 4. Re: PetStore 1.3.1 Admin Client
                Elmar Henne Newbie

                10. supplier.ear

                Now let us deploy the last application supplier.ear. Most of the following changes should look familiar to you.

                We need src\apps\supplier\src\jboss.xml with the following declarations:
                [pre]
                <enterprise-beans>

                <ejb-name>OrderFulfillmentFacadeEJB</ejb-name>
                <local-jndi-name>ejb/supplier/OrderFulfillment/OrderFulfillmentFacade
                </local-jndi-name>
                <resource-ref>
                <res-ref-name>url/EntityCatalogURL</res-ref-name>
                <res-url>http://localhost:8080/opc/EntityCatalog.jsp</res-url>
                </resource-ref>


                <message-driven>
                <ejb-name>SupplierOrderMDB</ejb-name>
                <!--jndi-name>jms/supplier/PurchaseOrderQueue</jndi-name-->
                <destination-jndi-name>queue/ex</destination-jndi-name>
                <resource-env-ref>
                <resource-env-ref-name>jms/opc/InvoiceTopic</resource-env-ref-name>
                <!--jndi-name>jms/opc/InvoiceTopic</jndi-name-->
                <jndi-name>topic/testTopic</jndi-name>
                </resource-env-ref>
                <resource-ref>
                <res-ref-name>jms/QueueConnectionFactory</res-ref-name>
                <!--jndi-name>jms/supplier/QueueConnectionFactory</jndi-name-->
                <jndi-name>ConnectionFactory</jndi-name>
                </resource-ref>
                <resource-ref>
                <res-ref-name>jms/TopicConnectionFactory</res-ref-name>
                <!--jndi-name>jms/supplier/TopicConnectionFactory</jndi-name-->
                <jndi-name>ConnectionFactory</jndi-name>
                </resource-ref>
                </message-driven>
                </enterprise-beans>

                [/pre]For this we need an entry in the "ejbjar" target of src\apps\supplier\src\build.xml:
                [pre]
                [/pre]
                For the web client we need src\apps\supplier\src\docroot\WEB-IF\jboss-web.xml:
                [pre]<jboss-web>
                <resource-ref>
                <res-ref-name>jms/TopicConnectionFactory</res-ref-name>
                <!--jndi-name>jms/supplier/TopicConnectionFactory</jndi-name-->
                <jndi-name>ConnectionFactory</jndi-name>
                </resource-ref>
                <resource-env-ref>
                <resource-env-ref-name>jms/opc/InvoiceTopic</resource-env-ref-name>
                <!--jndi-name>jms/opc/InvoiceTopic</jndi-name-->
                <jndi-name>topic/testTopic</jndi-name>
                </resource-env-ref>
                </jboss-web>
                [/pre]
                11. supplierpo

                To avoid naming conflicts we also need src\components\supplierpo\src\jboss.xml:
                [pre]
                <enterprise-beans>

                <ejb-name>SupplierOrderEJB</ejb-name>
                <local-jndi-name>ejb/supplier/supplierpo/SupplierOrder</local-jndi-name>


                <ejb-name>AddressEJB</ejb-name>
                <local-jndi-name>ejb/supplier/supplierpo/Address</local-jndi-name>


                <ejb-name>ContactInfoEJB</ejb-name>
                <local-jndi-name>ejb/supplier/supplierpo/ContactInfo</local-jndi-name>


                <ejb-name>LineItemEJB</ejb-name>
                <local-jndi-name>ejb/supplier/supplierpo/LineItem</local-jndi-name>

                </enterprise-beans>

                [/pre]For the unidirectional relationships we need src\components\supplierpo\src\jbosscmp-jdbc.xml:
                [pre]<jbosscmp-jdbc>

                <ejb-relation>
                <ejb-relation-name>SupplierOrder-ContactInfo</ejb-relation-name>
                <foreign-key-mapping/>
                <ejb-relationship-role>
                <ejb-relationship-role-name>purchaseorder-has-contactinfo
                </ejb-relationship-role-name>
                <key-fields/>
                </ejb-relationship-role>
                <ejb-relationship-role>
                <ejb-relationship-role-name>contactinfo-for-purchaseorder
                </ejb-relationship-role-name>
                <key-fields>
                <key-field>
                <field-name>id</field-name>
                </key-field>
                </key-fields>
                </ejb-relationship-role>
                </ejb-relation>

                <ejb-relation>
                <ejb-relation-name>ContactInfo-Address</ejb-relation-name>
                <foreign-key-mapping/>
                <ejb-relationship-role>
                <ejb-relationship-role-name>contactinfo-has-address
                </ejb-relationship-role-name>
                <key-fields/>
                </ejb-relationship-role>
                <ejb-relationship-role>
                <ejb-relationship-role-name>address-for-contactinfo
                </ejb-relationship-role-name>
                <key-fields>
                <key-field>
                <field-name>id</field-name>
                </key-field>
                </key-fields>
                </ejb-relationship-role>
                </ejb-relation>

                <ejb-relation>
                <ejb-relation-name>SupplierOrder-LineItem</ejb-relation-name>
                <foreign-key-mapping/>
                <ejb-relationship-role>
                <ejb-relationship-role-name>purchaseorder-has-lineitems
                </ejb-relationship-role-name>
                <key-fields>
                <key-field>
                <field-name>poId</field-name>
                <column-name>poId</column-name>
                </key-field>
                </key-fields>
                </ejb-relationship-role>
                <ejb-relationship-role>
                <ejb-relationship-role-name>lineItem-for-purchaseorder
                </ejb-relationship-role-name>
                <key-fields/>
                </ejb-relationship-role>
                </ejb-relation>

                </jbosscmp-jdbc>
                [/pre]You have also to correct some of the relationship-names and relationship-role-names in src\components\supplierpo\src\ejb-jar.xml to match the above names shown in bold.

                [/pre]Again we need entries in the "ejbjar" target of src\components\supplierpo\src\build.xml:
                [pre]

                [/pre]
                12. Relationships with LineItem

                Above I have included an entry for the one-to-many relationship SupplierOrder-LineItem. This is necessary to explicitly name the foreign key in lineItemEJB (<column-name>poId</column-name>). The same column we will also use for the other one-to-many relationship PurchaseOrder-LineItem by adding this to src\components\purchaseorder\src\jbosscmp-jdbc.xml:
                [pre] <ejb-relation>
                <ejb-relation-name>PurchaseOrder-LineItem</ejb-relation-name>
                <foreign-key-mapping/>
                <ejb-relationship-role>
                <ejb-relationship-role-name>purchaseorder-has-lineitems
                </ejb-relationship-role-name>
                <key-fields>
                <key-field>
                <field-name>poId</field-name>
                <column-name>poId</column-name>
                </key-field>
                </key-fields>
                </ejb-relationship-role>
                <ejb-relationship-role>
                <ejb-relationship-role-name>lineItem-for-purchaseorder
                </ejb-relationship-role-name>
                <key-fields/>
                </ejb-relationship-role>
                </ejb-relation>
                [/pre]Without this CMP in jboss would create two different foreign key columns (with generated names) and we would end up with conflicting table definitions for LineItemEJB. Reusing the same column for two different relations is here no problem but if you do not like this you would have to declare both SupplierOrder-LineItem and PurchaseOrder-LineItem in purchaseorder (for opc.ear) as well as in supplierpo (for supplier.ear) with two different column names.

                For the change in purchaseorder we have to rebuild its application opc.ear.


                Now with supplier running we can remove our temporary change from step 9 ("Using Pet Store core only") of the first topic in file src\apps\petstore\src\docroot\populating.jsp: Restore "/petstore/main.screen" to "/supplier/populating" and rebuild petstore.ear.

                • 5. Re: PetStore 1.3.1 Admin Client
                  Elmar Henne Newbie

                  13. Email notifications

                  PetStore is able to send email confirmations to the customer. To enable this we have to set the parameters param/SendConfirmationMail, param/SendApprovalMail and param/SendCompletedOrderMail in src\apps\opc\src\ejb-jar.xml to true.

                  To define the mail provider and the mailer queue we need src\components\mailer\src\jboss.xml:
                  [pre]
                  <enterprise-beans>
                  <message-driven>
                  <ejb-name>MailerMDB</ejb-name>
                  <!--jndi-name>jms/opc/MailQueue</jndi-name-->
                  <destination-jndi-name>queue/testQueue</destination-jndi-name>
                  <resource-ref>
                  <res-ref-name>mail/MailSession</res-ref-name>
                  <jndi-name>java:/Mail</jndi-name>
                  </resource-ref>
                  </message-driven>
                  </enterprise-beans>

                  [/pre]For this file we need an entry in the "ejbjar" target of src\components\mailer\src\build.xml:
                  [pre]
                  [/pre]
                  The configuration of the email service is done in <jboss-home>\server\default\deploy\Mail-service.xml. Here you have to provide your own values, especially the name of your SMTP server.


                  That is it! After rebuilding and deploying opc.ear you should now be able to receive emails for your PetStore orders.

                  I hope this whole exercise enabled you not only to run PetStore on Jboss but did provide you also with a better understanding of EJB and its implementation in Jboss.

                  • 6. Re: PetStore 1.3.1 Admin Client
                    Elmar Henne Newbie

                    14. Building PKGen

                    After trying a clean rebuild I noticed that the building instructions for PKGen under step 8. are incomplete. PKGen will be built bot not included on the server. I missed this because I still had an older version of PKGen.class residing in src\components\customer\build\classes.

                    To include PKGen.jar in the ear we need in the "init" target of src\apps\petstore\src\build.xml:
                    [pre] <!-- PKGen Component -->


                    [/pre]In the "ear" target we need:
                    [pre]
                    [/pre]We need pkgen also in the classpath for customer as described for contactinfo under step 9.

                    To link pkgen we need finally a reference to it in Class-Path of the manifest, e.g. in src\components\customer\src\manifest.mf:
                    [pre]Manifest-Version: 1.0
                    Class-Path: xmldocuments.jar servicelocator.jar pkgen.jar
                    [/pre]This should be also included in the manifest files of address, contactinfo, creditcard and lineitem.


                    Let me also repeat the source code of PKGen with the nicer formatting:

                    [pre]package com.sun.j2ee.blueprints.pkgen;

                    import java.util.Collections;
                    import java.util.Map;
                    import java.util.HashMap;
                    import java.sql.Connection;
                    import java.sql.ResultSet;
                    import java.sql.PreparedStatement;
                    import javax.sql.DataSource;
                    import javax.naming.InitialContext;
                    import javax.naming.Context;


                    /**
                    * This class has just a single static method for creating unique
                    * primary key values for the given database table
                    */
                    public final class PKGen {

                    private static final String dsName = "java:/OracleDS";
                    private static DataSource ds = null;
                    /**
                    * The last counter values for the tables are cashed in this map
                    */
                    private static Map map = null;

                    private PKGen() {}

                    /**
                    * This method returns the next available number for the
                    * the primary key ID of the given table.
                    * The table is accessed via datasource dsName.
                    * @param String tableName name of the database table
                    * @return Integer next value for primary key
                    */
                    public static Integer getNextKeyValue (String tableName) {
                    Integer returnValue = null;

                    if (map == null) {
                    // Create map for synchronized access
                    map = Collections.synchronizedMap (new HashMap(20));
                    }

                    // Get last counter value from map
                    returnValue = (Integer) map.get (tableName);

                    if (returnValue == null) {
                    // Table is accessed for the first time
                    PreparedStatement stmt = null;
                    Connection con = null;
                    ResultSet rs = null;

                    if (ds == null) {
                    // Get datasource
                    try {
                    Context context = new InitialContext();
                    ds = (DataSource) context.lookup (dsName);
                    } catch (Exception e) {
                    System.err.println ("Error in PKGen.getNextKeyValue(): "
                    + e.getMessage());
                    e.printStackTrace ();
                    return null;
                    }
                    }

                    // Get the maximal used PK from the table
                    try {
                    con = ds.getConnection();
                    stmt = con.prepareStatement ("SELECT MAX(ID) FROM "
                    + tableName);
                    rs = stmt.executeQuery();

                    if (rs.next()) {
                    returnValue = new Integer (rs.getInt (1));
                    } else {
                    // table is empty, start with 1 as PK
                    returnValue = new Integer (0);
                    }
                    } catch (Exception e) {
                    System.err.println ("Error in PKGen.getNextKeyValue(): "
                    + e.getMessage());
                    e.printStackTrace ();
                    return null;
                    } finally {
                    try {
                    rs.close();
                    } catch (Exception ignored) { }
                    try {
                    stmt.close();
                    } catch (Exception ignored) { }
                    try {
                    con.close();
                    } catch (Exception ignored) { }
                    }
                    }

                    // Increment the counter and store it in our map
                    returnValue = new Integer (returnValue.intValue() + 1);
                    map.put (tableName, returnValue);
                    // Some test output
                    System.out.println ("PKGen.getNextKeyValue (" + tableName + ") = "
                    + returnValue);
                    return returnValue;
                    }

                    }