Friday, April 30, 2010

Mediator to support Database stored procedure

Following blogpost shows how to use Apache Synapse's dblookup mediator to support a mysql database stored procedure.

Setting up mysql database server

1. Install mysql server.
heshan@heshan-laptop:~$ apt-get install mysql

2. Connect to mysql server.
heshan@heshan-laptop:~$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 300
Server version: 5.0.75-0ubuntu10.3 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>

3. Create a sample databasae
mysql>CREATE DATABASE comp;

4. Create a table using the following statement.
mysql>CREATE TABLE company(name varchar(10), id varchar(10), price double);

5. Inserts some data using following statements
mysql>INSERT INTO company VALUES ('IBM','c1',3.7563);
mysql>INSERT INTO company VALUES ('SUN','c2',3.2554);
mysql>INSERT INTO company VALUES ('MSFT','c3',0.0);

6. Create a Stored Procedure.
mysql>CREATE PROCEDURE getCompany(compName VARCHAR(10)) SELECT name, id, price FROM company WHERE name = compName;

7.Add mysql-connector-java-5.1.12-bin.jar to the classpath. This can be done by putting the jar into the Synapse lib directory.

Running the Synapse sample
1. Save the following synapse-configuration as synapse_sample_364.xml to the samples folder.
SYNAPSE_HOME/repository/conf/sample/synapse_sample_364.xml

2. Synapse configuration.
<!-- SYNAPSE_HOME/repository/conf/sample/synapse_sample_364.xml -->
<definitions xmlns="http://synapse.apache.org/ns/2010/04/configuration">

<sequence name="myFaultHandler">
<makefault response="true">
<code value="tns:Receiver" xmlns:tns="http://www.w3.org/2003/05/soap-envelope"/>
<reason expression="get-property('ERROR_MESSAGE')"/>
</makefault>
<send/>
<drop/>
</sequence>

<sequence name="main" onError="myFaultHandler">
<in>
<log level="custom">
<property name="text"
value="** Looking up from the Database **"/>
</log>
<dblookup>
<connection>
<pool>
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://localhost/comp</url>
<user>root</user>
<password>admin</password>
</pool>
</connection>
<statement>
<sql>call getCompany(?)</sql>
<parameter expression="//m0:getQuote/m0:request/m0:symbol"
xmlns:m0="http://services.samples" type="VARCHAR"/>
<result name="company_id" column="id"/>
</statement>
</dblookup>

<switch source="get-property('company_id')">
<case regex="c1">
<log level="custom">
<property name="text"
expression="fn:concat('Company ID - ',get-property('company_id'))"/>
</log>
<send>
<endpoint>
<address uri="http://localhost:9000/services/SimpleStockQuoteService"/>
</endpoint>
</send>
</case>
<case regex="c2">
<log level="custom">
<property name="text"
expression="fn:concat('Company ID - ',get-property('company_id'))"/>
</log>
<send>
<endpoint>
<address uri="http://localhost:9000/services/SimpleStockQuoteService"/>
</endpoint>
</send>
</case>
<case regex="c3">
<log level="custom">
<property name="text"
expression="fn:concat('Company ID - ',get-property('company_id'))"/>
</log>
<send>
<endpoint>
<address uri="http://localhost:9000/services/SimpleStockQuoteService"/>
</endpoint>
</send>
</case>
<default>
<log level="custom">
<property name="text" value="** Unrecognized Company ID **"/>
</log>
<makefault response="true">
<code value="tns:Receiver"
xmlns:tns="http://www.w3.org/2003/05/soap-envelope"/>
<reason value="** Unrecognized Company ID **"/>
</makefault>
<send/>
<drop/>
</default>
</switch>
<drop/>
</in>

<out>
<send/>
</out>

</sequence>

</definitions>

3. Start the Synapse configuration numbered 364.
i.e. synapse -sample 364

4. Start the Axis2 server and deploy the SimpleStockQuoteService if not already done.

5. Run the client.
ant stockquote -Daddurl=http://localhost:9000/soap/SimpleStockQuoteService -Dtrpurl=http://localhost:8280/ -Dsymbol=IBM 

6. Voila, you have invoked a database stored procedure.

Tuesday, April 27, 2010

Running Synapse ESB's Script Mediator with include option

Apache Synapse ESB's Script Mediator can be used for message mediation. Sample 350 of Synapse samples is demonstrating how this Script Mediator can be used in a real world scenario.

