-->

vendredi 18 décembre 2015

First step with Java EE 7

Introduction :

First, to explore the Java EE standard , I'll start by writing an article to simplify the development of applications that rely on Java EE 7.

Now, let's configure :


To begin, we will create a maven project and to do so, we can use the plugin maven below : 

mvn -DarchetypeGroupId=org.wildfly.archetype 
    -DarchetypeArtifactId=wildfly-javaee7-webapp-blank-archetype 
    -DarchetypeVersion=8.2.0.Final 
    -DgroupId=fr.blogspot.mjhazbri 
    -DartifactId=ContactManagement 
    -Dversion=1.0.0-SNAPSHOT 
    -Dpackage=fr.blogspot.mjhazbri 
    -Darchetype.interactive=false 
    --batch-mode 
    --update-snapshots 
     archetype:generate 


And as a result, we have a project called ContactManagement.

Framework :


The list of frameworks that will be used is EJB3.2 , JPA2.1.

Generated file :


There is a portion of source code and configuration that are generated and which are :

persistence.xml



   
      
      
      java:jboss/datasources/ContactsDS
      
         
         
         
      
   

If you notice well , we see a datasource was declared , the data dource is under the WEB- INF folder in the webapp folder : 





 
 
  jdbc:mysql://localhost:3306/contacts
  mysql
  
   root
   
  
 

 

This datasource refers to the RDBMS MySQL then the driver must be provided and also create the associated database , so the driver can be downloaded from internet and we must put it under the deployments directory of our Wildfly server.

 
To create the database , simply go on MySQL Workbench and execute the following query

create database contacts;


Now, let's code :

The first thing to do is to code entities , I'll create three entities User, Group and Right :

package fr.blogspot.mjhazbri.entities;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

/**
 * @author jhazbri
 *
 */
@Entity
@Table(name = "USERS")
public class User {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Integer id;

 @Column(name = "usr_login")
 private String login;

 @Column(name = "usr_password")
 private String password;

 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name = "usr_group")
 private Group group;

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getLogin() {
  return login;
 }

 public void setLogin(String login) {
  this.login = login;
 }

 public String getPassword() {
  return password;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 public Group getGroup() {
  return group;
 }

 public void setGroup(Group group) {
  this.group = group;
 }
}

package fr.blogspot.mjhazbri.entities;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 * @author jhazbri
 *
 */
@Entity
@Table(name = "GROUPS")
public class Group {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Integer id;

 @Column(name = "grp_name")
 private String groupName;

 @OneToMany(targetEntity = User.class, mappedBy = "group")
 private Set users = new HashSet();

 @ManyToMany(targetEntity = Right.class)
 private Set rights = new HashSet();

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getGroupName() {
  return groupName;
 }

 public void setGroupName(String groupName) {
  this.groupName = groupName;
 }

 public Set getUsers() {
  return users;
 }

 public void setUsers(Set users) {
  this.users = users;
 }

 public Set getRights() {
  return rights;
 }

 public void setRights(Set rights) {
  this.rights = rights;
 }
}

package fr.blogspot.mjhazbri.entities;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

/**
 * @author jhazbri
 *
 */
@Entity
@Table(name = "RIGHTS")
public class Right {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Integer id;

 @Column(name = "rht_name")
 private String rightValue;

 @ManyToMany(targetEntity = Group.class, mappedBy = "rights")
 private Set groups = new HashSet();

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getRightValue() {
  return rightValue;
 }

 public void setRightValue(String rightValue) {
  this.rightValue = rightValue;
 }

 public Set getGroups() {
  return groups;
 }

 public void setGroups(Set groups) {
  this.groups = groups;
 }

}

package fr.blogspot.mjhazbri.dao;

import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import fr.blogspot.mjhazbri.entities.User;

/**
 * @author jhazbri
 *
 */
@Stateless
public class UserDao {

 private final static Logger logger = Logger.getLogger(UserDao.class
   .getName());

 @PersistenceContext()
 private EntityManager em;

 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
 @PostConstruct
 public void init() {
  logger.info("init EJB method ... ");
 }

 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
 @PreDestroy
 public void destroy() {
  logger.info("destory EJB method ... ");
 }

 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
 public User create(User user) {
  em.persist(user);
  return user;
 }

 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
 public User update(User user) {
  em.merge(user);
  return user;
 }

 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
 public User search(Integer userId) {
  return em.find(User.class, userId);
 }
}

