Showing posts with label java. Show all posts
Showing posts with label java. Show all posts

Wednesday, May 05, 2010

Metering Java Reader and Writer Objects

Last few days I was working on measuring the bandwidth consumed by different java objects passed to our remote interfaces. It was simple task to do it with 'String' objects, byte arrays as we can directly get the sizes of them using String.length and byte[].size() methods.

But there were objects of type Reader and Writer, which is supposed to transfer big chunk of data. There also we could load all the data to memory and measure the sizes.
// measuring reader size - memory inefficient method

// read all data to buffer and measure the length
StringReader stringReader = new StringReader(reader);
int size = stringReader.toString().length;

doRealWork(stringReader);

But that will consume lot of memory (even possible to exceed available heap size). So it is wrong to use the above method to measure the size of reader or writer.

Anyway there is an easy solution for the problem. We can use the design of Reader API itself to measure it size. The Reader interface has a method that read the data by small chunks. We just need to intercept that call and measure the size of each small chunk and add them all. For that we need to implement the Reader interface in to a custom class (say 'MonitoredReader'). Here is how it is implemented.
import java.io.IOException;
import java.io.Reader;

/**
* The class to intercept the read method and calculate
* the number of reads
*/

public class MonitoredReader extends Reader {

Reader reader;
int totalReadSize;

/**
* constructor that wraps the original reader object
*/

public MonitoredReader(Reader reader) {
this.reader = reader;
totalReadSize = 0;
}

/**
* The method to call by the user to read the data. We will just calculate the amount
* of data read here.
*
* @param cBuf destination buffer
* @param off offset at which to start storing characters
* @param len maximum number of characters to read
*
* @return the number of characters read, or -1 if the end of the stream has been reached
* @throws IOException if an I/O error occurs
*/

public int read(char cbuf[], int off, int len) throws IOException {

int read = reader.read(cbuf, off, len);
totalReadSize += read;
return read;
}

/**
* Method to call after finishing reading the data. We will just pass the call to the
* original reader
*/


public void close() throws IOException {
reader.close();
}

/**
* Custom method that will return the total size of read data
*
* @return the size of the data read
*/

public int getTotalReadSize() {
return totalReadSize;
}
}

So our code to measure the size will simple reduce to the following piece of code.
// measuring reader size - memory efficient method

// just wrap the original reader with our custom reader
MonitoredReader monitoredReader = new MonitoredReader(reader);

// pass our custom reader to the real work
doRealWork(monitoredReader);

// get the size read in the real work
int size = monitoredReader.getTotalReadSize();

Similarly we can use this method to get the data size of the writer. (amount of data written to the writer).
import java.io.IOException;
import java.io.Writer;

/**
* The class to intercept the write method and calculate
* the number of writes
*/

public class MonitoredWriter extends Writer {

Writer writer;
int totalWrittenSize;

/**
* constructor that wraps the original writer object
*/

public MonitoredWriter(Writer writer) {
this.writer = writer;
totalWrittenSize = 0;
}

/**
* The method to call by the user to write the data. We will just calculate the amount
* of data written here.
*
* @param cBuf Array of characters
* @param off Offset from which to start writing characters
* @param len Number of characters to write
*
* @throws java.io.IOException If an I/O error occurs
*/

public void write(char cbuf[], int off, int len) throws IOException {
totalWrittenSize += (len - off);
writer.write(cbuf, off, len);
}

/**
* Method to call after finishing writing the data. We will just pass the call to the
* original writer
*/

public void close() throws IOException {
writer.close();
}

/**
* flush already written data. Here also we just pass the call to the original writer
*/

public void flush() throws IOException {
writer.flush();
}

/**
* Custom method that will return the total size of written data
*
* @return the size of the data written
*/

public int getTotalWrittenSize() {
return totalWrittenSize;
}
}

Here is how it is used in measuring the writer size.
// measuring writer size - memory efficient method

// just wrap the original writer with our custom writer
MonitoredWriter monitoredWriter = new MonitoredWriter(writer);

// pass our custom wrter to the real work

doRealWork(monitoredWriter);

// get the size written in the real work
int size = monitoredWriter.getTotalWrittenSize();

Anyway like every good methods, there are drawbacks of using these methods to measure the data size on Reader and Writer objects.

If we take measuring the bandwidth consumed by a reader in a remote interface, this gives a slightly low value because this particular code only provide the size of the data read by the end user application and not by the network hardware layers. But actually these low layers read more data and keep it in a buffer which is not measured here. But if we assume that most of the time the end user application read all the data from the reader (and very rarely read portion of data and give up), this give nearly accurate value.

The other drawback could be the performance degradation  by wrapping the reader/writer with our custom implementation. But mostly reader and writers are used in IO bound operations (like to read through network or files), so going through an another layer does really little effect to the overall performance. And after all the 'Observer effect theory' says we can't measure anything without causing any effect to the actual cause...

Thursday, January 07, 2010

Writing Business Rules in WSO2 Carbon Platform

If you want to write rules in a Java program you have lot of choices. You can use a third party library like Drools or use the JAVA built-in JSR-94 reference implementation. In WSO2 Carbon, there is a component that abstract the behaviour of different rule engine and give you a unified API. Currently it has plugged into Drools and JAVA built-in JSR-94 implementation.

The rule component in WSO2 Carbon platform mainly used by WSO2 ESB product to mediate messages according to the given business rules. But the component is written to facilitate any requirement of using business rules in WSO2 Carbon platform. I had such a requirement in past few days and manage to use the rule component easily with the help of the component author, indika@wso2.com. So I thought it is worth sharing my experience in here.

Here You will be preparing the following stuff.

1. Rule configuration -

