JMS in JDeveloper 11g and WebLogic 10.3

Edwin Biemond | Oct 10, 2008 16:50 -0600

JMS is a bit different in JDeveloper 11G and WebLogic 10.3 then when you use OC4J 10.1.3. This blog will show you how you can create a Queue and Connection Factory in WebLogic and use this in one of your JDeveloper 11g projects.
First you have to add two libraries to the project, The first is the AQJMS library ( even when we don't use AQ ) and the second library is the WebLogic 10.3 thin Client

Start the weblogic server. Menu Run , Start Server Instance


The url is http://localhost:7101/console. Username weblogic password weblogic.


Default the weblogic server has an empty configuration. We need to create a new jms server with a database of file persistance. We need this for the queue or topic persistence

create a new jms system module. In this module we will create a new connection factory and queue
Select the just created jms module and create a new connection factory first.



Create a new queue

Make sure that the queue uses the jms server. See the targets this can't be empty

And here is the java code to test the connection factory and the queue

The difference with oc4j is that you use other jndi properties. This are the right properties for WebLogic 10.3 in JDeveloper 11g

java.naming.factory.initial weblogic.jndi.WLInitialContextFactory
java.naming.provider.url t3://localhost:7101
java.naming.security.principal weblogic
java.naming.security.credentials weblogic




package nl.ordina.jms;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.Properties;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;

public class WeblogicClient {

private QueueConnection connection = null;
private QueueSession session = null;
private QueueSender sender = null;
private QueueReceiver receiver = null;
private Queue queue = null;
private long waitTime = 0;


public WeblogicClient() {
setUp();
put();
get();
tearDown();
}

public static void main(String[] args) {
WeblogicClient weblogicClient = new WeblogicClient();
}

public void tearDown() {
try {
sender.close();
receiver.close();
session.close();
connection.close();

} catch (JMSException je) {
je.printStackTrace();
} finally {
}
}

public void get(){
try {
javax.jms.TextMessage textMessage = (javax.jms.TextMessage)receiver.receive();
System.out.println("Receiving message [" + textMessage.getJMSMessageID() + "] enqueued at " + new Timestamp(textMessage.getJMSTimestamp()).toString());
String xmlText = textMessage.getText();
System.out.println(xmlText);
} catch (JMSException jmse) {
jmse.printStackTrace();
}
}

public void put(){
String messageId = null;
String xmlData = "";
FileInputStream fis;
try {
fis = new FileInputStream("D:\\projecten\\mhs_esb\\delfor.xml");
int x= fis.available();
byte b[]= new byte[x];
fis.read(b);
xmlData = new String(b);
} catch (FileNotFoundException e) {
// TODO
} catch (IOException e) {
// TODO
}
try {
TextMessage message = session.createTextMessage(xmlData);
sender.send(message);
} catch (JMSException jmse) {
jmse.printStackTrace();
}
}

protected void setUp() {

String queueName = "jms/QTest";
String queueConnectionFactoryName = "jms/CFTest";
Context ctx;

try {
Properties parm = new Properties();
parm.setProperty("java.naming.factory.initial","weblogic.jndi.WLInitialContextFactory");
parm.setProperty("java.naming.provider.url","t3://localhost:7101");
parm.setProperty("java.naming.security.principal","weblogic");
parm.setProperty("java.naming.security.credentials","weblogic");

ctx = new InitialContext(parm);

QueueConnectionFactory connectionFactory =
(QueueConnectionFactory)ctx.lookup(queueConnectionFactoryName);

connection = connectionFactory.createQueueConnection();
connection.start();
session = connection.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE);
queue = (Queue)ctx.lookup(queueName);
sender = session.createSender(queue);
receiver = session.createReceiver(queue);


} catch (JMSException je) {
throw new RuntimeException("Fout opgetreden bij het starten ",je);
} catch (Throwable t) {
throw new RuntimeException("Fout opgetreden bij het starten ",t);

}
}

}


Group column in an ADF Rich Table

Edwin Biemond | Oct 8, 2008 17:50 -0600

I noticed a small new group feature in the new JDeveloper 11G Rich Table component. You can group table columns together in a group column. Here you see an example of a rich table with two groups.

You can achieve this by dragging a viewobject from the datacontrol and selecting a table layout. Just select a few columns and press the group button.
JDeveloper creates a new column with the selected employee columns as childern

Debugging Task Flow in JDeveloper 11G

Edwin Biemond | Oct 7, 2008 17:10 -0600

With the final release of JDeveloper 11g we can debug Task Flows. This is a major step especially when you realize it is just xml definitions.
To debug a Task Flow you only have to select a calling task flow, view, method or router in this Task Flow and use the right button to toggle the Breakpoint.