So to test the injection of EJB , I create a service where I will inject Dao :

/**
 * 
 */
package fr.blogspot.mjhazbri.services;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

import fr.blogspot.mjhazbri.dao.UserDao;
import fr.blogspot.mjhazbri.entities.Group;
import fr.blogspot.mjhazbri.entities.User;

/**
 * @author jhazbri
 *
 */
@Stateless
public class UserManagementService {

 @EJB
 private UserDao userDao;

 @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
 private User addUserInGroup(User user, Group group) {
  user.setGroup(group);
  return userDao.create(user);
 }
}

mercredi 21 janvier 2015

CXF3 and Spring 4 : Using Interceptor ...

Introduction :

I continue with the exploration of the CXF framework and present my feedback with web services. With this present article, I will introduce interceptors and i will give an example among several use cases: I begin with introducing the example to more understand : if we want to handle all exceptions and not to return to the user a technical stack trace that can never understand, so instead we can return a customizable messages with any thrown exception. In this case, we must use CXF interceptors.

Let's code now:

As always I rely on my old article and I use the same sources. We will start by creating exceptions and first we will create an Enum for errors list :
package fr.mjhazbri.webservices.exception;
/**
 * 
 * @author jhazbri
 *
 */
public enum ExceptionErrorCode {

 GENERAL_ERROR("GENERAL_ERROR"), 
 VALIDATION_ERROR("VALIDATION_ERROR"), 
 CONNECTION_ERROR("CONNECTION_ERROR"), 
 INTERNAL_ERROR("INTERNAL_ERROR"), 
 UPLOAD_ERROR("UPLOAD_ERROR"), 
 STORAGE_ERROR("STORAGE_ERROR"), 
 UNKOWN_ERROR("UNKOWN_ERROR");

 private String value;

 /**
  * @return the value
  */
 public String getValue() {
  return value;
 }

 /**
  * @param value
  */
 private ExceptionErrorCode(String value) {
  this.value = value;
 }
}

Then we will create an exception called BusinessException to handle all errors:

/**
 * 
 */
package fr.mjhazbri.webservices.exception;

/**
 * @author jhazbri
 * 
 */
public class BusinessException extends Exception {

 private static final long serialVersionUID = 6379178121998659740L;
 private ExceptionErrorCode errorCode;

 public BusinessException(String message, Throwable cause,ExceptionErrorCode errorCode) 
 {
  super(message, cause);
  this.setErrorCode(errorCode);
 }

 public BusinessException(String message, ExceptionErrorCode errorCode) 
 {
  super(message);
  this.setErrorCode(errorCode);
 }

 public BusinessException(Throwable cause, ExceptionErrorCode errorCode) 
 {
  super(cause);
  this.setErrorCode(errorCode);
 }

 /**
  * @return the errorCode
  */
 public ExceptionErrorCode getErrorCode() 
 {
  return errorCode;
 }

 /**
  * @param errorCode
  *            the errorCode to set
  */
 public void setErrorCode(ExceptionErrorCode errorCode) 
 {
  this.errorCode = errorCode;
 }

 public String getCodeValue() 
 {
  if (errorCode == null)
   return "-1";
  else
   return errorCode.getValue();
 }
}


Now a ExceptionManager interface to manage and manufacture BusinessException:

/**
 * 
 */
package fr.mjhazbri.webservices.exception.api;

import fr.mjhazbri.webservices.exception.BusinessException;
import fr.mjhazbri.webservices.exception.ExceptionErrorCode;

/**
 * @author jhazbri
 *
 */
public interface ExceptionManager {
 BusinessException buildBusinessException (ExceptionErrorCode errorCode); 
 String getMessageByErrorCode (ExceptionErrorCode exceptionErrorCode); 
}


An implementation for this interface is ExceptionManagerImpl and load the properties file messages.properties that contains the error messages list :

/**
 * 
 */
package fr.mjhazbri.webservices.exception;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;

import fr.mjhazbri.webservices.exception.api.ExceptionManager;

/**
 * @author jhazbri
 * 
 */
@Configuration
@PropertySources(@PropertySource("classpath:messages.properties"))
public class ExceptionManagerImpl implements ExceptionManager {

 @Autowired
 private Environment env;