We can use this to provide the information about the rule implementation we are going to use, the rules (You can write rules inline or provide a reference to an external file) and the input and output adapter information.
<configuration xmlns="http://www.wso2.org/products/rule/drools">
<executionSet uri="simpleItemRuleXML">
<source key="file:src/test/resources/rules/simple-rules.drl"/>

<!-- <source>

<x><![CDATA[
rule InvokeABC
// rules inbuilt to the rule conf
end

]]>
</x>
</source> -->
<creation>
<property name="source" value="drl"/>

</creation>
</executionSet>
<session type="stateless"/>
<input name="facts" type="itemData" key="dataContext"></input>

<output name="results" type="itemData" key="dataContext"></output>
</configuration>



2. The Rules  -

You can write rules inline in the above configuration or put it in a file and refer it from a key which can be refered from the ResourceHelper (described below).
import java.util.Calendar;

rule YearEndDiscount
when
$item : org.test.pojo.SimpleItem(price > 100 )

then

Calendar calendar = Calendar.getInstance();
if (calendar.get(Calendar.MONTH) == Calendar.JANUARY) {

$item.setPrice($item.getPrice() * 80/100);
}

end

3. Data Context -

The context object that can be used to feed and retrieve data from and to rule engine. Here is the data context for my application.
public class SimpleDataContext {

public List<NameValuePair> getInput() {

// in reality the data will be retrieve from a database or some datasource
List<NameValuePair> itemPairList = new ArrayList<NameValuePair>();
SimpleItem item1 = new SimpleItem();
item1.setName("item1");
item1.setPrice(50);
itemPairList.add(new NameValuePair(item1.getName(), item1));

SimpleItem item2 = new SimpleItem();
item2.setName("item2");
item2.setPrice(120);
itemPairList.add(new NameValuePair(item2.getName(), item2));

SimpleItem item3 = new SimpleItem();
item3.setName("item3");
item3.setPrice(130);
itemPairList.add(new NameValuePair(item3.getName(), item3));

return itemPairList;
}

public void setResult(Object result) {

if (!(result instanceof SimpleItem)) {
System.out.println("it is not a SimpleItem");
}

SimpleItem item = (SimpleItem)result;
System.out.println("Item: " + item.getName() + ", Price: " + item.getPrice());
}

}

And the Item I'm going to manipulate using rule is a simple bean like this,
public class SimpleItem {
String name;
int price;
public String getName() {

return name;
}

public void setName(String name) {

this.name = name;
}

public int getPrice() {

return price;
}

public void setPrice(int price) {

this.price = price;
}
}

4. Data Adapter

You have to adapt the input and output with the rule engine. Mostly here you only have to wrap the data context. The advantage of having the data adapter is, a data adapter always associated with a input/output type. So in the rule configuration I can provide the type for the input and output. If you see my rule configuration above, you see the input/output type is marked as "ItemData". Here is my custom data adapter that is associated with the "itemData" type.
public class SimpleDataAdapter implements
ResourceAdapter, InputAdaptable, OutputAdaptable {

// the type associated with the adapter
private final static String TYPE = "itemData";
public String getType() {

return TYPE;
}

public Object adaptInput(ResourceDescription resourceDescription, Object tobeadapted) {

if (!(tobeadapted instanceof SimpleDataContext)) {
return null;
}

SimpleDataContext dataContext = (SimpleDataContext)tobeadapted;
return dataContext.getInput();
}

public boolean adaptOutput(ResourceDescription description,
Object value,
Object context,
ResourceHelper resourceHelper) {

if (!(context instanceof SimpleDataContext)) {
return false;
}

((SimpleDataContext)context).setResult(value);
return true;
}

public boolean canAdapt(ResourceDescription description, Object ouptput) {
String key = description.getKey();
return key != null && !"".equals(key);
}

}

5. Resource Helper

Resource Helper will map the keys refered from the configuration to JAVA objects. This is mostly used in mediation rule configurations which can extract the message data using a key or an xpath. In this example, we don't have much keys refering from the configuration only the rule file and the data context.
public class SimpleResourceHelper extends ResourceHelper {

public ReturnValue findByKey(String key, Object source, Object defaultValue) {

if (!(source instanceof SimpleDataContext)) {
return new ReturnValue(defaultValue);
}

SimpleDataContext dataContext = (SimpleDataContext)source;
if (key.startsWith("file:")) {

String filename = key.substring("file:".length());
try {

BufferedInputStream in = new BufferedInputStream(new FileInputStream(filename));
return new ReturnValue(in);
} catch (Exception e) {

return new ReturnValue(defaultValue);
}
}
if (key.startsWith("dataContext")) {

return new ReturnValue(dataContext);
}
return new ReturnValue(defaultValue);
}

// there are few more methods to be implemented, which can just leave not implemented for this example
}
}

That is all the accessories. Now you will be able to write the rule engine execution code.
File ruleConfigFile = new File(ruleConfigFilename);
XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(new FileInputStream(ruleConfigFile));

//create the builder
StAXOMBuilder builder = new StAXOMBuilder(parser);
//get the root element (in this case the envelope)

OMElement ruleConfig = builder.getDocumentElement();
EngineConfiguration configuration =
new EngineConfigurationFactory().create(ruleConfig, new AXIOMXPathFactory());

EngineController
engineController = new EngineController(configuration, new SimpleResourceHelper());
final ResourceAdapterFactory factory = ResourceAdapterFactory.getInstance();

ResourceAdapter adapter = new SimpleDataAdapter();
String adapterType = adapter.getType();
if (!factory.containsResourceAdapter(adapterType)) {

factory.addResourceAdapter(adapter);
}

SimpleDataContext simpleContext = new SimpleDataContext();

if (!engineController.isInitialized()) {
engineController.init(simpleContext);

}

if (engineController.isInitialized()) {
engineController.execute(simpleContext, simpleContext);

}