Archive

Archive for October, 2009

Forking navigations based on user roles in JBoss Seam using Seam-Security

October 14th, 2009 Shrihari 1 comment

This particular tip on loading different homepages for the role associated for a specific logged in user for a JSF web application written using JBoss Seam and identity management using Seam-Security. This tip assumes that a single user will be associated with a single role.

Assume we need to develop a course management JSF application which supports users of 2 different roles : teacher and student. This means if a user with teacher role logs in teacher specific homepage should come  and student logging in should display student homepage. The following are steps to achieve this requirement.

1) Change WEB-INF/components.xml to include the seam-security authentication patterns

  <components xmlns="http://jboss.com/products/seam/components"
          ....
          xmlns:security="http://jboss.com/products/seam/security"
          xsi:schemaLocation="http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.0.xsd
                ....
          ">
 ...
 <security:identity authenticate-method="#{authenticator.authenticate}"/>
</components>

2) Define an Authenticator action class with JBoss Seam component name “authenticator” and define an authenticate method inside

@Name("authenticator")
public class AuthenticatorAction
{   @In
    private Identity identity;
    @In(create=true)
    private AuthService service;

    ...
    @SuppressWarnings("deprecated")
    public boolean authenticate()
    {   String username = identity.getUsername();
        String password = identity.getPassword();
        if(username==null || password==null)
           return false;
        //authenticate the user from database
        User user = service.authenticate(username,password);
        // check user validity.
        identity.addRole(user.getRole());
        return true;
    }
}

3) Define the login portal widget in the login-page (say login.xhtml)

<ui:composition xmlns="http://www.w3.org/1999/xhtml">
 .....
   <h:panelGrid columns="2" style="width:27px">
      <h:outputText value="Username: "/>
      <h:inputText id="username" value="#{identity.username}" rows="1" cols="12">
         <f:validateLength minimum="8"  />
      </h:inputText>
      <h:outputText value="Password:" rows="1" cols="12"/>
      <h:inputSecret id="password" value="#{identity.password}" />
   </h:panelGrid>
   <h:commandButton id="login" action="#{identity.login}" value="Login"/>
    ...
 </ui:composition>

4) Wire the action outcomes to set up the navigation paths in WEB-INF/pages.xml

<pages xmlns="http://jboss.com/products/seam/pages"
       ....
       no-conversation-view-id="/index.html">

     <page view-id="/login.xhtml">
        <navigation from-action="#{authenticator.checkLogin}">
           <rule if="#{identity.loggedIn and identity.role=='Teacher'}">
               <redirect view-id="/teacher/homepage.xhtml" />
          </rule>
       </navigation>
       <navigation from-action="#{identity.login}">
           <rule if="#{identity.loggedIn and identity.hasRole('Teacher')}">
              <redirect view-id="/teacher/homepage.xhtml" />
          </rule>
       </navigation>
       <navigation from-action="#{authenticator.checkLogin}">
          <rule if="#{identity.loggedIn and identity.role=='Student'}">
               <redirect view-id="/student/homepage.xhtml" />
          </rule>
       </navigation>
       <navigation from-action="#{identity.login}">
           <rule if="#{identity.loggedIn and identity.hasRole('Student')}">
              <redirect view-id="/student/homepage.xhtml" />
          </rule>
       </navigation>
    </page>
    ....
   <exception>
        <redirect view-id="/index.xhtml">
           <message>Please log in first</message>
        </redirect>
   </exception>
</pages>

You can extend this tip to come out of different other user-role combinations.

Categories: jee-light Tags: ,

Unit testing CXF webservice endpoints using Spring-Test and TestNG

October 9th, 2009 Shrihari 1 comment

The Spring IOC feature of configurational wiring of POJOs to control and achieve network of business logic frees developers of writing any boilerplate code and eventually of bugs. I would like to explain one such facility with regard to establishing an unit testing infrastructure for writing unit test cases for JAXWS specific web service endpoints (CXF) using Spring-test module and TestNG unit testing framework.

Lets take a simple example of a web service endpoint exposing a functionality to query a book price, given the ISBN number, given a maven project. This write-up just concentrates on establishing a web service unit testing framework. Following are sequence of steps one could follow to achieve the required infrastructure setup.

1) Change ${basedir}/pom.xml include adding spring-test dependency, and TestNG dependency, with maven-surefire-plugin configuration (assuming CXF dependencies are already present)

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-test</artifactId>
 <version>2.5.6</version>
 </dependency>
<dependency>
 <groupId>org.testng</groupId>
 <artifactId>testng</artifactId>
 <version>5.9</version>
 <classfier>jdk15</classifier>
 <scope>test</scope>
</dependency>