 @Override
 public BusinessException buildBusinessException(ExceptionErrorCode errorCode) {
  String msg = getMessageByErrorCode(errorCode);
  return new BusinessException(msg, errorCode);
 }

 @Override
 public String getMessageByErrorCode(ExceptionErrorCode exceptionErrorCode) {
  return env.getProperty(exceptionErrorCode.getValue());
 }
}


And that is the content of the file messages.properties

GENERAL_ERROR = GENERAL_ERROR
VALIDATION_ERROR = VALIDATION_ERROR
CONNECTION_ERROR = CONNECTION_ERROR
INTERNAL_ERROR = INTERNAL_ERROR
UNKOWN_ERROR = UNKOWN_ERROR

Interceptor :

Now let's see the CXF Interceptor :
/**
 * 
 */
package fr.mjhazbri.webservices.interceptor;

import javax.xml.namespace.QName;
import javax.xml.ws.WebServiceException;

import org.apache.cxf.common.injection.NoJSR250Annotations;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import fr.mjhazbri.webservices.exception.BusinessException;
import fr.mjhazbri.webservices.exception.ExceptionErrorCode;
import fr.mjhazbri.webservices.exception.api.ExceptionManager;

@NoJSR250Annotations
public class ExceptionInterceptor extends AbstractPhaseInterceptor<Message> {
 /**
  * Class logger.
  */
 private static final Logger logger = Logger.getLogger(ExceptionInterceptor.class);
 
 @Autowired
 private ExceptionManager exceptionManager ; 
 
 public ExceptionInterceptor() {
  super(Phase.MARSHAL);
 }
 
 public void handleMessage(Message message) throws Fault {
  logger.debug("ExceptionInterceptor () : handleMessage ... () ");
  Fault fault = (Fault) message.getContent(Exception.class);
  if (fault != null )
  {
   if (fault.getCause() instanceof BusinessException )
   {
    BusinessException ex = (BusinessException) fault.getCause();
    logger.debug("BusinessException  ....." );
    RuntimeException exception=  new WebServiceException(ex.getMessage());
    Fault faultResponse = new Fault(exception);
    faultResponse.setFaultCode(new QName(ex.getErrorCode().getValue()));
    message.setContent(Exception.class, faultResponse); 
   }
   else 
   {
    Throwable ex = fault.getCause();
    logger.debug("ExceptionInterceptor () : handleMessage for class " + ex.getClass().getName());
    logger.error("Exception not managed : "+ ex.getClass().getName(),ex);
    // préparer le message à retourner.
    RuntimeException exception=  new WebServiceException(exceptionManager.getMessageByErrorCode(ExceptionErrorCode.INTERNAL_ERROR));
    Fault faultResponse = new Fault(exception);
    faultResponse.setFaultCode(new QName(ExceptionErrorCode.INTERNAL_ERROR.name()));
    message.setContent(Exception.class, faultResponse); 
   }
   
  }
 }
}

And finally let's configure the cxf-servlet.xml




 
  
 

mardi 20 janvier 2015

CXF3 and Spring 4 : using MTOM to upload file

Introduction :

For many applications, files used with any type and generally must upload them to a remote server. But transport files within an HTTP request is really painful for the connection and for the size of the HTTP request. So use the attachment of a file to the message in order to optimize the process. This is the MTOM.
And it is in this context we will see how to make web services with MTOM.
To do that, we must create a maven project and add cxf outbuildings and spring and configure all. You can see my previous article.

Let's code:

Also, we will use the Google API to manipulate files guava, so we have to add the maven dependency:


 com.google.guava
 guava
 14.0

So first we will create a class UploadParameter that implements Parameter (see previous article).
package fr.mjhazbri.webservices.schema;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import fr.mjhazbri.webservices.validation.parameter.Parameter;

@XmlRootElement(name = "uploadParameter")
@XmlAccessorType(XmlAccessType.FIELD)
public class UploadParameter implements Parameter {
 /**
  * The file name parameter.
  */
 @XmlElement(name = "fileName", required = false)
 private String fileName;
 /**
  * the file size.
  */
 @XmlElement(name = "fileSize", required = true, nillable = false)
 private Long fileSize;

 /**
  * the file hash code.
  */
 @XmlElement(name = "hashCode", required = false, nillable = false)
 private String hashCode;

 /**
  * the file hash type.
  */
 @XmlElement(name = "hashType", required = false, nillable = false)
 private String hashType;