When Weblogic reaches a breakpoint you will return to JDeveloper where you will see the debugging information in one of the following tabs, Smart Data, Data or ADF Data or EL Evaluator.

Here I add a breakpoint to a router to see the result of the method. The method returns a pageflowscope variable.The value of this pageflowscope variable can be seen in the EL evaluator. In this it is false because the salary is higher then 2000.

I wish you happy Debugging.

Lookup Oracle database queue (AQ) with jndi and LDAP

Edwin Biemond | Oct 3, 2008 12:30 -0600

In my previous blog entry I explained how you can register tnsnames entries and database connections in OpenLDAP, this blog entry goes a little further. This blog explains how you can register Oracle database queues (AQ) in ldap and use them with JNDI. I don't use OID because the product takes too many resources. I just want to lookup some queues. So I downloaded openldap and create the oracle ldap schema and add this to openldap. Now I can register oracle object in LDAP and use it in java.

Here you see how my LDAP tree looks like. Click here to get the LDIF file
SCOTT.TEST is a queue called test in the scott oracle schema. SCOTT.TEST_TABLE is the queue table of the test queue.
The LDAP attributes of the queue entry and this queue has a pointer to the queue table (first attribute)
The queue table attributes

The database connection registration which you can use for the connection factory
Here is the plsql code to create the queue and queue table

begin
sys.dbms_aqadm.create_queue_table(
queue_table => 'TEST_TABLE',
queue_payload_type => 'SYS.AQ$_JMS_MESSAGE',
sort_list => 'PRIORITY',
compatible => '10.0.0',
primary_instance => 0,
secondary_instance => 0,
storage_clause => 'tablespace USERS pctfree 10 initrans 1 maxtrans 255 storage ( initial 64K minextents 1 maxextents unlimited )');
end;
/
begin
sys.dbms_aqadm.create_queue(
queue_name => 'TEST',
queue_table => 'TEST_TABLE',
queue_type => sys.dbms_aqadm.normal_queue,
max_retries => 5,
retry_delay => 0,
retention_time => 0);
end;
/

The java code where we do a lookup of the database connection to create the connection factory and do a lookup to create a queue.

package jms2;

import java.util.Hashtable;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;

import javax.jms.TextMessage;

import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;

//import oracle.jms.AQjmsOracleDebug;


public class jmsclient2 {

Hashtable env = null;
boolean envSet = false;
private QueueConnection connection = null;
private QueueSession session = null;
private QueueSender sender = null;
private QueueReceiver receiver = null;
private QueueConnectionFactory queueConnectionFact = null;;
private Queue queue = null;;


public void testRegistration() {
// AQjmsOracleDebug.setLogStream(out);
// AQjmsOracleDebug.setTraceLevel(AQjmsOracleDebug.AQ_ORA_TR6);
// AQjmsOracleDebug.setDebug(true);

env = new Hashtable(5, 0.75f);
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL , "ldap://localhost:389");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL , "o=sgi,c=us");
env.put(Context.SECURITY_CREDENTIALS , "secret");

DirContext dirContext = null;
try {
dirContext = new InitialDirContext(env);

dirContext=(DirContext)dirContext.lookup("cn=ORCL, cn=OracleContext, ou=Services, o=sgi,c=us");
DirContext destctxCF = (DirContext)dirContext.lookup("cn=oracledbconnections");
DirContext destctxQF = (DirContext)dirContext.lookup("cn=OracleDBQueues");

queueConnectionFact = (QueueConnectionFactory)destctxCF.lookup("cn=SCOTT");
queue = (Queue) destctxQF.lookup("cn=SCOTT.TEST");

connection = queueConnectionFact.createQueueConnection();
session = connection.createQueueSession(true, QueueSession.AUTO_ACKNOWLEDGE);
connection.start();

sender = session.createSender(queue);
String xmlData = "1111";
TextMessage message = session.createTextMessage(xmlData);
sender.send(message);

receiver = session.createReceiver(queue);
TextMessage textMessage = (javax.jms.TextMessage)receiver.receive();
String xmlText = textMessage.getText();
System.out.println(xmlText);


} catch (NamingException ne) {
ne.printStackTrace();
} catch (JMSException jmse) {
jmse.printStackTrace();
}
}

public static void main (String[] args) {
jmsclient2 client = new jmsclient2();
client.testRegistration();

}
}


That's all.

Generate Edifact message from a xml

Edwin Biemond | Sep 30, 2008 15:30 -0600

The people of DataDirect made a great product called XML convertors with this you can generate an Edifact message from a xml and back. This is not new but this java software can easily embedded in your own java program. In this blog entry I will show how you can generate an edifact message in a few hours and very flexible.

This are the steps to generate an APERAK Edifact message from an Oracle table.
Step 1 generate the xsd from xml convertor for our edifact message.
In this example I want to generate an Aperak Edifact message so I first need a xml schema,then I know how the xml must look like. You have to do this only once.