Suppose the script you specified is calling a function of another script. Then the latter script should also be included in the script mediator configuration. It's done using the <include> element. The key attribute of the <include> element can be specified with the script program statements stored in a separate file, which is referenced via the local or remote registry entry. Following example shows how it can be done.
<script key="string" language="string" [function="script-function-name"]>
<include key="string"/>
</script>
I am using the same example used in Sample 350 to demonstrate this scenario.

1) Replace the stockquoteTransform.js with the following javascript.

// stockquoteTransform.js
function transformRequest(mc) {
transformRequestFunction(mc);
}

function transformResponse(mc) {
transformResponseFunction(mc);
}

2) Add the following sample.js file to SYNAPSE_HOME/repository/samples/resources/script

// sample.js
function transformRequestFunction(mc) {
var symbol = mc.getPayloadXML()..*::Code.toString();
mc.setPayloadXML(
<m:getquote m="http://services.samples">
<m:request>
<m:symbol>{symbol}</m:symbol>
</m:request>
</m:getquote>);
}

function transformResponse(mc) {
var symbol = mc.getPayloadXML()..*::symbol.toString();
var price = mc.getPayloadXML()..*::last.toString();
mc.setPayloadXML(
<m:checkpriceresponse m="http://services.samples/xsd">
<m:code>{symbol}</m:code>
<m:price>{price}</m:price>
</m:checkpriceresponse>);
}

3) Update the synapse configuration with the following configuration.

<definitions xmlns="http://ws.apache.org/ns/synapse">
<localEntry key="stockquoteScript" src="file:repository/conf/sample/resources/script/stockquoteTransform.js"/>
<localEntry key="sampleScript" src="file:repository/samples/resources/script/sample.js"/>

<in>
<!-- transform the custom quote request into a standard quote request expected by the service -->
<script language="js" key="stockquoteScript" function="transformRequest">
<include key="sampleScript"/>
<script>
<send>
<endpoint>
<address uri="http://localhost:9000/soap/SimpleStockQuoteService"/>
</endpoint>
</send>
</in>
<out>
<!-- transform the standard response back into the custom format the client expects -->
<script language="js" key="stockquoteScript" function="transformResponse"/>
<include key="sampleScript"/>
<script>
<send/>
</out>
</definitions>

4) Start the Synapse ESB with the above configuration. Deploy the SimpleStockQuote service (which is shipped with Synapse) on SimpleAxisServer. Then invoke the client giving the following command.

ant stockquote -Daddurl=http://localhost:9000/services/SimpleStockQuoteService -Dtrpurl=http://localhost:8280/ -Dmode=customquote

5) It will yield the following result.
   [java] Custom :: Stock price = $161.76045110619708 

6) For more information.
WSO2 ESB articles - http://wso2.org/library/esb
Apache Synapse - http://synapse.apache.org/

Monday, April 5, 2010

Recursively remove .svn files

Following command can be used to recursively remove .svn files.

Following command can be used to list out .svn files.

find . -type d -name .svn

Following command can be used to remove all .svn files.
 
rm -rf `find . -type d -name .svn`

Friday, April 2, 2010

Using Event Broker in WSO2 Carbon to generate Events

Following code/configuration show what are required to make use of the event broker in carbon to generate events. In the following example we are looking at how to generate an email notification.

1). Declarative Service Component to obtain the service URL.

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.engine.AxisConfiguration;
import org.apache.axis2.engine.ListenerManager;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.AxisFault;
import org.apache.log4j.*;
import org.osgi.service.component.ComponentContext;
import org.osgi.framework.BundleContext;
import org.wso2.carbon.utils.ConfigurationContextService;
import org.wso2.carbon.utils.NetworkUtils;
import org.wso2.carbon.eventing.broker.services.EventBrokerService;

import java.net.SocketException;

