Pages

Monday, December 14, 2009

SCA Spring in Weblogic 10.3.2 & Soa Suite 11g Part 2

In my previous post I made a SCA Spring example and deployed this on Weblogic 10.3.2 Now it is time to use the same spring context and java sources in a Soa Suite 11g composite ( without any changes) . Keep in mind SCA Spring in WLS and the Spring Component in Soa Suite is still in preview and will be supported in Patch Set2 of Soa Suite 11g.
In this example I use the java Logger and BPEL component of Clemens OTN article.

First I copied the java sources from my Weblogic SCA Spring project to the Soa project and created for demo purposes two Spring Bean configurations.
The first has a bean with a service and a reference and the second one has only a bean with a service. ( Off course I can combine these two together in one spring bean configuration, but this is more fun)

The first spring bean configuration will just passthrough the java call. We need to remove the WS and EJB binding in the service and reference. ( Soa Suite don't need this )

<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca">
<!--Spring 2.5 Bean defintions go here-->
<sca:service target="loggerPassThrough" name="LogServiceWS"
type="nl.whitehorses.wls.sca.spring.ILoggerComponent">
</sca:service>

<bean class="nl.whitehorses.wls.sca.spring.LoggerPassThrough" name="loggerPassThrough">
<property name="reference" ref="loggerEJBReference" />
</bean>

<sca:reference name="loggerEJBReference"
type="nl.whitehorses.wls.sca.spring.ILoggerComponent">
</sca:reference>

</beans>

The second spring bean configuration is the java logger. Here we also have to remove the WS binding in the service

<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca">
<!--Spring 2.5 Bean defintions go here-->
<sca:service target="logger" name="LogServiceEJB"
type="nl.whitehorses.wls.sca.spring.ILoggerComponent">
</sca:service>

<bean class="nl.whitehorses.wls.sca.spring.LoggerComponentImpl" name="logger">
<property name="output" ref="loggerOutput" />
</bean>

<bean id="loggerOutput" class="nl.whitehorses.wls.sca.spring.LoggerOutput"></bean>

</beans>

Now the composite application looks like this. We need to add some wires between the BPEL and Spring Components.



Open the BPEL component and add an invoke to call the partnerlink and create the input and output variables.

Deploy the composite and test it in the Soa Suite. With this as result.
Conclusion: With the new SoaSuite Spring component it is relative easy to do a java call outs and you can exchange your SCA spring application from Weblogic to Soa Suite without any changes.

Here is my Soa Suite example workspace

Sunday, December 13, 2009

SCA Spring in Weblogic 10.3.2 & Soa Suite 11g

With Weblogic 10.3.2 ( part of Patch Set 1 of Fusion Middleware R1 ) Oracle released a preview of SCA Spring. For more info about this announcement see the blog of Raghav.
This is great news because everything what works in SCA Spring can be used without any problem in Soa Suite 11g. With Patch Set 2 SCA Spring will be supported in WLS or Soa Suite. For a Soa Suite Spring example see this blog of Clemens

To test this I made a small example based on the blog of Clemens. In this test I will call a logging webservice and this service will pass the call to a passthrough bean which has a reference to an logging EJB service which calls a logging bean and this bean has an other bean injected which does the output.

First deploy the weblogic-sca war as a library to the WLS server, you can find this war in wlserver_10.3/common/deployable-libraries/ folder and is called weblogic-sca-1.0.war

Create in JDeveloper a new web project ( no need for ADF ) . We don't need the spring jar or add some config in the web.xml ( this can be empty , don't need to add spring listener )

Add a library reference in your webapp to the weblogic-sca library, Do this in the weblogic.xml or weblogic-application.xml file
<?xml version = '1.0' encoding = 'windows-1252'?>
<weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-web-app http://www.bea.com/ns/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd"
                 xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app">
   <library-ref>
       <library-name>weblogic-sca</library-name>
   </library-ref>
</weblogic-web-app>