 /**
  * @return the fileName
  */
 public String getFileName() {
  return fileName;
 }

 /**
  * @param fileName
  *            the fileName to set
  */
 public void setFileName(String fileName) {
  this.fileName = fileName;
 }

 /**
  * @return the fileSize
  */
 public Long getFileSize() {
  return fileSize;
 }

 /**
  * @param fileSize
  *            the fileSize to set
  */
 public void setFileSize(Long fileSize) {
  this.fileSize = fileSize;
 }

 /**
  * @return the hashCode
  */
 public String getHashCode() {
  return hashCode;
 }

 /**
  * @param hashCode
  *            the hashCode to set
  */
 public void setHashCode(String hashCode) {
  this.hashCode = hashCode;
 }

 /**
  * @return the hashType
  */
 public String getHashType() {
  return hashType;
 }

 /**
  * @param hashType
  *            the hashType to set
  */
 public void setHashType(String hashType) {
  this.hashType = hashType;
 }

 /* (non-Javadoc)
  * @see java.lang.Object#toString()
  */
 @Override
 public String toString() {
  return "UploadParameter [fileName=" + fileName + ", fileSize="
    + fileSize + ", hashCode=" + hashCode + ", hashType="
    + hashType + "]";
 }

}
And also the UploadResponse class to simplify the return of the web service :
/**
 * 
 */
package fr.mjhazbri.webservices.upload.response;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
 * @author jhazbri
 * 
 */
@XmlRootElement(name = "UploadResponse")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name="UploadResponse")
public class UploadResponse {
 /**
  * The response status : OK or KO
  */
 @XmlElement(name = "status")
 private ResponseStatus responseStatus;

 /**
  * the error messages stack
  */
 private List<String> errorMessage = new ArrayList<String>();

 @XmlElement(name = "fileStoragePath")
 private String fileStoragePath;

 /**
  * @return the responseStatus
  */
 public ResponseStatus getResponseStatus() {
  return responseStatus;
 }

 /**
  * @param responseStatus
  *            the responseStatus to set
  */
 public void setResponseStatus(ResponseStatus responseStatus) {
  this.responseStatus = responseStatus;
 }

 /**
  * @return the errorMessage
  */
 public List<String> getErrorMessage() {
  return errorMessage;
 }

 /**
  * @param errorMessage
  *            the errorMessage to set
  */
 public void setErrorMessage(List<String> errorMessage) {
  this.errorMessage = errorMessage;
 }

 /**
  * @return the fileStoragePath
  */
 public String getFileStoragePath() {
  return fileStoragePath;
 }

 /**
  * @param fileStoragePath
  *            the fileStoragePath to set
  */
 public void setFileStoragePath(String fileStoragePath) {
  this.fileStoragePath = fileStoragePath;
 }

 public void addErrorMessage(String message) {
  if (errorMessage == null)
   errorMessage = new ArrayList<String>();
  errorMessage.add(message);
 }
}
Do not forget ResponseStatus for the status of the response:
package fr.mjhazbri.webservices.upload.response;

import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;

@XmlType(name="status")
@XmlEnum(String.class)
public enum ResponseStatus {

 OK("OK"), KO("KO");

 private String value;

 /**
  * @return the value
  */
 public String getValue() {
  return value;
 }

 /**
  * @param value
  */
 private ResponseStatus(String value) {
  this.value = value;
 }
}
And now UploadWebService interface is written to model the methods to expose:
/**
 * 
 */
package fr.mjhazbri.webservices.upload.api;

import javax.activation.DataHandler;
import javax.jws.WebService;
import javax.xml.ws.WebServiceException;

import fr.mjhazbri.webservices.schema.UploadParameter;
import fr.mjhazbri.webservices.upload.response.UploadResponse;

/**
 * @author jhazbri
 *
 */
@WebService
public interface UploadWebService {
 /**
  * This method is used to send file
  * 
  * @param UploadParameter
  * @param dataHandler
  * @return UploadResponse
  */
 UploadResponse  upload(UploadParameter uploadParameter, DataHandler dataHandler) throws WebServiceException;
}

Finally, what interests us is the class for writing the business logic of the upload starting with validation web service, save the received file and return a result to the client

/**
 * 
 */
package fr.mjhazbri.webservices.upload;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.UUID;

import javax.activation.DataHandler;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.MTOM;