/**
* @scr.component name="org.wso2.carbon.health.service" immediate="true"
* @scr.reference name="configuration.context.service"
* interface="org.wso2.carbon.utils.ConfigurationContextService" cardinality="1..1"
* policy="dynamic" bind="setConfigurationContextService" unbind="unsetConfigurationContextService"
* @scr.reference name="listener.manager.service"
* interface="org.apache.axis2.engine.ListenerManager" cardinality="0..1" policy="dynamic"
* bind="setListenerManager" unbind="unsetListenerManager"
* @scr.reference name="eventbroker.service"
* interface="org.wso2.carbon.eventing.broker.services.EventBrokerService"
* cardinality="1..1" policy="dynamic" target="(name=HealthMonitorEventBroker)"
* bind="setHealthMonitorEventBrokerService" unbind="unsetHealthMonitorEventBrokerService"
*/
public class HealthMonitorEventingServiceComponent {
private static Log log = LogFactory.getLog(HealthMonitorEventingServiceComponent.class);

private boolean configurationDone = false;

private ConfigurationContextService configurationContextService = null;

private ListenerManager listenerManager = null;

private boolean initialized = false;

private static EventBrokerService healthMonitorEventBrokerService = null;

private String endpoint = null;

private static BundleContext bundleContext = null;

static Logger logger = Logger.getLogger(HealthMonitorEventingServiceComponent.class);

Logger rootLogger = LogManager.getRootLogger();

protected void activate(ComponentContext context) {
bundleContext = context.getBundleContext();
}

public static BundleContext getBundleContext() {
return bundleContext;
}

private void initializeAppender() {
if (listenerManager == null || healthMonitorEventBrokerService == null) {
return;
}
System.out.println("Carbon Health Monitor started!");

rootLogger.addAppender(new JiraAppender());
log.info("Jira appender added to the root logger");
// Setting level of root logger
//rootLogger.setLevel(Level.DEBUG);

addAppenders();
rootLogger.error("This log message is used to trigger the JiraAppender");
}

public void addAppenders() {
SimpleLayout layout = new SimpleLayout();
FileAppender appender = null;
try {
appender = new FileAppender(layout, "/tmp/output1.txt", false);
} catch (Exception e) {
logger.error(e);
}

rootLogger.addAppender(appender);
logger.debug("DEBUG message 2");
logger.debug("DEBUG message 3");
log.info("Appender added to the logger");
}

protected void deactivate(ComponentContext context) {
}

protected void setConfigurationContextService(ConfigurationContextService configurationContextService) {
this.configurationContextService = configurationContextService;
}

protected void unsetConfigurationContextService(ConfigurationContextService configurationContextService) {
}

protected void setHealthMonitorEventBrokerService(EventBrokerService healthMonitorEventBrokerService) {
this.healthMonitorEventBrokerService = healthMonitorEventBrokerService;
initializeAppender();
}

protected void unsetHealthMonitorEventBrokerService(EventBrokerService healthMonitorEventBrokerService) {
this.healthMonitorEventBrokerService = null;
}

protected void setListenerManager(ListenerManager listenerManager) {
this.listenerManager = listenerManager;
initialize();
initializeAppender();
}

protected void unsetListenerManager(ListenerManager listenerManager) {
this.listenerManager = null;
}

private void initialize() {
ConfigurationContext serverConfigurationContext = configurationContextService.getServerConfigContext();
if (!configurationDone && listenerManager != null) {
String host = null;
try {
host = NetworkUtils.getLocalHostname();
} catch (SocketException e) { }
if (serverConfigurationContext != null) {
AxisConfiguration config = serverConfigurationContext.getAxisConfiguration();
if (config != null && config.getTransportIn("http") != null &&
config.getTransportIn("http").getReceiver() != null) {
try {
EndpointReference[] eprArray = config.getTransportIn("http")
.getReceiver().getEPRsForService("HealthMonitorEventingService",
host);
if (eprArray != null && eprArray[0] != null) {
endpoint = eprArray[0].getAddress();
}
} catch (AxisFault e) { }
}
}
configurationDone = true;
}
}

public static EventBrokerService getHealthMonitorEventBrokerService() {
return healthMonitorEventBrokerService;
}
}


2). Generating Events.

OMFactory fac = OMAbstractFactory.getOMFactory();
Event<OMElement> event = new Event<OMElement>(zipElement);
event.setTopic("/mail-sending/mail");
OMElement topic = EventBrokerUtils.buildTopic(fac,event);
EventBrokerUtils.generateEvent(event.getMessage(), topic, HealthMonitorEventingServiceComponent.getHealthMonitorEventBrokerService());


3). The services.xml for the service.

<serviceGroup>
<service name="HealthMonitorEventingService" targetNamespace="http://eventing.registry.carbon.wso2.org">
<transports>
<transport>http</transport>
</transports>
<description>
Health Monitor Eventing Service
</description>
<messageReceivers>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only"
class="org.wso2.carbon.eventing.broker.receivers.CarbonEventingMessageReceiver"/>
<messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.wso2.carbon.eventing.broker.receivers.CarbonEventingMessageReceiver"/>
</messageReceivers>
<parameter name="enableSubscribe" locked="true">true</parameter>
<operation name="Subscribe" mep="http://www.w3.org/ns/wsdl/in-out">
<actionMapping>http://schemas.xmlsoap.org/ws/2004/08/eventing/Subscribe</actionMapping>
</operation>
<parameter name="enableRenew" locked="true">false</parameter>
<!--operation name="Renew" mep="http://www.w3.org/ns/wsdl/in-out">
<actionMapping>http://schemas.xmlsoap.org/ws/2004/08/eventing/Renew</actionMapping>
</operation-->
<parameter name="enableUnsubscribe" locked="true">false</parameter>
<!--operation name="Unsubscribe" mep="http://www.w3.org/ns/wsdl/in-out">
<actionMapping>http://schemas.xmlsoap.org/ws/2004/08/eventing/Unsubscribe</actionMapping>
</operation-->
<parameter name="enableGetStatus" locked="true">false</parameter>
<!--operation name="GetStatus" mep="http://www.w3.org/ns/wsdl/in-out">
<actionMapping>http://schemas.xmlsoap.org/ws/2004/08/eventing/GetStatus</actionMapping>
</operation-->
<operation name="Publish" mep="http://www.w3.org/ns/wsdl/in-out">
<actionMapping>http://ws.apache.org/ws/2007/05/eventing-extended/Publish</actionMapping>
</operation>

