Archive

Posts Tagged ‘Tutorial’

Oracle BPEL Hello World

Hi, everybody! Today, I´m gonna show you how to do a hello world using Oracle´s BPEL engine. You are gonna need:

  • A properly installed SOA Suite , 11g (I´m not gonna show here how to install it, but there are plenty of good stuff on this subject on the web);
  • A JDeveloper 11g with SOA Extensions enabled
  • A test tool named SOAP UI

So, let´s do it: start your SOA Suite and JDeveloper. Once your JDeveloper is open, right-click the applications area, as shown in the figure:

Then, select the menu “SOA Tier” and the SOA Project:

Select your project´s name and the project technology (in our case, SOA):

Create your project using Composite with BPEL (as it should be just a simple BPEL project; some day I will explain here what the other types are):

Select the synchronous template and mark the “expose as a SOAP service” checkbox:

Then, you should see something similar to the image:

Click on the assign component and drag it to the diagram. You see highlighted positions; they are places where you can drop the component:

Place the assign in the proper position:

Then, double click the just-placed component. You should see an image similar to the next one:

Click the ‘plus’ icon and you will see the following options:

Once doing so, click the “copy operation” option. You should see the following screen:

Expand both sides until you see the following screen (you should select the options, too):

Then, click OK. You will get back to the previous screen. Select the “general” tab and change the name of the operation to “AssignEcho”, like the screen:

Click OK and you will get back to the BPEL Designer screen. Now, it´s time to deploy our process. Right-click your project, and you should see a menu like the following:

As you maybe don´t have a connection in place, select the “new connection” option. Then, follow the wizard:

I´m assuming here that your username is weblogic (and you know the password as well):

Also, I´m assuming here that your SOA Suite is loaded on localhost, port 7001 (or 7002 if it´s SSL), with a domain loaded to soa_domain. They are the defaults.

Then, click “test connection”. If everything is OK, you should see the “8 of 8 tests succesful” status message.

Click OK and, once again, you will get back to the BPEL Designer screen. Now, your new connection should be available on the connections list:

Click the newly-created connection and you should see the deploy screen:

While deploying, it should ask for username and password:

Then, access the Enterprise Manager site (for me, it is available on http://localhost:7001/em). Once inputting the username and password, you should see the following screen:

Expand the selections according to the image and select your newly-deployed project:

If you click the “test” button, you should see a screen like this:

Personally, I don´t like using the enterprise manager to test my services, for personal reasons. So, I´d rather using SOAP UI. Select the WSDL of your service and then, create a project in SOAP UI, like this one:

Once doing so, SOAP UI will create a screen like the following (if everything is OK, change the interrogation sign for “hello, world!” or anything like this):

If everything is OK, then you should see a screen like this:

And that´s it! Your BPEL Process is working, as it is echoing every phrase you input to it. If you want to go further, try to explore the Assign component and the others, to improve your knowledge.

See ya!

Advertisements
Categories: BPEL Tags: , , ,

Quick Post: Integrating Spring and Jersey

02/02/2011 2 comments

When: When you need Spring to manage Jersey´s libraries, but still want to be free to develop your services using JAX-RS.

When not: If your team does not know REST, maybe it´s not a good idea to use it in development, because REST has its own culture – Uniform Interfaces, Hypermedia, the concept of Resource-Oriented, etc.

How:

BaseEntity.java

package com.alesaudate.domain;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Version;

import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;


@MappedSuperclass
public abstract class BaseEntity {
	
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
	
	@Version
	private Long version;

	public Long getId() {
		return id;
	}

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

	public Long getVersion() {
		return version;
	}

	public void setVersion(Long version) {
		this.version = version;
	}
	
	
	
	/**
	Validation code, that is valid for all subclasses.
	*/
	public void validate () throws ValidationException {
	
		ClassValidator validator = new ClassValidator(getClass());		
		
		InvalidValue[] invalidValues = validator.getInvalidValues(this);
		
		if (invalidValues != null && invalidValues.length > 0) {
			throw new ValidationException(buildValidationExceptionMessage(invalidValues));
		}
	}
	
	
	public String buildValidationExceptionMessage (InvalidValue[] invalidValues) {
		StringBuilder builder = new StringBuilder();
		
		for (InvalidValue value : invalidValues) {
			builder.append(value.toString()).append("\n");
		}
		
		return builder.toString();
	}
	
}


BaseService.java

package com.alesaudate.services;

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

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.alesaudate.domain.BaseEntity;
import com.alesaudate.domain.InvalidStateException;
import com.alesaudate.domain.Person;
import com.alesaudate.services.support.collections.Collection;



@Component
public abstract class BaseService<T extends BaseEntity> {
	
	
	@Autowired
	private HibernateTemplate hibernateTemplate;
	
	
	@Transactional
	public T createOnDatabase (T entity) throws InvalidStateException {
		entity.validate();
		getHibernateTemplate().persist(entity);
		return entity;
	}
	
	
	@POST
	@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
	public T create (T entity) throws InvalidStateException {
		return createOnDatabase(entity);
	}
	
	
	@Transactional
	public T updateOnDatabase (T entity) throws InvalidStateException {
		entity.validate();
		getHibernateTemplate().update(entity);
		return entity;
	}
	
	@PUT
	@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
	public T update (T entity) throws InvalidStateException  {
		return updateOnDatabase(entity);
	}
	
	
	@Transactional(readOnly=true)
	public T findOnDatabase (Long id) {
		
		DetachedCriteria criteria = DetachedCriteria.forClass(getManagedClass()).add(Restrictions.eq("id", id));
		List entities = getHibernateTemplate().findByCriteria(criteria);
		if (entities.isEmpty()) 
			return null;
		return (T)entities.get(0);
	}
	
	@GET
	@Path("{id}")
	@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
	public T find (@PathParam("id")Long id) {
		T entity = findOnDatabase(id);
		loadEntity(entity);
		return entity;
	}
	
	
	@Transactional(readOnly=true)
	public List<T > findAllFromDatabase () {
		
		DetachedCriteria criteria = DetachedCriteria.forClass(getManagedClass());
		List<T> all = getHibernateTemplate().findByCriteria(criteria);
		loadList(all);
		return all;
	}
	
	@GET	
	@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})	
	@Collection
	public List<T> findAll() {
		return findAllFromDatabase();
	}
	
	
	@Transactional
	public void deleteFromDatabase (T toDelete) {
		getHibernateTemplate().delete(toDelete);
	}
	
	

	@DELETE
	@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
	public void delete (T toDelete) {
		deleteFromDatabase(toDelete);
	}
	
	public abstract Class<? extends BaseEntity> getManagedClass();
	
	
	public abstract void loadEntity (T data);
	
	public abstract void loadList (java.util.Collection<T> data);
	
	
	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}
	
	public HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

}

PersonService.java

package com.alesaudate.services;

import java.util.Collection;

import javax.ws.rs.Path;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alesaudate.domain.Address;
import com.alesaudate.domain.BaseEntity;
import com.alesaudate.domain.Person;




@Component
@Path("/person")
public class PersonService extends BaseService<Person>{

	@Override
	public Class<? extends BaseEntity> getManagedClass() {
		return Person.class;
	}

	@Override
	public void loadEntity(Person data) {
		//Load addresses
		data.getAddresses();
	}

	@Override
	@Transactional(propagation=Propagation.MANDATORY)
	public void loadList(Collection<Person> data) {
		for (Person p : data) {			
			getHibernateTemplate().find("select p.addresses from Person p");
			for (Address address : p.getAddresses()) {
				getHibernateTemplate().evict(address);
				address.makeXMLCompatible();
			}
			getHibernateTemplate().evict(p);
		}
		
	}

	
	
	
	
	
	
}

applicationContext.xml:


<?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:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	

	<context:component-scan base-package="com.alesaudate.services" />

<!-- Other bean definitions... -->		


</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>Architecture</display-name>
  
  <context-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath:applicationContext.xml</param-value>
   </context-param>

 
   <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
   </listener>
   <listener>
          <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
   </listener>

 <servlet>
 	<servlet-name>Jersey Servlet</servlet-name>
 	<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
 </servlet>
 
 <servlet-mapping>
 	<servlet-name>Jersey Servlet</servlet-name>
 	<url-pattern>/*</url-pattern>
 </servlet-mapping>
  
</web-app>

End.

How to develop a standalone SSL web service

12/08/2010 14 comments

How to develop a standalone SSL web service

Hi! Today, I´m gonna show you how to develop a simple SSL web service. Please note that this is not a “enterprise” way of developing a secure web service, but this may be very useful for testing purposes.

Constraints

To develop this service, you need to use a Sun/Oracle JVM (I tested with 1.6 VM, but should work with 1.5, too). This is not gonna work in any other kind of VM. Also, you need to have JAX-WS in your classpath.

The code

The code begins with the development of the web service itself. I used a standard JAX-WS service as an example, so my service looks like this:


@WebService
public class SOAPService {

	public String test() {
		return "Hello, SSL world!";
	}	
}

Next, we may use Java´s Endpoint class to create our object as an web service. The code is like

Endpoint endpoint = Endpoint.create(new SOAPService());

At this point, you need to create the server, and to create the server, you need a .jks (Java Keystore) file. You may create this file using JDK´s (or JRE´s) tooling, but I prefer using a GUI to do so. Personally, I like Lazgo´s KeyStore Explorer (available here). I won´t get in details here on how to create the JKS file; if you don´t know how to do it, you may want to have a look here.

Note: be aware that the CN attribute of your certificate must be equal to your host´s name!

After creating the JKS file, we are ready to create our HTTPS server. I won´t explain this code very much, as it should be pretty self-explanatory:


public static void main(String[] args) throws Exception {
		Endpoint endpoint = Endpoint.create(new SOAPService());
		SSLContext ssl =  SSLContext.getInstance("SSLv3");
		
		
		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 
		KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());


		//Load the JKS file (located, in this case, at D:\keystore.jks, with password 'test'
		store.load(new FileInputStream("D:\\keystore.jks"), "test".toCharArray()); 

		//init the key store, along with the password 'test'
		kmf.init(store, "test".toCharArray());
		KeyManager[] keyManagers = new KeyManager[1];
		keyManagers = kmf.getKeyManagers();
		
		

		//Init the trust manager factory
		TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

		//It will reference the same key store as the key managers
		tmf.init(store);
		
		TrustManager[] trustManagers = tmf.getTrustManagers();
		
		
		ssl.init(keyManagers, trustManagers, new SecureRandom());

		//Init a configuration with our SSL context
		HttpsConfigurator configurator = new HttpsConfigurator(ssl);
		

		//Create a server on localhost, port 443 (https port)
		HttpsServer httpsServer = HttpsServer.create(new InetSocketAddress("localhost", 443), 443);
		httpsServer.setHttpsConfigurator(configurator);
		
		
		//Create a context so our service will be available under this context
		HttpContext context = httpsServer.createContext("/test");
		httpsServer.start();
		

		//Finally, use the created context to publish the service
		endpoint.publish(context);
		
		

	}


And voilà! That should be enough to your service be available under SSL. With this code, the wsdl of the service should be available under https://localhost:443/test?wsdl.

Please note that, at the time that I developed this service, I had some trouble with the generated WSDL, specifically with the port address and references to schema files. A reasonable work around for this problem (if you have it too) is to download the files (WSDL, schemas, and so on) and fix it by hand.

See ya!

How to intercept web services ingoing/outgoing messages

Hi, everybody! I´m just passing around to show a technique to easily intercept JAX-WS web services messages. It is a quick piece of knowledge, and it is based on three simple steps.

I know that you always say, first, what I need to know…

That´s right. First of all, be aware that I tested it with EJB´s services, and I don´t know if it works with other types of exposed web services. Also, note that I present here a solution that works with annotated web services.

Ok, show me the solution

Step #1: Annotate your web service´s class with @HandlerChain:

package com.alesaudate.webservices;

@WebService
@HandlerChain(file="handlers.xml") //Here, you need to reference a configuration file. In this case, it got to be in the same package as the class
public class MyService {

//...


}

Step #2: Create a configuration file, like:

<?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>MimeHandler</jws:handler-name>
      <jws:handler-class>com.alesaudate.webservices.MimeTypeHandler</jws:handler-class>      
    </jws:handler>
</jws:handler-chain>
  
</jws:handler-chains>

(Note that it need to be named “handlers.xml”, as referenced in the annotation)

Step #3: Create a interceptor for the class:

package com.alesaudate.webservices;

import java.util.Set;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
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;

public class MimeTypeHandler implements SOAPHandler<SOAPMessageContext>{

	public Set<QName> getHeaders() {
		return null;
	}

	public void close(MessageContext context) {
		
	}

	public boolean handleFault(SOAPMessageContext context) {
		return true;
	}

	public boolean handleMessage(SOAPMessageContext context) {
		try {
                                          context.getMessage().getMimeHeaders().setHeader("Content-Type", "text/xml");
		} catch (SOAPException e) {
			throw new RuntimeException(e);
		}
		return true;
	}
	

}

Pretty easy, right? I´m taking for granted that the interceptor and the configuration file are pretty self-explanatory, but if you have any doubts, don´t hesitate asking me, OK?

Bye!

How to create a contract-first web service (or: how to create a web service that handles XML)

08/31/2010 1 comment

Hello! Today, I´m gonna show you a sample on how to develop a contract-first web service in java. To do so, you are gonna need:

  • An Apache Tomcat (or any Application Server that is compatible with JAX-WS)
  • A JAX-WS runtime (I used the RI, that I got from here – most application servers already have)

Now, as it is a contract-first web service, we need the contract. I used this one:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://alesaudate.com/webservices" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="blog" targetNamespace="http://alesaudate.com/webservices">
  <wsdl:types>
    <xsd:schema targetNamespace="http://alesaudate.com/webservices">
      <xsd:element name="operation">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="request" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="operationResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="response" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="operation">
    <wsdl:part element="tns:operation" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="operationResponse">
    <wsdl:part element="tns:operationResponse" name="parameters"/>
  </wsdl:message>
  <wsdl:portType name="blog">
    <wsdl:operation name="blogOperation">
      <wsdl:input message="tns:operation"/>
      <wsdl:output message="tns:operationResponse"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="mySOAPBinding" type="tns:blog">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="blogOperation">
      <soap:operation soapAction="http://alesaudate.com/webservices/SampleOperation"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="blogService">
    <wsdl:port binding="tns:mySOAPBinding" name="blogSOAP">
      <soap:address location="http://localhost:8080/WebServices/provider"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Now, we must provide an implementation class (or, how I like to call, where the magic happens =P ). Here is my implementation:

package com.alesaudate.webservices;

import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Provider;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.Service.Mode;

@ServiceMode(Mode.MESSAGE)
@WebServiceProvider(portName="blogSOAP", 
		serviceName="blogService",  
		targetNamespace="http://alesaudate.com/webservices", 
		wsdlLocation="WEB-INF/wsdl/blog.wsdl")
public class MySOAPProvider implements Provider<SOAPMessage>{

	@Override
	public SOAPMessage invoke(SOAPMessage request) {
		try {
			request.writeTo(System.out);
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return request;
	}

}

Please note that this refers to a WEB-INF directory. So, as you may have guessed by now, it MUST run on a web project (.war). Other forms of java files, like .jar or .ear are unable to run this code.

Now, we must provide what I call “the glue”: files that provide the binding. For the RI implementation, we must provide a file called sun-jaxws.xml and place it under the WEB-INF directory. For our project, it has the following structure:

<endpoints
    xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
    version="2.0">

    <endpoint
        name="example"
        implementation="com.alesaudate.webservices.MySOAPProvider"
        wsdl="WEB-INF/wsdl/blog.wsdl"
        service="{http://alesaudate.com/webservices}blogService"
        port="{http://alesaudate.com/webservices}blogSOAP"
        url-pattern="/provider" />

</endpoints>

We also need to insert the right entries into web.xml:

<listener>
        <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>provider</servlet-name>
        <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>provider</servlet-name>
        <url-pattern>/provider</url-pattern>
    </servlet-mapping>

The URL pattern, here, is the address where our web service will answer requests and will provide it´s contract.

Having reached this point so far, we need to place JAX-WS lib´s on the common library directory under Tomcat (for application servers like JBoss, you may skip this step).

And that´s all! Accessing the address http://localhost:8080/WebServices/provider?wsdl has shown me the WSDL that I quoted above, how about you?

You may check the code that I used here at the downloads section.

See ya!

Rules hot deploy using Drools / Guvnor – final

05/30/2010 5 comments

In this final part of the tutorial, I will present how to deploy rules to Guvnor.

As a web application (. War), Guvnor provides much of its functionality as Servlets. Recently, a feature to communicate via REST has been incorporated so we can create, update or delete content from the server. This API is known as REST API, and responds in the URL of / api. For example, create a package, the syntax used is as follows:

/ org.drools.guvnor.Guvnor / api / packages /<package name>. package

Similar to the package creation is the creation of rules, made as follows:

/ org.drools.guvnor.Guvnor / api / packages / <package name> /<rule name>. drl

As these invocations are done via REST, HTTP methods to these URLs are created as follows:

  • GET -> reading
  • POST -> creation
  • PUT -> update
  • DELETE -> delete

That is, assuming the previous examples, and in the case of my machine, reading the rule created previously could be done by making a GET to the URL http://localhost:8080/drools-guvnor/org. drools.guvnor.Guvnor / packages / seguradora/ admissaosegurado.drl

If the reader is in doubt as to how to make such a request, below is an example on how to create a rule using Apache Commons HTTP:

 
/**
* @param drl The rule to be created, that is, the body of the rule itself
* @param drlName The rule name
* @param packageName Guess =)
*/
public static void createDrl (String drl, String drlName, String packageName) throws HttpException, IOException {
		String uri = URL_FOR_RULES_CREATION.replace("{0}", packageName).replace("{1}", drlName);
		PostMethod post = new PostMethod(uri);
		post.setRequestBody(drl);
		sendRequest(post);
	}