2) Add a Spring configuration file (service-test.xml) under ${basedir}/src/test/resources. The CXF’s JaxWSProxyFactoryBean can be wired to to create a service proxy. Below is the snippet of the configuration file.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                    http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd"></span></pre>
    <import resource="classpath:META-INF/cxf/cxf.xml" />
     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
     <import resource="classpath:META-INF/cxf/cxf-extension-http.xml" />

     <!-- Required to load the service endpoint uris -->
     <bean id="propConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
         <property name="locations">
            <list>
               <value>classpath:service-test.properties</value>
             </list>
         </property>
     </bean>

     <bean id="proxyFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
         <property name="serviceClass" value="com.schtech.service.endpoint.store.BookPriceService"/>
         <property name="address" value="${bookstore.service.endpoint.url}"/>
         <property name="bus" ref="cxf" />
    </bean>

     <bean id="serviceClient" class="com.schtech.service.endpoint.store.BookPriceService" factory-bean="proxyFactory"
        factory-method="create"/>
</beans>

3) Implement the TestNG test case to test web service. Here three things have to be noted: (1) ContextConfiguration annotation helps us load
the configuration file. (2) The test case extends AbstractTestNGSpringContextTests to load the context configuration and autowire the service
bean. (3) Autowire annotation on the service endpoint interface initializes with proxy factory created service proxy instance.

@ContextConfiguration(locations = {"classpath:service-context.xml"})
public class BookCatalogTest extends AbstractTestNGSpringContextTests
 { @Autowired
   private BookPriceService serviceClient;

   @Test
   public void testBookPrice()
   { String isbn = "978-3-16-148410-0,";
     Double price  = serviceClient.findBookPrice(isbn);
     assertNotNull(price);
   }
}

The infrastructure is ready to be used to test other service endpoints hosted elsewhere. Running mvn test at the ${basedir} prompt would unit test the service endpoints.

Categories: jee-light Tags: , ,

Creating asynchronous service using Spring JMS (ServiceExporter) and ActiveMQ

October 2nd, 2009 Shrihari No comments

Spring MDPs (Message-Driven-Pojos) provide excellent alternatives to MDBs (EJB specification), as we can enable asynchronous request handling capabilities to any tested business logic without defining container constraints or dependency hooks. There are three ways we could implement a Spring MDP:

(1) implementing javax.jms.MessageListener
(2) implementing org.springframework.jms.listener.SessionAwareMessageListener (which is a wrapper around javax.jms.MessageListener with javax.jms.Session) or
(3) any interface with implementation logic which accepts a string argument containing message data

This particular post tries to explore the third option of converting a tested business entity to handle asynchronous requests using Spring JMS (ServiceExporter bean) with ActiveMQ embedded broke, using a JMS Queuer.

1) Let us assume that our business entity interface and implementation are defined as below:

public interface MyBusinessEntity
{      public void doBusinessLogic(String logicParameters) throws MyBusinessException;
}
public class MyBusinessEntityImpl implements MyBusinessEntity
{      public void doBusinessLogic(String logicParameters) throws MyBusinessException
       {       //using logicParameters, perform the business logic
       }
}

2) The Spring application context should be defined to wire the embedded ActiveMQ broker with Spring JMS Service Exporter bean and the business entities as below:

<beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:amq="http://activemq.apache.org/schema/core"
        xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                                  http://activemq.apache.org/schema/core  http://activemq.apache.org/schema/core/activemq-core-5.2.0.xsd">

     <!-- Defines the ActiveMQ embedded  broker -->
    <amq:broker useJmx="false" persistent="false">
        <amq:transportConnectors>
                 <amq:transportConnector uri="tcp://localhost:0" />
        </amq:transportConnectors>
      </amq:broker>

     <amq:queue id="qDestination" physicalName="embedded" />

    <amq:connectionFactory id="connectionFactory" brokerURL="vm://localhost" />

   <bean id="mybusinessService" class="org.springframework.jms.remoting.JmsInvokerServiceExporter">
     <property name="serviceInterface" value="mypackage.MyBusinessEntity"/>
     <property name="service">
            <bean class="mypackage.MyBusinessEntityImpl"/>
     </property>
   </bean>

   <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer">
       <property name="connectionFactory" ref="connectionFactory"/>
       <property name="destination" ref="qDestination"/>
       <property name="messageListener" ref="placeBidService"/>
   </bean>

</beans>

3) If the application is a web-archive (war), add the Spring ContextLoader Listener to load the spring Context to your WEB-INF/web.xml.

<listener>
     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
   <context-param>
          <param-name>contextConfigLocation</param-name>
         <param-value>classpath:jms-context.xml</param-value>
   </context-param>

4) In order to asynchronously place a request, from the client, you need to define another client Spring context configuration mapping containing the reference to MyBusinessEntrity interface as given below, which could be used directly to place a request.

<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:amq="http://activemq.apache.org/schema/core"
      xsi:schemaLocation="http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                       http://activemq.apache.org/schema/core  http://activemq.apache.org/schema/core/activemq-core-5.2.0.xsd">

 <bean id="mybusinessService" class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean">
      <property name="serviceInterface" value="mypackage.MyBusinessEntity"/>
      <property name="connectionFactory" ref="connectionFactory"/>
      <property name="queue" ref="qDestination"/>
 </bean>

 <amq:queue id="qDestination" physicalName="embedded" />
 <amq:connectionFactory id="connectionFactory" brokerURL="vm://localhost" />
</beans>

Having setup the server and client logics, the client can asynchronously lodge a request on server for the specific business logic.

Categories: jee-light Tags: ,