ConverterFactory factory = new ConverterFactory();
try {
Result xsdResult = new StreamResult("d:/projecten/xml_edi/aperak.xsd");
String uri = "EDI:dialect=EDIFACT:version=D96A:message=APERAK:tbl=no";
SchemaGenerator generator = factory.newSchemaGenerator(uri);
generator.getSchema(xsdResult);
System.out.println("schema generator finished: --> aperak.xsd");
}
catch (Exception e)
{
System.out.println("schema generator failed with exception: " + e);
}

This how the xsd looks like.

Step 2 generate a xml from the oracle database with xmldb
With Oracle xmldb we can generate the following xml from the database.

<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="db.xsd"
type="APERAK" date="080928" time="1347" runId="1001" test="true">
<records>
<aperak_rff nr="1" message_id="A_200508030000603782" ref_qlfier="AAN" ref_nr="T200508031110N002" message_function="27"/>
<aperak_nad nr="1" message_id="A_200508030000603782" code_ontv_afz="A" party_qlfier="GRP" ean_code_party="8716867999990"/>
<aperak_nad nr="2" message_id="A_200508030000603782" code_ontv_afz="O" party_qlfier="GRU" ean_code_party="8712423009097"/>
<aperak_dtm nr="1" message_id="A_200508030000603782" dt_qualifier="137" dt_period="200508031112" dt_format_qlfier="203"/>
<aperak_dtm nr="2" message_id="A_200508030000603782" dt_qualifier="178" dt_period="200508031113" dt_format_qlfier="203"/>
<aperak_dtm nr="3" message_id="A_200508030000603782" dt_qualifier="735" dt_period="2" dt_format_qlfier="805"/>
<aperak_erc nr="1" message_id="A_200508030000603782" error_code="00023" nr_text="1" volgnummer="505484510" error_text=" (E) "/>
</records>
</message>

You probably noticed I do not have all the required elements in this database xml to generate a complete edifact message. But we will solve this in the next step.
With Altova XMLSPY I will generate a new XSD based on this example xml.

Now we have two xsd's and a database xml, we can use Altova Mapforce to create a xslt which can transform the database xml to the more complex edifact aperak xml.
Step 3 Use Mapforce to create a XSLT.
I will use Altova Mapforce because with this program I can visual make a xslt and immediately see the result of the xlst. We need to import the two xsd's in mapforce and make the mappings between these two xsd's and add the missing data to the edifact aperak schema.

This is the output of the xslt.

<?xml version="1.0" encoding="UTF-8"?>
<EDIFACT xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="D:/projecten/xml_edi/aperak.xsd">
<UNB>
<UNB01>
<UNB0101>UNOC</UNB0101>
<UNB0102>3</UNB0102>
</UNB01>
<UNB02>
<UNB0201>8712423009097</UNB0201>
<UNB0202>14</UNB0202>
</UNB02>
<UNB03>
<UNB0301>8716867999990</UNB0301>
<UNB0302>14</UNB0302>
</UNB03>
<UNB04>
<UNB0401>080928</UNB0401>
<UNB0402>1347</UNB0402>
</UNB04>
<UNB05>1001</UNB05>
<UNB11>1</UNB11>
</UNB>
<APERAK>
<UNH>
<UNH01>1001</UNH01>
<UNH02>
<UNH0201>APERAK</UNH0201>
<UNH0202>D</UNH0202>
<UNH0203>96A</UNH0203>
<UNH0204>ZZ</UNH0204>
<UNH0205>EDINE1</UNH0205>
</UNH02>
</UNH>
<BGM>
<BGM01>
<BGM0101>12E</BGM0101>
<BGM0103>9</BGM0103>
</BGM01>
<BGM02>A_200508030000603782</BGM02>
<BGM03>27</BGM03>
</BGM>
<DTM>
<DTM01>
<DTM0101>137</DTM0101>
<DTM0102>200508031112</DTM0102>
<DTM0103>203</DTM0103>
</DTM01>
</DTM>
<DTM>
<DTM01>
<DTM0101>178</DTM0101>
<DTM0102>200508031113</DTM0102>
<DTM0103>203</DTM0103>
</DTM01>
</DTM>
<DTM>
<DTM01>
<DTM0101>735</DTM0101>
<DTM0102>2</DTM0102>
<DTM0103>805</DTM0103>
</DTM01>
</DTM>
<GROUP_1>
<RFF>
<RFF01>
<RFF0101>AAN</RFF0101>
<RFF0102>T200508031110N002</RFF0102>
</RFF01>
</RFF>
</GROUP_1>
<GROUP_2>
<NAD>
<NAD01>GRP</NAD01>
<NAD02>
<NAD0201>8716867999990</NAD0201>
<NAD0203>9</NAD0203>
</NAD02>
</NAD>
<NAD>
<NAD01>GRU</NAD01>
<NAD02>
<NAD0201>8712423009097</NAD0201>
<NAD0203>9</NAD0203>
</NAD02>
</NAD>
</GROUP_2>
<GROUP_3>
<ERC>
<ERC01>
<ERC0101>00023</ERC0101>
<ERC0103>60</ERC0103>
</ERC01>
</ERC>
<FTX>
<FTX01>AAO</FTX01>
<FTX04>
<FTX0401> (E) </FTX0401>
</FTX04>
</FTX>
</GROUP_3>
<UNT>
<UNT02>1001</UNT02>
</UNT>
</APERAK>
<UNZ>
<UNZ01>1</UNZ01>
<UNZ02>1001</UNZ02>
</UNZ>
</EDIFACT>