private static void sendRequest (HttpMethod method, String username, String password) throws HttpException, IOException {
		HttpClient client = new HttpClient();		
		client.getState().setCredentials("users", host, new UsernamePasswordCredentials(username, password));
		method.setDoAuthentication(true);
		
		client.executeMethod(method);
		
		int statusCode = method.getStatusCode();
		String response = method.getResponseBodyAsString();
		
		if (statusCode != 200 || !response.equals("OK")) {
			throw new HttpException("Something went wrong with the invocation. Status code: " + statusCode + ". Response: " + response);
		}		
	}

However, here is a sign of a problem with this approach: even the present version of Guvnor, the build and create a package / snapshot can not be done in an automated manner using this approach (so, it prevents the immediate consumption of these rules ). I´m not sure if a can expose my solution to this problem on this blog (due to a number of restrictions). However, if I have any appeal of the community I will be happy to check these constraints and if there are no issues, I may publish it here.

How to dynamically create the rule

Rule creation can be done through a set of classes available in the package org.drools.guvnor.client.modeldriven.brl .* , available in JAR drools-compiler- . Thus the creation of a simple rule can be made as follows:

 

**
* @param rule Presumably, a POJO that contains data from the the rule to be generated.
*/
public String generateRuleData(Rule rule) {
		RuleModel model = new RuleModel();
		model.name = rule.getName();
		
		FieldConstraint constraint = evaluate(rule);
		 
		FactPattern fact = new FactPattern(NOME_DO_FATO);
		fact.boundName = VARIABLE_THAT_REPRESENTS_THE_FACT;
		fact.addConstraint(constraint);
		
		model.lhs = new IPattern[]{fact};
		model.rhs = getActions(rule);
		
		BRLPersistence persistence = BRDRLPersistence.getInstance();
		String ruleData = persistence.marshal(model);
		
		return ruleData;
	}