Create a spring from jdeveloper and call this file spring-context.xml and put this in META-INF/jsca folder ( in the src folder )
When you got experience with Apache Tuscany then this spring configuration is not so difficult.
<?xml version="1.0" encoding="windows-1252" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:sca="http://xmlns.oracle.com/weblogic/weblogic-sca"
      xmlns:wlsb="http://xmlns.oracle.com/weblogic/weblogic-sca-binding"
      xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://xmlns.oracle.com/weblogic/weblogic-sca http://xmlns.oracle.com/weblogic/weblogic-sca/1.0/weblogic-sca.xsd http://xmlns.oracle.com/weblogic/weblogic-sca-binding http://xmlns.oracle.com/weblogic/weblogic-sca-binding/1.0/weblogic-sca-binding.xsd">
 <!--Spring 2.5 Bean defintions go here-->
 <sca:service target="loggerPassThrough" name="LogServiceWS"
              type="nl.whitehorses.wls.sca.spring.ILoggerComponent">
   <wlsb:binding.ws xmlns="http://xmlns.oracle.com/sca/1.0"
               name="LoggerService_WS" uri="/LoggerService_Uri"
               port="LoggerService_PORT"/>
 </sca:service>

 <sca:reference name="loggerEJBReference"
 type="nl.whitehorses.wls.sca.spring.ILoggerComponent">
   <wlsb:binding.ejb uri="LoggerService_EJB30_JNDI" />
 </sca:reference>

 <bean class="nl.whitehorses.wls.sca.spring.LoggerPassThrough" name="loggerPassThrough">
    <property name="reference" ref="loggerEJBReference" />
 </bean>

 <sca:service target="logger" name="LogServiceEJB"
              type="nl.whitehorses.wls.sca.spring.ILoggerComponent">
   <wlsb:binding.ejb uri="LoggerService_EJB30_JNDI" remote="true"
                     name="LoggerService_EJB"/>
 </sca:service>

 <bean class="nl.whitehorses.wls.sca.spring.LoggerComponentImpl" name="logger">
    <property name="output" ref="loggerOutput" />
 </bean>

 <bean id="loggerOutput" class="nl.whitehorses.wls.sca.spring.LoggerOutput"></bean>

</beans>

Now we need to create a logger interface, this will be use by the WS & EJB services and references.
package nl.whitehorses.wls.sca.spring;

public interface ILoggerComponent
{
   
    /**
     * Log a message, including the originating component, its instance id and
     * a message.
     * @param pComponentName the name of the component that sends this log msg
     * @param pInstanceId the instanceId of the component instance
     * @param pMessage the message to be logged
     */
    public void log (String pComponentName,
                     String pInstanceId, String pMessage);
}

Create a passthrough class this will pass on the logger call to the ejb service.
package nl.whitehorses.wls.sca.spring;

public class LoggerPassThrough implements ILoggerComponent {
    public LoggerPassThrough() {
        super();
    }

    @Override
    public void log(String pComponentName, String pInstanceId,
                    String pMessage)  {
    
       reference.log(pComponentName, pInstanceId, pMessage);
    }

    private ILoggerComponent reference;

    public void setReference(ILoggerComponent reference) {
        this.reference = reference;
    }

    public ILoggerComponent getReference() {
        return reference;
    }

}

Create the logger implementation class.
package nl.whitehorses.wls.sca.spring;

public class LoggerComponentImpl implements ILoggerComponent {
    public LoggerComponentImpl() {
        super();
    }

    @Override
    public void log(String pComponentName, String pInstanceId,
                    String pMessage)  {
       output.logToConsole(pComponentName, pInstanceId, pMessage);
    }

    private LoggerOutput output;

    public void setOutput(LoggerOutput output) {
        this.output = output;
    }

    public LoggerOutput getOutput() {
        return output;
    }
}

The last class prints the console output
package nl.whitehorses.wls.sca.spring;

public class LoggerOutput {
    public LoggerOutput() {
        super();
    }
    
    public void logToConsole(String pComponentName, String pInstanceId,  String pMessage)
    {
        StringBuffer logBuffer = new StringBuffer ();
        logBuffer.append("[").append(pComponentName).append("] [Instance: ").
            append(pInstanceId).append("] ").append(pMessage);
        
        System.out.println(logBuffer.toString());
    }
}


Deploy the web application the weblogic server and use soapui to test the service. The wsdl url is in my case http://laptopedwin.wh.lan:7001/WlsSpring-ScaSpring-context-root/LoggerService_Uri?WSDL or make a EJB test client and use this ILoggerComponent logger = (ILoggerComponent)context.lookup("LoggerService_EJB30_JNDI");

Here you can download my code on github https://github.com/biemond/jdev11g_examples/tree/master/WlsSpring

Saturday, December 5, 2009

ADF Data push with Active Data Service

With Patch Set 1 of JDeveloper 11G we can push data to the JSF page. This is called Active Data Service and this works with the following JSF component
  • activeCommandToolbarButton
  • activeImage
  • activeOutputText
  • table
  • tree
  • All DVT components
One requirement for this feature is you need to have a Datasource who supports data push. For example the BAM server of the Soa Suite support this. For other cases you need to use the Active Data Proxy framework. In this blog I made a demo with 3 different working examples , The first is an example of Oracle this in the fusion demo and can be downloaded here , the second is the one of Matthias Wessendorf and the last is the one of the Oracle ADS documentation example which works with a EJB datacontol.