import org.apache.log4j.Logger;

import com.google.common.io.Files;

import fr.mjhazbri.webservices.schema.UploadParameter;
import fr.mjhazbri.webservices.upload.api.UploadWebService;
import fr.mjhazbri.webservices.upload.response.ResponseStatus;
import fr.mjhazbri.webservices.upload.response.UploadResponse;
import fr.mjhazbri.webservices.validation.result.Result;
import fr.mjhazbri.webservices.validation.service.ParameterValidationServiceImpl;
import fr.mjhazbri.webservices.validation.service.api.ParameterValidationService;

/**
 * @author jhazbri
 *
 */
@WebService(endpointInterface = "fr.mjhazbri.webservices.upload.api.UploadWebService",serviceName = "UploadWebService", portName = "UploadWebServicePort", name = "UploadWebService", targetNamespace = "http://www.mjhazbri.fr/upload")
@MTOM(enabled=true)
@BindingType(value = javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_MTOM_BINDING)
public class UploadWebServiceImpl implements UploadWebService{

 private static final Logger logger = Logger.getLogger(UploadWebServiceImpl.class);
 private ParameterValidationService<UploadParameter>validationService = new ParameterValidationServiceImpl<UploadParameter>(); 
 /**
  * The XSD PATH
  */
 private static final String DEFAULT_XSD_PATH = "parameter.xsd";
 
 @Override
 public UploadResponse upload(UploadParameter uploadParameter,DataHandler dataHandler) throws WebServiceException {

  UploadResponse response = new UploadResponse() ; 
  response.setResponseStatus(ResponseStatus.OK);
  
  logger.debug("calling sayHello with parameter : "+ uploadParameter);
  // validate the web services 
  File fileTmp = new File(this.getClass().getClassLoader().getResource(DEFAULT_XSD_PATH).getPath()) ; 
  
  Result result = validationService.validateParam(uploadParameter, fileTmp);
  if (!result.isValid()) 
  {
   throw new WebServiceException(result.getMessage());
  }
  
  File storage = fileTmp.getParentFile() ; 
  if (storage == null )
  {
   throw new WebServiceException("Cannot store file");
  }
  
  // get and store file
  byte[] dataByte = null ;
  try 
  {
   ByteArrayOutputStream bos = new ByteArrayOutputStream();
   dataHandler.writeTo(bos);
   bos.flush();
   bos.close();
   dataByte = bos.toByteArray();
   StringBuilder builder = new StringBuilder() ; 
   builder.append(storage.getPath());
   builder.append("/");
   
   builder.append(Files.getNameWithoutExtension(uploadParameter.getFileName()));
   builder.append("_");
   builder.append(UUID.randomUUID().toString().replaceAll("-", ""));
   if (Files.getFileExtension(uploadParameter.getFileName()) != null &&  !"".equals(Files.getFileExtension(uploadParameter.getFileName())))
   {
    builder.append(".");
    builder.append(Files.getFileExtension(uploadParameter.getFileName()));
   }
   Files.write(dataByte, new File(builder.toString())) ;
   response.setFileStoragePath(builder.toString());
   logger.debug("End upload with success !");
  }
  catch (Exception e) 
  {
   logger.error("error on attachment : "+e.getMessage());
   response.setResponseStatus(ResponseStatus.KO);
   response.addErrorMessage(e.getMessage());
  }
  
  return response;
 }
}

CXF3 and Spring 4 : Parameter validation with XSD ...

Introduction :

For many times, we still believe validate our web services parameters against a xsd file. During this short tutorial, we will learn how to generate a xsd file from our Java classes in a context of maven project and then we will validate the data sent by the client with our xsd already generated.

Generate XSD :

There are the jaxb2-maven-plugin to manipulate Java and XSD :This plugin uses jaxb2 to generate Java classes from XML Schemas (and binding files) and to create XML Schemas for Existing Java classes. For using this plugin, we should know goals plugins : 

  • jaxb2:schemagen Creates XML Schema Definition (XSD) file(s) from annotated Java sources. 
  • jaxb2:testSchemagen Creates XML Schema Definition (XSD) file(s) from annotated Java test sources. 
  • jaxb2:xjc Generates Java sources from XML Schema(s). 
  • jaxb2:testXjc Generates Java test sources from XML Schema(s).