Now we can generate the database xml and have an xlst to transform the xml to the edifact aparak xml. In the last step we can make the edifact message with the xml convertor software.
Step 4 Generate the edifact message
What really cool is that xml convertor can detect the xml message and transforms it to the right edifact message so you only need this code for all your outgoing edifact messages.

try {
Source converterSource = new StreamSource("d:/projecten/xml_edi/aperak_gen.xml");
Result converterResult = new StreamResult("d:/projecten/xml_edi/aperak.edn3");
Converter fromXml = factory.newConvertFromXML("converter:EDI");
fromXml.convert(converterSource, converterResult);
System.out.println("fromXML finished: aperak.xml -> aperak.edn2");
}
catch (Exception e)
{
System.out.println("fromXML failed with exception: " + e);
}

This is the generated edifact message
UNA:+,? '
UNB+UNOC:3+8712423009097:14+8716867999990:14+080928:1347+1001++++++1'
UNH+1001+APERAK:D:96A:ZZ:EDINE1'
BGM+12E::9+A_200508030000603782+27'
DTM+137:200508031112:203'
DTM+178:200508031113:203'
DTM+735:2:805'
RFF+AAN:T200508031110N002'
NAD+GRP+8716867999990::9'
NAD+GRU+8712423009097::9'
ERC+00023::60'
FTX+AAO+++ (E)'
UNT+11+1001'
UNZ+1+1001'

Conclusion, this is an flexible solution to generate an Edifact message. In your java application you can easily embed the xml convertor software and use the xslt to transform the xml. With this solution you can change your backoffice application or edifact message. You only have to change the xslt which is very easy in Altova Mapforce.

Flex Ruby performance with XML, JSON and RubyAMF

Edwin Biemond | Sep 28, 2008 14:10 -0600

One of the great things when you use Flex in combination Ruby is that you can easily change the communication method. For instance you can use xml rpc or json rpc or RubyAMF, just by changing the ruby controller. Off course when you use json then you need to download the corelib for Flex and when you use RubyAMF we need to install this plugin in ruby.
But when you have a choice then you need to know what is the best communication method for your project. In this test I will measure the performance of the three methods. For this I made a simple Flex / Ruby project.

Here are the average results in ms (100 times executed) with the total records count in our test table.
records101004001000
amf159213380731
json4575252781
xml4357197612

Conclusion
RubyAMF has a little overhead and is fast with a large recordset and you can use remoteobject in Flex. Xml is fast but the performance can vary. With 1000 records it can take 200ms but sometimes 2000ms. It looks like Ruby can cache xml and sometimes refresh the cache. Json is very stable and fast with small recordsets. If you just want to retrieve some data in Flex, I would use json but when you want to do more with this data in Flex then RubyAMF has many benefits, like association between objects, conversion to actionscript class and a lot more.

The Ruby Code
you can test the output by adding the format parameter to the url http://localhost:3000/departments/find_all?format=json or format=xml.
Here is the controller code I used

class DepartmentsController < ApplicationController

# return all Departments
def find_all
respond_to do |format|
format.amf { render :amf => Department.find(:all) }
format.json { render :text => Department.find(:all).to_json }
format.xml { render :xml => Department.find(:all) }
end
end

end

department table created in a mysql database

class CreateDepartments < ActiveRecord::Migration

def self.up
create_table :departments do |t|
t.string :name
t.string :location
t.timestamps
end
end

def self.down
drop_table :departments
end
end

RubyAMF configuration

require 'app/configuration'
module RubyAMF
module Configuration
ClassMappings.ignore_fields = ['created_at','updated_at']
ClassMappings.translate_case = true
ClassMappings.assume_types = false
ParameterMappings.scaffolding = false

ClassMappings.register(
:actionscript => 'Department',
:ruby => 'Department',
:type => 'active_record',
:attributes => ["id", "name", "location", "created_at", "updated_at"])