In the next weeks I will make an ADS example with EJB and JMS, The ADS Page components will then listen on a topic for EJB data change events and refreshes the page with push .

But first let me show what you need to do.
Configure the adf-config.xml , located in the .adf\META-INF folder ( workspace level). With this ADF file you can configure the push parameters, study these parameters because they can influence the application performance.


<?xml version="1.0" encoding="windows-1252" ?>
<adf-config xmlns="http://xmlns.oracle.com/adf/config"
xmlns:sec="http://xmlns.oracle.com/adf/security/config"
xmlns:ads="http://xmlns.oracle.com/adf/activedata/config">
<sec:adf-security-child xmlns="http://xmlns.oracle.com/adf/security/config">
<CredentialStoreContext credentialStoreClass="oracle.adf.share.security.providers.jps.CSFCredentialStore"
credentialStoreLocation="../../src/META-INF/jps-config.xml"/>
</sec:adf-security-child>
<ads:adf-activedata-config xmlns="http://xmlns.oracle.com/adf/activedata/config">
<!--
transport allows three settings:
* streaming (default)
* polling
* long-polling
-->
<transport>long-polling</transport>
<latency-threshold>10000</latency-threshold>
<keep-alive-interval>10000</keep-alive-interval>
<polling-interval>3000</polling-interval>
<max-reconnect-attempt-time>1800000</max-reconnect-attempt-time>
<reconnect-wait-time>10000</reconnect-wait-time>
</ads:adf-activedata-config>
</adf-config>

Then create in the .adf\META-INF folder a new folder called services and put a new file in called adf-config.properties and add the following content.
http\://xmlns.oracle.com/adf/activedata/config=oracle.adfinternal.view.faces.activedata.ActiveDataConfiguration$ActiveDataConfigCallback