And now, to generate our file, we will use the first goal : schemagen. To do this, it's so simple, we should add in our pom.xml this plugin and configure the how to access java classes :

 org.codehaus.mojo
 jaxb2-maven-plugin
 1.5
 
  
   
    schemagen
   
   generate-sources
   
    ${project.build.directory}/schemas
    ${project.build.directory}/generated-sources/jaxb
    
     fr/mjhazbri/webservices/schema/*.java
     fr/mjhazbri/webservices/validation/parameter/*.java
    
    
     
      http://www.mjhazbri.fr/parameter
      parameter.xsd
     
    
   
  
 

With this addition to our pom.xml was that each file in the directory fr/mjhazbri/webservices/validation/parameter/*.java will be generated as a parameter to our web services and that will be generated with the namespace http://www.mjhazbri.fr/parameter For this, we will add a small package-info in this same directory :
@javax.xml.bind.annotation.XmlSchema(
  namespace="http://www.mjhazbri.fr/parameter",
  elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package fr.mjhazbri.webservices.schema;

Parameter validation with our XSD generated:

To do this, we will create a first interface to represents parameter, it look like this :
package fr.mjhazbri.webservices.validation.parameter;

public interface Parameter {

}
The result of the web services validation :
package fr.mjhazbri.webservices.validation.result;

public class Result {

 /**
  * Is validation ok.
  */
 private boolean isValid = true;

 /**
  * Message from validation.
  */
 private String message = null;

 public final boolean isValid() {
  return isValid;
 }

 public final void setValid(boolean isValid) {
  this.isValid = isValid;
 }

 public final String getMessage() {
  return message;
 }

 public final void setMessage(String message) {
  this.message = message;
 }
}
And a second interface to represent the methods needed :
**
 * 
 */
package fr.mjhazbri.webservices.validation.service.api;

import java.io.File;

import fr.mjhazbri.webservices.validation.parameter.Parameter;
import fr.mjhazbri.webservices.validation.result.Result;

/**
 * @author jhazbri
 * 
 */
public interface ParameterValidationService<T extends Parameter> {

 Result validateParam(final T param, final File xsdFile);
}
And now, we will code the class ParameterValidationService that implements the logic of validation :
/**
 * 
 */
package fr.mjhazbri.webservices.validation.service;

import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.util.JAXBSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.apache.log4j.Logger;
import org.xml.sax.SAXParseException;

import fr.mjhazbri.webservices.validation.parameter.Parameter;
import fr.mjhazbri.webservices.validation.result.Result;
import fr.mjhazbri.webservices.validation.service.api.ParameterValidationService;

/**
 * @author jhazbri
 * 
 */
public class ParameterValidationServiceImpl<T extends Parameter> implements ParameterValidationService<T> {

	private static final Logger LOGGER = Logger.getLogger(ParameterValidationServiceImpl.class);

	private static final SchemaFactory SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
	private static final ConcurrentHashMap<Class<?>, JAXBContext> CONTEXTS = new ConcurrentHashMap<Class<?>, JAXBContext>();
	private static final ConcurrentMap<String, Validator> VALIDATORS = new ConcurrentHashMap<String, Validator>();

 @Override
 public synchronized Result validateParam(T param, File xsdFile)
 {
  Result result = new Result() ; 
  try 
  {
   // Validate source.
   LOGGER.debug(String.format("Begins validation - param %s - xsdPath %s !", param.getClass().toString(), xsdFile.toString()));
   Validator validator = VALIDATORS.get(xsdFile.toString());
   if (validator == null )
   {
    LOGGER.debug("VALIDATOR - resources : " + ParameterValidationServiceImpl.class.getResource("."));
    LOGGER.debug("xsd File : " + xsdFile.toString());
    final Schema schema = SCHEMA_FACTORY.newSchema(xsdFile);
    validator = schema.newValidator();
    VALIDATORS.putIfAbsent(xsdFile.toString(), validator);
   }
   
   JAXBContext context = CONTEXTS.get(param.getClass());
   if (context == null) 
   {
    context = JAXBContext.newInstance(param.getClass());
    CONTEXTS.putIfAbsent(param.getClass(), context);
   }
   validator.validate(new JAXBSource(context ,param));
         LOGGER.debug("Ends validation !");
  } catch (SAXParseException spe)
  {
   LOGGER.warn(spe.getMessage(), spe);
   result.setValid(false);
   result.setMessage(spe.getMessage());
  }
  catch (Exception exception)
  {
   LOGGER.warn(exception.getMessage(), exception);
   result.setValid(false);
   result.setMessage(exception.getMessage());
  }
        LOGGER.debug("isValid : " + result.isValid());
  return result;
 }

}