ClassMappings.force_active_record_ids = true
ClassMappings.use_ruby_date_time = false
ClassMappings.use_array_collection = true
ClassMappings.check_for_associations = true
ParameterMappings.always_add_to_params = true
end
end


The Flex code

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import vo.Timing;

private var idNum:int;
private var maxNum:int = 100;

[Bindable]
private var timing:ArrayCollection = new ArrayCollection();

private function startRubyAMF():void {
timing = new ArrayCollection();
idNum = 0;
loadAll();
}
private function startJSON():void {
timing = new ArrayCollection();
idNum = 0;
loadAllJson();
}
private function startXML():void {
timing = new ArrayCollection();
idNum = 0;
loadAllXML();
}
]]>
</mx:Script>


<mx:Script>
<![CDATA[
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.controls.Alert;
import mx.collections.ArrayCollection;

[Bindable]
private var departments:ArrayCollection = new ArrayCollection();
private var rubyDateFrom:Date;

private function loadAll():void {
rubyDateFrom = new Date();
var token:AsyncToken = AsyncToken(departmentService.find_all());
token.kind = idNum.toString();

var time:Timing = new Timing();
time.id = idNum;
time.startDate = rubyDateFrom;
timing.addItem(time);

}

private function faultHandler(event:FaultEvent):void {
Alert.show(event.fault.faultString + " : " + event.fault.faultCode + " : " + event.fault.faultDetail , "Error in LoginCommand");
}

private function resultHandler(event:ResultEvent):void {
departments = event.result as ArrayCollection;
var rubyDateFinish:Number = new Date().valueOf() - rubyDateFrom.valueOf() ;
rubyLabel.text="RubyAMF departments "+rubyDateFinish.toString()+" ms";

var time:Timing = timing.getItemAt(event.token.kind) as Timing;
time.endDate = new Date();
time.rubyamf = rubyDateFinish;

idNum = idNum +1;
if (idNum < maxNum ) {
loadAll();
} else {
var average:Number = 0;
for ( var i:int = 0 ; i < timing.length ; i++ ) {
var time2:Timing = timing.getItemAt(i) as Timing;
average = average + time2.rubyamf;
}
average = average / timing.length;
rubyLabel.text="RubyAMF departments average "+average.toString()+" ms";

trace("end");
}
}

]]>
</mx:Script>
<mx:HBox>
<mx:Button label="refresh RubyAMF" click="startRubyAMF()"/>
<mx:Button label="refresh JSON" click="startJSON()"/>
<mx:Button label="refresh XML" click="startXML()"/>
</mx:HBox>

<mx:RemoteObject id="departmentService" destination="rubyamf"
endpoint="http://localhost:3000/rubyamf_gateway/"
source="DepartmentsController"
showBusyCursor="true"
result="resultHandler(event)"
fault="faultHandler(event)"
/>

<mx:Label id="rubyLabel" text="RubyAMF departments"/>

<mx:DataGrid id="dg" dataProvider="{departments}">
<mx:columns>
<mx:DataGridColumn dataField="id" headerText="Key"/>
<mx:DataGridColumn dataField="name" headerText="Name"/>
<mx:DataGridColumn dataField="location" headerText="Location"/>
</mx:columns>
</mx:DataGrid>

<mx:Script>
<![CDATA[
import com.adobe.serialization.json.JSON;
[Bindable]
private var dp:ArrayCollection = new ArrayCollection() ;
private var rubyJsonDateFrom:Date;

private function loadAllJson():void {
rubyJsonDateFrom = new Date();
var token:AsyncToken = AsyncToken(json.send());
token.kind = idNum.toString();

var time:Timing = new Timing();
time.id = idNum;
time.startDate = rubyJsonDateFrom;
timing.addItem(time);
}


private function resultHandlerJSON(event:ResultEvent):void
{
dp = new ArrayCollection();
var rawData:String = String(event.result);
var arr:Array = (JSON.decode(rawData) as Array);
for ( var i:int = 0 ; i < arr.length ; i++ ) {
dp.addItem(arr[i].department);
}
var rubyJsonDateFinish:Number = new Date().valueOf() - rubyJsonDateFrom.valueOf() ;
rubyJSONLabel.text="JSON departments "+rubyJsonDateFinish.toString()+" ms";

var time:Timing = timing.getItemAt(event.token.kind) as Timing;
time.endDate = new Date();
time.json = rubyJsonDateFinish;

idNum = idNum +1;
if (idNum < maxNum ) {
loadAllJson();
} else {
var average:Number = 0;
for ( var ii:int = 0 ; ii < timing.length ; ii++ ) {
var time2:Timing = timing.getItemAt(ii) as Timing;
average = average + time2.json;
}
average = average / timing.length;
rubyJSONLabel.text="JSON departments average "+average.toString()+" ms";

trace("end");
}


}
]]>
</mx:Script>