/**
* This method will retrieve a custom model of what will happen inside a rule´s body.
* Remeber that IAction is an interface, so, evaluate the possible implementations.
*/
protected IAction[] getActions(Rule rule) {

//In my case, the method will retrieve a representation of setting a value onto the fact.
		ActionSetField actionSetField = new ActionSetField(VARIABLE_THAT_REPRESENTS_THE_FACT);		
		
		actionSetField.fieldValues = new ActionFieldValue[] {new ActionFieldValue(
"attribute", "value", "type - String, Integer, etc.")};
		
		
		return new IAction[]{actionSetField};
	}

protected FieldConstraint evaluate (Rule rule) {
/*Must return an implementation of the interface FieldConstraint. The available implementations (so far) are the class SingleFieldConstraint and the class CompositeFieldConstraint, that represent, respectively, a single operation on a fact definition and many operations. For particular reasons, I won´t present here this method implementation, so it´s on the reader. */

return null;
}

Where:

  • FactPattern class represents the fact. In the constructor of the same, will the fact that the name will be used. In boundName attribute, you create a variable to perform the assignment.

  • In the method

  • addConstraint are added to the clauses of fact.
  • In

  • attributes rhs and lhs, class RuleModel are added, respectively, the definition of fact and the definition of the consequences.

This method will then return the representation of the rule as String. I won´t go into details here of how to accomplish the definition of fact, as unknown, until now, automated way of creating this in the body’s own rule. Also, since I went into detail about how the syntax for defining the first part of this tutorial, then left to the reader the automated generation ddo fact.