Go to your JSF page and drag and drop for example departments from your datacontrol to your page. Choose a readonly table ( when you want to use inputtext instead of outputtext JSF components then keep in mind you need to reset the uicomponent else it won't get the new value ) and don't use Filtering on the table.
This is how your JSF can look like.

<af:table value="#{bindings.departmentsFindAll.collectionModel}"
var="row"
rows="#{bindings.departmentsFindAll.rangeSize}"
emptyText="#{bindings.departmentsFindAll.viewable ? 'No data to display.' : 'Access Denied.'}"
fetchSize="#{bindings.departmentsFindAll.rangeSize}"
rowBandingInterval="0"
selectedRowKeys="#{bindings.departmentsFindAll.collectionModel.selectedRow}"
selectionListener="#{bindings.departmentsFindAll.collectionModel.makeCurrent}"
rowSelection="single" id="t3">
<af:column sortProperty="departmentId" sortable="true"
headerText="#{bindings.departmentsFindAll.hints.departmentId.label}"
id="c8">
<af:outputText value="#{row.departmentId}" id="ot9">
<af:convertNumber groupingUsed="false"
pattern="#{bindings.departmentsFindAll.hints.departmentId.format}"/>
</af:outputText>
</af:column>
<af:column sortProperty="departmentName" sortable="true"
headerText="#{bindings.departmentsFindAll.hints.departmentName.label}"
id="c9">
<af:outputText value="#{row.departmentName}" id="ot8"/>
</af:column>
</af:table>

Here the pagedef of the page ,we need to have a handle to the departmentsFindAll tree in our Active Data Proxy framework.

<?xml version="1.0" encoding="UTF-8" ?>
<pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
version="11.1.1.55.36" id="ADStablePageDef"
Package="nl.whitehorses.app.view.pageDefs">
<parameters/>
<executables>
<iterator Binds="root" RangeSize="25" DataControl="CountrySessionEJBLocal"
id="CountrySessionEJBLocalIterator"/>
<accessorIterator MasterBinding="CountrySessionEJBLocalIterator"
Binds="departmentsFindAll" RangeSize="25"
DataControl="CountrySessionEJBLocal"
BeanClass="nl.whitehorses.model2.Departments"
id="departmentsFindAllIterator"/>
</executables>
<bindings>

<tree IterBinding="departmentsFindAllIterator" id="departmentsFindAll">
<nodeDefinition DefName="nl.whitehorses.model2.Departments"
Name="departmentsFindAll0">
<AttrNames>
<Item Value="departmentId"/>
<Item Value="departmentName"/>
</AttrNames>
</nodeDefinition>
</tree>
</bindings>
</pageDefinition>

Now we can make our own DepartmentModel where we add Active Data Service to it. We need the departmentsFindAll tree attribute from the pagedef.

package nl.whitehorses.app.ads;

import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.view.rich.model.ActiveCollectionModelDecorator;

import oracle.adf.view.rich.model.ActiveDataModel;

import oracle.adfinternal.view.faces.model.binding.FacesCtrlHierBinding;


import org.apache.myfaces.trinidad.model.CollectionModel;

public class DepartmentModel extends ActiveCollectionModelDecorator {

private MyActiveDataModel _activeDataModel = new MyActiveDataModel();
private CollectionModel _model = null;


public CollectionModel getCollectionModel() {
if (_model == null) {
DCBindingContainer dcBindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
FacesCtrlHierBinding treeData = (FacesCtrlHierBinding)dcBindings.getControlBinding("departmentsFindAll");
_model = treeData.getCollectionModel();
}
return _model;
}

public ActiveDataModel getActiveDataModel() {
return _activeDataModel;
}

public MyActiveDataModel getMyActiveDataModel() {
return _activeDataModel;
}

}

Make your own active ActiveDataModel in which we start a thread where we fire change events.


package nl.whitehorses.app.ads;

import java.util.Collection;

import java.util.concurrent.atomic.AtomicInteger;
import oracle.adf.view.rich.activedata.BaseActiveDataModel;
import oracle.adf.view.rich.event.ActiveDataUpdateEvent;

public class MyActiveDataModel extends BaseActiveDataModel
{
protected void startActiveData(Collection<Object> rowKeys, int startChangeCount)
{
_listenerCount.incrementAndGet();
if (_listenerCount.get() == 1)
{
System.out.println("start up");

Runnable dataChanger = new Runnable()
{
public void run()
{
System.out.println("MyThread starting.");
try
{
Thread.sleep(2000);
System.out.println("thread running");
Change chg = new Change();
chg.triggerDataChange(MyActiveDataModel.this);
}
catch (Exception exc)
{
System.out.println("MyThread exceptioned out.");
}
System.out.println("MyThread terminating.");
}
};
Thread newThrd = new Thread(dataChanger);
newThrd.start();
}
}

protected void stopActiveData(Collection<Object> rowKeys)
{
_listenerCount.decrementAndGet();
if (_listenerCount.get() == 0)
{
System.out.println("tear down");
}
}

public int getCurrentChangeCount()
{
return _currEventId.get();
}

public void bumpChangeCount()
{
_currEventId.incrementAndGet();
}

public void dataChanged(ActiveDataUpdateEvent event)
{
fireActiveDataUpdate(event);
}

private final AtomicInteger _listenerCount = new AtomicInteger(0);
private final AtomicInteger _currEventId = new AtomicInteger();
}




For demo purposes we send some change events. For this we need to know the Key of the department iterator and the attribute which changed. See the ADS help page for other events like insert ,delete etc..


package nl.whitehorses.app.ads;

import oracle.adf.view.rich.event.ActiveDataEntry;

import oracle.adf.view.rich.event.ActiveDataUpdateEvent;

import oracle.adfinternal.view.faces.activedata.ActiveDataEventUtil;
import oracle.adfinternal.view.faces.activedata.JboActiveDataEventUtil;


public class Change {
public Change() {

}

public void triggerDataChange(MyActiveDataModel model) throws Exception {


for ( int i = 0 ; i < 10 ; i++) {
try {
Thread.sleep(4000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}


model.bumpChangeCount();

ActiveDataUpdateEvent event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.UPDATE,
model.getCurrentChangeCount(),
JboActiveDataEventUtil.convertKeyPath(new Object[] { new Long(10) , new Integer(0) }),
null,
new String[] { "departmentName" },
new Object[] { "Administration" });

model.dataChanged(event);

try {
Thread.sleep(2000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}

model.bumpChangeCount();
event =
ActiveDataEventUtil.buildActiveDataUpdateEvent(ActiveDataEntry.ChangeType.UPDATE,
model.getCurrentChangeCount(),
JboActiveDataEventUtil.convertKeyPath(new Object[] { new Long(30), new Integer(0) }),
null,
new String[] { "departmentName" },
new Object[] { "Purchasing" });

model.dataChanged(event);

}
}
}

We need to add the DepartmentModel class as backing bean in the adfc-config or faces-config xml.

<managed-bean>
<managed-bean-name>DepartmentModel</managed-bean-name>
<managed-bean-class>nl.whitehorses.app.ads.DepartmentModel</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>


And at last change the value attribute of the af:table to our managed bean
af:table value="#{DepartmentModel}" var="row"

Here you can download my demo workspace