lundi 19 janvier 2015

CXF3 and Spring 4 : from the begining ...

Introduction :

In our development, we often need to make web services if either SOAP or REST, one of the best frameworks that not only implements the standard JEE but offers many more possibilities for the web services dev is apache-cxf.

Personally, there are more than two years that I use apache-cxf to develop web services, but recently when I had to push things, I discovered how to do many other features with this framework that I would like to share on my blog.

Now, let's code :

So first we need to create a web project with maven: for this is simple just run this command on a system console :

mvn archetype:generate 
-DgroupId=fr.mjhazbri.webservices 
-DartifactId=ShowInterceptor 
-DarchetypeArtifactId=maven-archetype-webapp 
-DinteractiveMode=false

Now our project is created, but contains no great thing: neither config spring nor the config cxf. So we're going to edit web.xml : 

 
  Apache CXF binding
  cxf
  org.apache.cxf.transport.servlet.CXFServlet
  1
 
 
  cxf
  /*
 

Then we will create a file named cxf-servlet.xml in the same place as web.xml, so under WEB-INF directory, this file will contain the cxf and spring configuration, it look like this: 


 
 
 
  
 
 

 

Then we will create a file named cxf-servlet.xml in the same place as web.xml, so under WEB-INF directory, this file will contain the cxf and spring configuration, it look like this:  Now we should edit the pom file file to get cxf, spring dependencies, so we will add this to our pom.xml file (we must put these lines between the dependencies tags ):
  
   org.springframework
   spring-web
   4.0.5.RELEASE
   compile
  
  
   org.springframework
   spring-context
   4.0.5.RELEASE
   compile
  
  
   org.apache.cxf
   cxf-api
   2.7.14
   compile
  
  
   org.apache.cxf
   cxf-rt-frontend-jaxws
   2.7.14
   compile
  
  
   org.apache.cxf
   cxf-rt-transports-http
   2.7.14
   compile
  
  
   org.springframework
   spring-test
   4.0.5.RELEASE
   test
  
  
   junit
   junit
   4.8.2
   test
  
  
   log4j
   log4j
   1.2.17
  
Be careful before you have to update the list of repositories, it will look like this :
 
  
   springsource-repo
   SpringSource Repository
   http://repo.springsource.org/release
  
  
   maven2-repository.java.net
   Java.net Repository for Maven
   http://download.java.net/maven/2/
   default
  

First, the class Parameter in the package fr.mjhazbri.webservices.schema should be created to represent input data to our web service :
/**
 * 
 */
package fr.mjhazbri.webservices.schema;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

/**
 * @author jhazbri
 * 
 */
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Parameter {
 private String name;

 /**
  * @return the name
  */
 public String getName() {
  return name;
 }

 /**
  * @param name
  *            the name to set
  */
 public void setName(String name) {
  this.name = name;
 }

 @Override
 public String toString() {
  return "Parameter [name=" + name + "]";
 }

}

After this class, in the package fr.mjhazbri.webservices.api, we will create the interface : HelloWorld

package fr.mjhazbri.webservices.api;

import javax.jws.WebService;

import fr.mjhazbri.webservices.schema.Parameter;

/**
 * @author jhazbri
 *
 */
@WebService
public interface HelloWorld {
 
 String sayHello (Parameter parameter );

}

The class that implements HelloWorld interface will be in the fr.mjhazbri.webservices package and named HelloWorldImpl : it look like this :
package fr.mjhazbri.webservices;

import javax.jws.WebService;

import org.apache.log4j.Logger;

import fr.mjhazbri.webservices.api.HelloWorld;
import fr.mjhazbri.webservices.schema.Parameter;

@WebService(endpointInterface = "fr.mjhazbri.webservices.api.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
 private static final Logger logger = Logger.getLogger(HelloWorldImpl.class);
 @Override
 public String sayHello(Parameter parameter) {
  logger.debug("calling sayHello with parameter : "+ parameter);
  return "Hello : " + parameter.getName();
 }
}

Finally, you can now deploy to web container and use your web service.