<mx:HTTPService id="json"
url="http://localhost:3000/departments/find_all?format=json"
result="resultHandlerJSON(event)" useProxy="false" />
<mx:Label id="rubyJSONLabel" text="JSON departments"/>
<mx:DataGrid id="dg4" dataProvider="{dp}">
<mx:columns>
<mx:DataGridColumn dataField="id" headerText="Key"/>
<mx:DataGridColumn dataField="name" headerText="Name"/>
<mx:DataGridColumn dataField="location" headerText="Location"/>
</mx:columns>
</mx:DataGrid>

<mx:Script>
<![CDATA[
import com.adobe.serialization.json.JSON;
[Bindable]
private var dp2:ArrayCollection = new ArrayCollection() ;
private var rubyXmlDateFrom:Date;

private function loadAllXML():void {
rubyXmlDateFrom = new Date();
var token:AsyncToken = AsyncToken(xml.send());
token.kind = idNum.toString();

var time:Timing = new Timing();
time.id = idNum;
time.startDate = rubyJsonDateFrom;
timing.addItem(time);


}

private function resultHandlerXML(event:ResultEvent):void
{
if ( event.result != null ) {
dp2 = event.result.departments.department;
}
var rubyXmlDateFinish:Number = new Date().valueOf() - rubyXmlDateFrom.valueOf() ;
rubyXmlLabel.text="XML departments "+rubyXmlDateFinish.toString()+" ms";

var time:Timing = timing.getItemAt(event.token.kind) as Timing;
time.endDate = new Date();
time.xml = rubyXmlDateFinish;

idNum = idNum +1;
if (idNum < maxNum ) {
loadAllXML();
} else {
var average:Number = 0;
for ( var ii:int = 0 ; ii < timing.length ; ii++ ) {
var time2:Timing = timing.getItemAt(ii) as Timing;
average = average + time2.xml;
}
average = average / timing.length;
rubyXmlLabel.text="XML departments average "+average.toString()+" ms";
trace("end");
}
}
]]>
</mx:Script>


<mx:HTTPService id="xml" url="http://localhost:3000/departments/find_all?format=xml"
result="resultHandlerXML(event)" useProxy="false" />
<mx:Label id="rubyXmlLabel" text="XML departments"/>
<mx:DataGrid id="dg5" dataProvider="{dp2}">
<mx:columns>
<mx:DataGridColumn dataField="id" headerText="Key"/>
<mx:DataGridColumn dataField="name" headerText="Name"/>
<mx:DataGridColumn dataField="location" headerText="Location"/>
</mx:columns>
</mx:DataGrid>

</mx:Application>

Google Maps for JSF (GMaps4JSF) in JDeveloper 11G

Edwin Biemond | Sep 23, 2008 15:50 -0600

A new version of GMaps4JSF was released. GMaps4JSF is a Google maps jsf component. This blog entry will show you how you can use it in your own 11g application.
In this example you can hide and show markers. Add markers from a backing bean and add an click event on these markers so when you click on a marker you will get an alert.

The first step to get this jsf component in JDeveloper 11g is to download the GMaps4JSF jar and import this jsp tag library in your own project.

You need your own api key. Use this url http://code.google.com/apis/maps/signup.html to get the api key
Now we are ready to use it. It is very important not to use af:document else you won't see a map. I will investigate this later. Change my api key in the javascript and use your own.
GMaps4JSF is a good start but it is not ready yet. You still need to write your own javascript and you can not click on a marker to see a description.For this you need to add an eventhandler and use javascript to fire something.

Here is the code for the JSF page.

<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
xmlns:yui="http://code.google.com/p/gmaps4jsf/">
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<body onunload="GUnload()">
<f:view>
<f:verbatim>
<![CDATA[
<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAM7FSGSscPTbXiOt1No2LPRRKSto2PHtB06oPKyRV-hhKjT8WCBSTy2uiK1M8ViuI9Xm8mMLb7d1aKQ"
type="text/javascript">
</script>
]]>
</f:verbatim>
<af:form id="form">
<af:panelGroupLayout>
<f:verbatim>
<![CDATA[
<script type="text/javascript">
function hideMarker() {
office1.hide();
office2.hide();
}

function showMarker() {
office1.show();
office2.show();
}

function marker1ClickHandler() {
alert("You clicked on the headoffice");
}

function marker2ClickHandler() {
alert("You clicked on a office");
}

</script>
]]>
</f:verbatim>
<af:panelHeader text="GMaps4JSF">
<input type="button" value="Hide Offices" onclick="hideMarker();"/>
<input type="button" value="Show Offices" onclick="showMarker();"/>
<yui:map width="500px" height="500px" zoom="7"
binding="#{GoogleBean.map}">
<yui:mapControl name="GLargeMapControl"
position="G_ANCHOR_BOTTOM_RIGHT"/>
<yui:mapControl name="GMapTypeControl"/>
</yui:map>
</af:panelHeader>
</af:panelGroupLayout>
</af:form>
</f:view>
</body>
</jsp:root>