Once the generation is made, just synchronize it with the model presented in the first part of this tutorial and ready, we have the rule generation and creation in Guvnor presented.

solution analysis

Thereby closing the tutorial, step to the critical analysis of the solution (as I hope to do every time I post a tutorial here).

One of the most serious problems with this solution was the fact that you were referred from the party itself, ie the impossibility of creating a snapshot to enable the immediate consumption of the rules. This can cause major problems for those who want immediate solution to the problem (although, as already mentioned, I have the solution to this problem and hope to submit as soon as possible for both the community and for the team responsible for the Guvnor).

Another problem, too, is the creation of a sequence of rules. You can create sequences through the implementation of the rules attribute salience , that can be embedded in the body of rules, and by creating flows rule . However, both mechanisms add problems when it made an automatic creation of new rules, which ultimately makes the scheme impractical.

And finally, citing the problems I cite also the management of these rules is problematic when done by Guvnor, recommended, so some sort of maintaining the route database. This creates a different problem when the use of rules is achieved, because the consumer of the rules is optimized to work with URLs or files, or if consumption of rules is done via the database, it is necessary to create a REST API own (or at least a Servlet that responds by GET method). Moreover, the consumer rules only works with the built package, which can generate an additional problem generation.

That said, it must be noted that the control of an application via API rules is extremely powerful, because besides being a flexible mechanism (from the standpoint of the system operator) is also relatively easy to manage.

Furthermore, a mechanism is also extremely fast (in benchmarks that I made between two different machines and after the creation of consumer rules, the meter accused times between 1 and 10 msec).

