Saturday, 26 February 2011

Play! Framework deploy to cloudbees.com

I've been trying the Play framework in the last few weeks and I've to admit its a real pleasure to code in that environment.
Now, I want to share my experience with deploying a Play! application to cloudbees.com
These guys offer the platform as a service, you can build, test and deploy Java web applications in the cloud. I thought it was brilliant.
Register, for free (DEV tools seems to be free only till the end of Feb 2011) and you get a nice control panel from where you can create git/svn repositories, Jenkins (hudson) jobs, deploy you application create databases etc.

Here are some of the steps you need in order to get your Play! application running there.

I'm using play 1.1.1

  • Edit your play application.conf

    # The production deployment information
    %production.db=java:/comp/env/jdbc/${project_name}
    
    %production.db.url=jdbc:cloudbees://${db_name}
    %production.db.driver=com.cloudbees.jdbc.Driver
    %production.db.user=${db_username}
    %production.db.pass=${db_password}
    %production.jpa.dialect=org.hibernate.dialect.MySQLDialect
    

    What I didn't find on the Play! documentation was the first line to specify the JNDI entry
  • Create the database
    from the website you can easily create a database



  • Create the cloudbees resource file
    Create a folder war/WEB-INF in your Play! project ( if you don't have it ) and create a file called cloudbees-web.xml that looks like this
    <br /><?xml version="1.0"?><br /><br /><cloudbees-web-app xmlns="http://www.cloudbees.com/xml/webapp/1"><br /> <resource name="jdbc/${project_name}" auth="Container" type="javax.sql.DataSource"><br />  <param name="username" value="${db_username}" /><br />  <param name="password" value="${db_password}" /><br />  <param name="url" value="jdbc:cloudbees://${db_name}" /><br /> </resource><br /></cloudbees-web-app><br /><br />


  • Generate your database DDL
    Add the module db to your application
    module.db=${play.path}/modules/db-1.1.1


    and then run the command

    play db:export --%production


    You might get an javax.naming.NoInitialContextException, if you do comment out the line

    #%production.db=java:/comp/env/jdbc/${project_name}

    in the application.conf

    You might also get some sort of command not found error. Make sure you install db-1.1.1. I think if you don't specify the version it'll get version 1.0.1

  • Create your remote db
    This took me some time to figure out. I thought there was some sort of phpMyAdmin but instead you need to use your own MySql client. So on the cloudbees website if you look under
    RUN@Cloud > Databases >Manage >${your_db} in the Overview section you can find server name, port, schema and username.
    Open your client ( I've used MySql workbench ) and copy in there the sql generated at the previous step

  • Build your app
    play war ${path to your play project} -o ${war name}  --%production --zip

    This will generate the for file for production

  • Deploy your app
    I used the cloudbees sdk and works very well.
    Just download it and the first time you use it, it will ask you for some keys that you can find on cloudbees under Account > Security Keys. Put them in and you are ready to go.
    To deploy your application run the command

    bees app:deploy -a login/yourProject ${war file}


    The cool thing is that the bees deploy command will calculate the difference between the last war and the one you are uploading and will only upload that delta!

Friday, 25 February 2011

How to add information to a SOAP fault message with EJB 3 based web services

Are you building a Java web service based on EJB3?
Do you need to return a more significant message to your web service clients other that just the exception message or even worst the recurring javax.transaction.TransactionRolledbackException?
Well if the answer is YES to the above questions then keep reading...

The code in this article has been tested with JBoss 5.1.0 but it should (!?) work on other EJB containers as well
  1. Create a base application exception that will be extended by all the other exception, I will refer to it as MyApplicationBaseException .
    This exception contains a list of UserMessage, again a class I created with some messages and locale information
  2. You need to create a javax.xml.ws.handler.soap.SOAPHandler < SOAPMessageContext > implementation. Mine looks like this
    import java.util.Set;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Marshaller;
    import javax.xml.namespace.QName;
    import javax.xml.soap.SOAPException;
    import javax.xml.soap.SOAPFault;
    import javax.xml.soap.SOAPMessage;
    import javax.xml.ws.handler.MessageContext;
    import javax.xml.ws.handler.soap.SOAPHandler;
    import javax.xml.ws.handler.soap.SOAPMessageContext;
    
    import org.apache.commons.lang.exception.ExceptionUtils;
    
    public class SoapExceptionHandler implements SOAPHandler<SOAPMessageContext> {
     private transient Logger logger = ServiceLogFactory.getLogger(SoapExceptionHandler.class);
    
     @Override
     public void close(MessageContext context) { }
    
     @Override
     public boolean handleFault(SOAPMessageContext context) {
      try {
       boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
    
       if (outbound) {
        logger.info("Processing " + context + " for exceptions");
        SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
        SOAPFault fault = msg.getSOAPBody().getFault();
        // Retrives the exception from the context
        Exception ex = (Exception) context.get("exception");
        if (ex != null) {
         // Add a fault to the body if not there already
         if (fault == null) {
          fault = msg.getSOAPBody().addFault();
         }
         // Get my exception
         int indexOfType = ExceptionUtils.indexOfType(ex, MyApplicationBaseException.class);
         if (indexOfType != -1) {
          ex = (MyApplicationBaseException)ExceptionUtils.getThrowableList(ex).get(indexOfType);
          MyApplicationBaseException myEx = (AmsException) ex;
          fault.setFaultString(myEx.getMessage());
          try {
           JAXBContext jaxContext = JAXBContext.newInstance(UserMessages.class);
           Marshaller marshaller = jaxContext.createMarshaller();
           //Add the UserMessage xml as a fault detail. Detail interface extends Node
           marshaller.marshal(amsEx.getUserMessages(), fault.addDetail());
          } catch (JAXBException e) {
           throw new RuntimeException("Can't marshall the user message ", e);
          }
         }else {
          logger.info("This is not an AmsException");
         }
        }else {
         logger.warn("No exception found in the webServiceContext");
        }
       }
    
      } catch (SOAPException e) {
       logger.warn("Error when trying to access the soap message", e);
      }
      return true;
     }
    
     @Override
     public boolean handleMessage(SOAPMessageContext context) {
      return true;
     }
    
     @Override
     public Set<QName> getHeaders() {
      return null;
     }
     
     
    }
    
    

  3. Now that you have the exception handler you need to register this SoapHandler with the EJB. To do that you'll need to create an Xml file in your class path and add an annotation to the EJB implementation class.
    The xml file :
    <?xml version="1.0" encoding="UTF-8"?>
       <jws:handler-chains xmlns:jws="http://java.sun.com/xml/ns/javaee">
    
         <jws:handler-chain>
        <jws:handler>
          <jws:handler-name>ExceptionHandler</jws:handler-name>
          <jws:handler-class>com.mycompany.utilities.ExceptionHandler</jws:handler-class>
        </jws:handler>
         </jws:handler-chain>
    
       </jws:handler-chains>
      

    and the EJB with annotation will be

    import javax.jws.HandlerChain;
      
      @Local(MyService.class)
      @Stateless
      @HandlerChain(file = "soapHandler.xml")
      @Interceptors( { MyApplicationInterceptor.class })
      @SOAPBinding(style = SOAPBinding.Style.RPC)
      @WebService(endpointInterface = "com.mycompany.services.myservice", targetNamespace = "http://myservice.services.mycompany.com")
      public final class MyServiceImpl implements MyService {
      
      // service implementation
      
      }
      
      

  4. To make sure all my exceptions have proper messages and that the exception is set in the SOAPMessageContext I use an Interceptor to wrap all the service methods and transform any exception to an instance of MyApplicationException
    The interceptor has a single method
    @AroundInvoke
      private Object setException(InvocationContext ic) throws Exception {
       Object toReturn = null;
       try {
        toReturn = ic.proceed();
       } catch (Exception e) {
        logger.error("Exception during the request processing.", e);
        //converts any exception to MyApplicationException
        e = MyApplicationExceptionHandler.getMyApplicationException(e);
        if (context != null && context.getMessageContext() != null) {
         context.getMessageContext().put("exception", e);
        }
        throw e;
       }
       return toReturn;
      }
      
  5. That's it! You're done.

Thursday, 24 February 2011

My first blog

I've been asked a million times the question "do you have a blog" and I've always thought, yeah I should get one and post some of the geeky stuff I go thru every day at work..but I never got around to actually do it! 
Here I go my first blog...
My non techy girl friend thought that dev in the blog name dev-in-progress stands for devil :)
I thought it was a good name, is it not?