The backing bean I used

package nl.ordina.google.backing;

import com.googlecode.gmaps4jsf.component.map.Map;
import com.googlecode.gmaps4jsf.component.marker.Marker;
import com.googlecode.gmaps4jsf.component.eventlistener.EventListener;

public class GoogleBean {
private Map map;

public void setMap(Map map) {
this.map = map;
map.setLatitude("52.05");
map.setLongitude("5.11");


Marker mark = new Marker();
mark.setLatitude("52.05");
mark.setLongitude("5.11");
mark.setJsVariable("office1");
mark.setId("mark1");
EventListener event = new EventListener();
event.setEventName("click");
event.setJsFunction("marker1ClickHandler");
mark.getChildren().add(event);
map.getChildren().add(mark);

Marker mark2 = new Marker();
mark2.setLatitude("53.19");
mark2.setLongitude("6.53");
mark2.setJsVariable("office2");
mark.setId("mark2");
EventListener event2 = new EventListener();
event2.setEventName("click");
event2.setJsFunction("marker2ClickHandler");
mark2.getChildren().add(event2);
map.getChildren().add(mark2);

}

public Map getMap() {
return map;
}

}

faces-config.xml

<?xml version="1.0" encoding="windows-1252"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee">
<application>
<default-render-kit-id>oracle.adf.rich</default-render-kit-id>
</application>
<managed-bean>
<managed-bean-name>GoogleBean</managed-bean-name>
<managed-bean-class>nl.ordina.google.backing.GoogleBean</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>

JDeveloper 11G boxer release will available on the 1st of October

Edwin Biemond | Sep 22, 2008 14:20 -0600

Today Ted Farrell annouced that JDeveloper 11G ( codename boxer ) will be released on the 1st of october. This release will contain the ADF JSF RichFaces components and the new TaskFlows. These 11g components are certified to run on Weblogic 10.3 . Next year Oracle will release the bulldog version with the WebCenter and Soa components.
For more info read Lucas blog entry http://technology.amis.nl/blog/?p=3478

Building a MySQL Cluster

Edwin Biemond | Sep 16, 2008 16:00 -0600

I am now building a Mail Handling System for a customer and one of the customer non functional requirement is high availibility. So I made a MySQL cluster. Now I can run at the same time two instances of this Mail Handling System in different sites. Now one site can fail and the other site can still work on. The MySQL cluster software make sure that the data is always stored, available and in sync on two nodes. Off course you can use Oracle Data guard or RAC but this is much easier and a lot cheaper.
I was suprised how easy it is to make a cluster. This blog entry will show you the steps.
I will use three x86 sun solaris 10 servers. Two servers are the storage nodes and one is the management node (The load of the management node is very low so you can easily add this to a existing server).

First I downloaded the MySQL 6.2.15 cluster edition from
http://dev.mysql.com/downloads/cluster/index.html#solaris_tar in my case the solaris software.

I don't use the default mysql folders. This are my mysql folder locations.
mysql folder /mysql/mysql
mysql data folder /mysql/mysql/data
mysql cluster folder /mysql/cluster

And I changed the default mysql parameters because we need to support big xml files.

Just change it or remove these parameters.

First step, installation of the mysql software.

Storage node ac-mhs20 (193.176.63.50)
mysql-ndb-1# mkdir /mysql
mysql-ndb-1# cd /mysql
mysql-ndb-1# put the mysql software in the /mysql folder
mysql-ndb-1# gunzip *.gz
mysql-ndb-1# tar xvf *.tar
mysql-ndb-1# rm *.tar
mysql-ndb-1# ln –s mysql-cluster-gpl-6.2.15-solaris10-i386 mysql
mysql-ndb-1# vi /etc/profile add PATH=$PATH:/mysql/mysql/bin

Let's make the mysql configuration file
mysql-ndb-1# vi /etc/my.cnf

[mysqld]
basedir=/mysql/mysql
datadir=/mysql/mysql/data
max_allowed_packet=64M
key_buffer_size=192M
table_cache=512
sort_buffer_size=12M
read_buffer_size=8M
wait_timeout=172800
interactive=172800


mysql-ndb-1# groupadd mysql
mysql-ndb-1# useradd -g mysql mysql
mysql-ndb-1# cd mysql ( /mysql/mysql )
mysql-ndb-1# scripts/mysql_install_db --user=mysql
mysql-ndb-1# chown -R root .
mysql-ndb-1# chown -R mysql data
mysql-ndb-1# chgrp -R mysql .
mysql-ndb-1# cp support-files/mysql.server /etc/init.d/mysql.server