Therefore, it is the burden of developers and / or the architect of an application to analyze the cost / benefit of this. Just leave here a reminder that the problems I mentioned occur in development time, and advantages occur with the application already put into production. That is, I believe that the benefits always overwhelm the problems, either from or in any other Drools Rules Engine.

Rules hot deploy using Drools / Guvnor – part 2

05/24/2010 7 comments

In the first part of this tutorial, I introduced the basics and the Drools Guvnor, as well as demonstrated the creation of a simple rule in Guvnor. The objective of this part is to show how to consume the rule set, as well as present as expose it as web service.

Preparing rules for consumption

To prepare the rules for consumption, it is necessary to generate a snapshot of the rules. This is done by his own administration interface Guvnor by selecting the package, performing the same build and selecting the option to create snapshot, as shown:

As a rule created to consume

The rules can be consumed by the Drools API itself, as shown in the code below:

package com.alesaudate.drools.sample.client;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.drools.RuleBase;
import org.drools.StatelessSession;
import org.drools.agent.RuleAgent;
import org.drools.definition.type.FactType;

public class Client {
private Properties config;
private String factName;
private Map factValues;
private Map expectedResultsMap;
private static Map agents = new HashMap();

public void consume () throws InstantiationException, IllegalAccessException {
RuleAgent agent = getAgent(getConfig().getProperty("url"));
RuleBase ruleBase = agent.getRuleBase();
FactType factType = ruleBase.getFactType(getFactName());
Object fact = factType.newInstance();
Map values = getFactValues();
for (String key : values.keySet()) {
factType.set(fact, key, values.get(key));
}
StatelessSession statelessSession = ruleBase.newStatelessSession();
statelessSession.execute(fact);
Map expectedResults = getExpectedResultsMap();
for (String key : expectedResults.keySet()) {
Object resultValue = factType.get(fact, key);
expectedResults.put(key, resultValue);
}
}
private RuleAgent getAgent(String url) {
if (!agents.containsKey(url)) {
RuleAgent agent = RuleAgent.newRuleAgent(getConfig());
agents.put(url, agent);
}
return agents.get(url);
}
// getters and setters…

}

Where the field config is the configuration of the API (to be explained), the field factName represents the fully qualified name of the POJO that will be used as fact (in our example, is seguradora.Segurado) factValues is a map that will be used to populate fields and the fact expectedResultsMap is a map that will be used to retrieve the desired values of the implementation of the rule (in our case, the status field is interesting).

The field config can be configured using the following data:

  • newInstance: Used to determine whether a new instance of the rule will be created (for the case of rules that keep state)
  • file: to determine which file (local) will be read
  • dir: to establish a directory for reading
  • url: URL to determine a reading (which will be used, in our case)
  • poll: interval in seconds, rereading the selected source (to determine whether the rule needs to be updated on the local machine)
  • name: name of the configuration
  • localCacheDir: directory will be cached remotely read the rule.

For the last class as an example, you can perform the reading of the rule using the following code:

 
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		Client client = new Client();
		Properties config = new Properties();
		config.put("url", "http://localhost:8080/drools-guvnor/org.drools.guvnor.Guvnor/package/seguradora/LATEST");
		client.setConfig(config);
		client.setFactName("seguradora.Segurado");
		
		Map factValues = new HashMap();
		factValues.put("idade", 18);
		client.setFactValues(factValues);
		
		Map expectedResults = new HashMap();
		expectedResults.put("status", null);
		client.setExpectedResultsMap(expectedResults);
		
		
		client.consume();
		
		System.out.println(client.getExpectedResultsMap());
		
	}

Still using the example code, you can expose the rules as services for two distinct ways:

  • Creating a custom web service (the sample code is available on the downloads page, next to the sample code consumer rules)
  • Using Drools Server, placing a file. properties (similar to the properties file passed as parameter to the client) in the “WEB-INF/classes” Application of Drools Server. In this way, however, the web service will be exposed as REST.

The downloads page , is available for download the client’s consumption and exposure rule as SOAP web service.

The next part will present how to perform deploy a rule dynamically.