<parameter name="eventBrokerInstance" locked="true">HealthMonitorEventBroker</parameter>
</service>
<parameter name="hiddenService" locked="true">true</parameter>
</serviceGroup>


4). Event Broker configuration

<eventBroker xmlns="http://wso2.org/ns/2009/09/eventing">
<eventStream name="HealthMonitorEventBroker">
<subscriptionManager class="org.wso2.carbon.eventing.impl.EmbeddedRegistryBasedSubscriptionManager">
<parameter name="topicHeaderName">topic</parameter>
<parameter name="topicHeaderNS">http://wso2.org/ns/2009/09/eventing/notify</parameter>
<parameter name="subscriptionStoragePath">/carbon/eventing/registry</parameter>
</subscriptionManager>
<!-- Uncomment to to RemoteRegistryBasedSubscriptionManager -->
<!--subscriptionManager class="org.wso2.carbon.eventing.impl.RemoteRegistryBasedSubscriptionManager">
<parameter name="topicHeaderName">topic</parameter>
<parameter name="topicHeaderNS">http://wso2.org/ns/2009/09/eventing/notify</parameter>
<parameter name="subscriptionStoragePath">/carbon/eventing/registry</parameter>
<parameter name="registryURL">http://remote-ip:port/registry/</parameter>
<parameter name="username">username</parameter>
<parameter name="password">password</parameter>
</subscriptionManager-->
<!--<eventDispatcher>org.wso2.carbon.registry.eventing.RegistryEventDispatcher</eventDispatcher>-->
<eventDispatcher>org.wso2.carbon.eventing.broker.CarbonEventDispatcher</eventDispatcher>
<notificationManager class="org.wso2.carbon.eventing.broker.CarbonNotificationManager">
<parameter name="minSpareThreads">25</parameter>
<parameter name="maxThreads">150</parameter>
<parameter name="maxQueuedRequests">100</parameter>
<!-- Keep Alive time in nano seconds -->
<parameter name="keepAliveTime">1000</parameter>
<!-- Specify path of security policy file to enable security. -->
<!--parameter name="securityPolicy">policypath</parameter-->

<!-- Parameters specific to the Registry Event Broker configuration -->
<!-- Set this as false to disable displaying of registry URL in notification e-mails -->
<parameter name="showRegistryURL" locked="true">true</parameter>
</notificationManager>
</eventStream>
</eventBroker>


I have used the above method to generate email notifications within the Carbon Health Monitor component which I developed. More info on Health Monitor component will be discussed later.

Convert String to a InputStream

Following code snippet show how to $subject.

import java.io.UnsupportedEncodingException;
import java.io.ByteArrayInputStream;
import java.io.InputStream;

public class StringSample {
public static void main(String[] args) {
String textInput = "Convert String to InputStream";
try {
InputStream is = new ByteArrayInputStream(textInput.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}

Programmetically add Log4J Appenders

Following code snippet shows how to programmetically add log4j Appenders to your java code.

import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.SimpleLayout;

public class SampleLoggingAppender {
static Logger logger = Logger.getLogger(SampleLoggingAppender.class);

public static void main(String args[]) {
SimpleLayout layout = new SimpleLayout();
FileAppender fileAppender = null;
try {
fileAppender = new FileAppender(layout, "log.txt", false);
} catch (Exception ignored) {
}

ConsoleAppender consoleAppender = new ConsoleAppender(new PatternLayout());

logger.addAppender(fileAppender);
logger.addAppender(consoleAppender);
logger.setLevel(Level.DEBUG);

logger.debug("DEBUG");
logger.info("INFO");
logger.warn("WARN");
logger.error("ERROR");
logger.fatal("FATAL");
}
}