change the mysqld_safe file located in /mysql/mysql/bin

first change
if test -f ./share/mysql/english/errmsg.sys -a -x ./bin/mysqld
to
if test -f ./share/english/errmsg.sys -a -x ./bin/mysqld

second change
elif test -f ./share/mysql/english/errmsg.sys -a -x ./libexec/mysqld
to
elif test -f ./share/english/errmsg.sys -a -x ./libexec/mysqld

Do the same steps for storage node two
Storage node ac-mhs21 (193.176.63.51)

Now we can put the mysql management software to the management server
Management server ac-mhs22 (193.176.63.52)

mysql-ndb-mgt # mkdir /mysql
mysql-ndb-mgt # cd /mysql
mysql-ndb-mgt # mkdir cluster
mysql-ndb-mgt # cd cluster
mysql-ndb-mgt # ftp or rcp /mysql/mysql/bin/ndb_mgm and ndb_mgmd from storage node A or B to /mysql/cluster folder of the management server
mysql-ndb-mgt # chmod u+x ndb_


Step 2 Add the cluster configuration

Management server ac-mhs22 (193.176.63.52)
Create a new file called config.ini and put this in the /mysql/cluster folder

[NDBD DEFAULT]
NoOfReplicas=2
DataDir=/mysql/cluster
DataMemory=80M
IndexMemory=18M

[MYSQLD DEFAULT]
[NDB_MGMD DEFAULT]
[TCP DEFAULT]

# Management Server
[NDB_MGMD]
id=1
HostName=193.176.63.52 # IP address of this server

# Storage Nodes
[NDBD]
id=2
HostName=193.176.63.50 # IP address of storage-node-1
DataDir= /mysql/cluster

[NDBD]
id=3
HostName=193.176.63.51 # IP address of storage-node-2
DataDir=/mysql/cluster

[MYSQLD]
[MYSQLD]
[MYSQLD]
[MYSQLD]

Storage node ac-mhs20 (193.176.63.50)
mysql-ndb-1# vi /etc/my.cnf and add this to it

ndbclusterndb-connectstring='host=193.176.63.52' # IP address of the management server
default-table-type=NDBCLUSTER
[mysql_cluster]ndb-connectstring='host=193.176.63.52' # IP address of the management


Storage node ac-mhs21 (193.176.63.51)
mysql-ndb-2# vi /etc/my.cnf and add this to it

ndbclusterndb-connectstring='host=193.176.63.52' # IP address of the management server
default-table-type=NDBCLUSTER
[mysql_cluster]ndb-connectstring='host=193.176.63.52' # IP address of the management



Step 3 Let's start the cluster

Management server ac-mhs22 (193.176.63.52)
mysql-ndb-mgt # cd /mysql/cluster
mysql-ndb-mgt # ./ndb_mgmd

Storage node ac-mhs20 (193.176.63.50)
mysql-ndb-1# cd /mysql/cluster
mysql-ndb-1# /mysql/mysql/bin/ndbd --initial
mysql-ndb-1# /etc/init.d/mysql.server start

Storage node ac-mhs21 (193.176.63.51)
mysql-ndb-2# cd /mysql/cluster
mysql-ndb-2# /mysql/mysql/bin/ndbd --initial
mysql-ndb-2# /etc/init.d/mysql.server start

Step 4 Check the cluster status

Management server ac-mhs22 (193.176.63.52)
Start the management console

mysql-ndb-mgt # cd /mysql/cluster
mysql-ndb-mgt # ndb_mgm
ndb_mgm> show

Step 5 Create a new database

Storage node ac-mhs20 (193.176.63.50)

mysql-ndb-1# mysql -u root
mysql-ndb-1# create database foo;
mysql-ndb-1# use foo;
mysql-ndb-1# create table test1 ( i int );
mysql-ndb-1# insert into test1 () values (1);

Storage node ac-mhs21 (193.176.63.51)

mysql-ndb-2# mysql -u root
mysql-ndb-2# use foo;
mysql-ndb-2# select * from test1;


That's all the cluster is working

For more info check this great white paper. http://www.lod.com/whitepapers/mysql-cluster-howto.html

JDeveloper 11g is Coming

Edwin Biemond | Sep 11, 2008 16:40 -0600

Today I heard some rumours that JDeveloper 11G ADF edition will be released this month. This is very great news. I thought that it will take at least another 6 months before Oracle will release JDeveloper 11G.
I saw today that a JDeveloper 11g ADF application works perfectly with BEA as the embedded application server in JDeveloper 11g. I must say the execution of a webapp in JDeveloper has changed a little bit. In OC4J you can run the webapp without a deployment. With BEA as embedded AS, JDeveloper has to create a war and ear file first and then these files are deployed to the BEA application server ( You have to start BEA first).