Decoding binary data from TCP to XML in JBoss Fuse - tcp

I'm new to ESBs in general, however I've been tasked to implement a TCP reader which outputs XML data in JBoss Fuse. I've tried a few methods, with limited success so far.
I started by using the Camel Blueprint ArchType (since that's what most of the Fuse tutorials are based around). I then defined a simple route beginning with a Netty4 input, running through a custom decoder class and logging the result.
blueprint.xml
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<bean id="myMessageDecoder" class="com.mycompany.binaryreceiver.binaryreceiver.MyMessageDecoder" />
<camelContext allowUseOriginalMessage="false" xmlns="http://camel.apache.org/schema/blueprint">
<route>
<from uri="netty4:tcp://localhost:9999?decoder=#myMessageDecoder&sync=false"/>
<log message="Message Received: ${body}"/>
</route>
</camelContext>
</blueprint>
MyMessageDecoder.java
package com.mycompany.binaryreceiver.binaryreceiver;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
public class MyMessageDecoder extends ByteToMessageDecoder {
#Override
protected void decode(ChannelHandlerContext context, ByteBuf buffer, List<Object> out) throws Exception {
if (buffer.readableBytes() < 3) {
return;
}
byte[] bytes = new byte[3];
buffer.readBytes(bytes);
MyMessage myMessage = new MyMessage(bytes);
out.add(myMessage);
}
}
MyMessage.java
package com.mycompany.binaryreceiver.binaryreceiver;
public class MyMessage {
protected int data1;
protected int data2;
protected int data3;
public MyMessage(byte[] data) {
data1 = data[0];
data2 = data[1];
data3 = data[2];
}
public String toString() {
return "MyMessage: { " + this.data1 + ", " + this.data2 + ", " + this.data3 + " }";
}
}
The decoder simply checks if there are three bytes on the buffer. If there is, then it pushes these bytes into MyMessage, which offers a toString interface which the logger can access.
Obviously this is just an example, and the final message is much more complex, involving variable length content, however this illustrates the basic process.
This all appears to work, if I push a stream of bytes into port 9999, the logger outputs the expected values:
INFO Message Received: MyMessage: { 1, 13, 58 }
INFO Message Received: MyMessage: { 2, 36, 63 }
INFO Message Received: MyMessage: { 3, 74, 5 }
INFO Message Received: MyMessage: { 4, 12, 92 }
INFO Message Received: MyMessage: { 5, 111, -121 }
INFO Message Received: MyMessage: { 6, 0, 0 }
INFO Message Received: MyMessage: { 7, 80, 64 }
INFO Message Received: MyMessage: { 8, 0, 0 }
INFO Message Received: MyMessage: { 9, 0, -116 }
INFO Message Received: MyMessage: { 10, -108, 111 }
INFO Message Received: MyMessage: { 11, -17, -100 }
INFO Message Received: MyMessage: { 12, -35, -28 }
However, during startup I'm getting the following message:
[Blueprint Extender: 1] NettyConfiguration WARN
The decoder com.mycompany.binaryreceiver.binaryreceiver.MyMessageDecoder#2413a0f1
is not #Shareable or an ChannelHandlerFactory instance. The decoder cannot safely be used.
After looking up the warning, I found references which seemed to suggest that I'm going around the whole process incorrectly, and that I should be going through the Netty4 ChannelHandlerFactories.
Has anyone got experience of performing a similar binary -> XML process who can comment or assist with the correct/recommended process for performing these tasks?
Is the warning a legitimate problem, or a red herring? Should I be utilising the ChannelHandlerFactories, or a completely different approach?

See the camel-netty4 documentation, at the section about non shareable encoders or decoders. Its all documentation what you have to do:
http://camel.apache.org/netty4

I was planning to implement a factory in order to try and handle the instantiation of the decoders; however, I was lucky and the functionality I actually needed could be implemented with the built-in length field decoder.
<bean id="lengthFieldDecoder"
class="org.apache.camel.component.netty4.ChannelHandlerFactories"
factory-method="newLengthFieldBasedFrameDecoder">
<argument value="10000" /> <!-- Max Frame Length -->
<argument value="0" /> <!-- Length Field Offset -->
<argument value="4" /> <!-- Length Field Length -->
<argument value="0" /> <!-- Length Adjustment -->
<argument value="4" /> <!-- Initial Bytes to Strip -->
</bean>
<camelContext trace="false" xmlns="http://camel.apache.org/schema/blueprint">
<route>
<from uri="netty4:tcp://localhost:9999?decoder=#lengthFieldDecoder&
clientMode=true&sync=false"
id="Netty4 TCP Client" />
<log message="Message (Binary): ${body}" id="Log Binary"/>
</route>
</camelContext>

Related

Wait for asynchronous logging to finish before Thread ends slf4j/log4j2

I'm using an asynchronous database logging setup with slf4j/log4j2. All is working as expected.
The only drawback is, that sometimes when the server is shut down the logger has not completed writing to the database (e.g. if there was a burst before shutdown).
I get:
WARN AsyncLoggerConfigDisruptor: shutdown timed out after 0
MILLISECONDS Log4j2-TF-1-AsyncLoggerConfig-1 ERROR Attempted to append
to non-started appender databaseAppender
And I lose everything that was in the logger-buffer.
Is there a way in slf4j or log4j2 to wait for the asynchronous logger to finish (e.g. a blocking call at the end of a task)?
Or at least to get the size of the buffer, that still needs to be written?
Currently, I use a setup with synchronous file logger and an asynchronous database logger, so the data is still in the file...
--> Edit 02/28/19
Here is a quick and dirty example code:
protected static final Logger logger = LoggerFactory.getLogger(App.class);
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Runnable task = new TestLogging(i);
executor.submit(task);
}
executor.shutdown();
System.out.println("Done");
//LogManager.shutdown();
}
private static class TestLogging implements Runnable {
private int i;
public TestLogging(int i) {
this.i=i;
}
public void run() {
// Generate a big string for DB test
StringBuffer b=new StringBuffer();
IntStream.range(0, 1000).parallel().forEach(i -> b.append(UUID.randomUUID().toString()));
// Writing to context for logging
MDC.put("test", "test"+i);
MDC.put("test1", b.toString());
// Log 1000 lines
IntStream.range(0, 1000).forEach(j -> logger.error("Exception " + j + "-"+ i, new RuntimeException()));
}
}
The configuration xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<File name="MyFile" fileName="all.log" immediateFlush="false"
append="false">
<PatternLayout
pattern="%d{yyy-MM-dd HH:mm:ss.SSS} tx.id=%X{test} tx.content=%X{test1} [%t] %-5level %logger{36} - %msg%n" />
</File>
<JDBC name="databaseAppender" tableName="log_test">
<PoolingDriver connectionString="jdbc:oracle:thin:#centos.fritz.box:1521:orcl" userName="test" password="test" driverClassName="oracle.jdbc.driver.OracleDriver"/>
<Column name="LOG_ID" pattern="%u" />
<Column name="ENTRY_DATE" isEventTimestamp="true" />
<Column name="LOGGER" pattern="%logger" />
<Column name="LOG_LEVEL" pattern="%level" />
<Column name="MESSAGE" pattern="%m" />
<Column name="EXCEPTION" pattern="%throwable" />
<Column name="ID" pattern="%X{test}" />
<Column name="XML" pattern="%X{test1}" />
</JDBC>
</Appenders>
<Loggers>
<AsyncRoot level="info">
<AppenderRef ref="databaseAppender" />
</AsyncRoot>
<Logger name="com.test" level="trace" includeLocation="true">
<AppenderRef ref="MyFile" />
</Logger>
</Loggers>
</Configuration>
And for Oracle the DDL:
CREATE TABLE "TEST"."LOG_TEST"
( "LOG_ID" VARCHAR2(100 BYTE),
"ENTRY_DATE" TIMESTAMP (6),
"LOGGER" VARCHAR2(100 BYTE),
"LOG_LEVEL" VARCHAR2(100 BYTE),
"MESSAGE" CLOB,
"EXCEPTION" CLOB,
"ID" VARCHAR2(20 BYTE),
"XML" CLOB,
PRIMARY KEY ("LOG_ID"))
If I use the
LogManager.shutdown();
The logging doesn't even start. Without, depending on the DB, I get about 500-1000 lines in the DB, before the error appears.
The logging in the real database will be much more sparse, this is just a worst-case test... and as mentioned, the logging in the file works. It would just be nice, to have a consistent logging in DB and file, even if the service is shut down.

Is ServletContext for 'localhost/prj' and 'xyz.com/prj' the same?

According to the following code, internal and external access of a Tomcat project (prj.war), the ServletContext seems to be different. I state this because after a few internal access to the servlet, an external call resets the value and proceeds normally. May I have your opinions ? Thanks.
public void doPost(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.setHeader("Connection", "keep-alive");
PrintWriter out = response.getWriter();
//-------------------------------------------------------------------------------------------------------------//
// P A R A M E T E R S //
//-------------------------------------------------------------------------------------------------------------//
String x_value = request.getParameter("value"); if ( x_value == null ) {x_value = "0";}
//-------------------------------------------------------------------------------------------------------------//
String w_value = ( String ) getServletConfig().getServletContext().getAttribute("value");
if ( w_value == null ) {
w_value = "0";
getServletConfig().getServletContext().setAttribute("value", w_value);
out.println("value reset !!!");
}
out.println("current value = " + w_value);
out.println("add value = " + x_value);
out.println("total value = " + ( w_value = String.valueOf(Integer.valueOf(w_value).intValue() + Integer.valueOf(x_value).intValue()) ));
getServletConfig().getServletContext().setAttribute("value", w_value);
out.println("new value set");
}
EDIT :
SERVER.XML
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
</Host>
<!-- ************************ -->
<!-- package.xyz.com -->
<!-- ************************ -->
<Host name="package.xyz.com" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
<Context docBase="package" path=""/>
</Host>
<!-- ************************ -->
<!-- www.package.xyz.com -->
<!-- ************************ -->
<Host name="www.package.xyz.com" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
<Context docBase="package" path=""/>
</Host>
INIT
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
Context is the same, no matter which way you are reaching it (internally or externally). As stated in Oracle document:
There is one context per "web application" per Java Virtual Machine. (A "web application" is a collection of servlets and content installed under a specific subset of the server's URL namespace such as /catalog and possibly installed via a .war file.)
Are you sure that your application is not reloaded (like hot deployment on the server)?

GCMRegistrar Crash with ECPN plugin for unity android

I encountered a severe problem with the ECPN plugin (push notification) for android. As client needs it these days and push notification is my last hurdle......
OK, I changed your plugin quite a bit, just to work with my own purpose. It seems that my Samsung S4 and Sony Xperia got crash whenever gcm.jar is used, for example:
#Override
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
Intent i = getIntent();
Boolean isRegistration = getIntent().getExtras().getBoolean("isRegistration ");
/*
if(!isRegistration) {
// Unregister device
GCMRegistrar.unregister(this);
} else {
// Carry on with registration
String SENDER_ID = i.getStringExtra("senderID");
// Checking device and manifest dependencies
*/
GCMRegistrar.checkDevice(this);
// GCMRegistrar.checkManifest(this);
// Get GCM registration id
//final String regId = GCMRegistrar.getRegistrationId(this);
/*
// Check if regid already presents
if (regId.equals("")) {
// Registration is not present, register now with GCM
GCMRegistrar.register(this, SENDER_ID);
} else {
// Send ID to Unity
sendConfirmRegistration(regId);
// if registeredOnServer flag is not set, send info to Unity
if (!GCMRegistrar.isRegisteredOnServer(this)) {
GCMRegistrar.setRegisteredOnServer(this, true);
}
}
}
*/
finish();
return;
}
The procedure crashes at the bold line. Any idea why it is so ?
My bundleID is com.redcross.redcross, below is my manifest file for GCM portion.
<!-- GCM java -->
<activity
android:name="com.redcross.redcross.GCMJava"
android:label="#string/app_name" > <!-- android:name must coincide with GCMJava package name + .GCMJava-->
</activity>
<receiver android:name="com.google.android.gcm.GCMBroadcastR eceiver" androidermission="com.google.android.c2dm.permission.SEND " >
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEI VE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGIS TRATION" />
<category android:name="com.redcross.redcross" /> <!-- android:name must coincide with GCMJava package name -->
</intent-filter> </receiver>
<service android:name="com.redcross.redcross.GCMIntentServi ce" /> <!-- android:name must coincide with GCMJava package name + .GCMIntentService-->
<!-- end -->
And I tried putting classes.jar, gcm.jar and android.jar around in unity asset folder => plugins => android and referenced inside or outside GCMJava.jar, it simply doesn't work out... Worked two whole days already...
Really appreciate your help ...

Windows Workflow 4 with custom instance store not working

I created a simple workflow service with custom instance store(to support oracle).
The custom instance store, i follow the demo from MSDN: [http://msdn.microsoft.com/en-us/library/ee829481.aspx][1]
But when i invoke the service api, raise the exception:
A value of the wrong type was retrieved from the instance store. A
value of type {/Default Web Site/}OrderService.svc was expected, but
null was encountered instead.
I try to use SqlWorkflowInstanceStore, it's OK. No problem for service.
I use custom instance store again and debug, i found LoadWorkflowCommand be executed before SaveWorkflowCommand. I think it's an issue. Need your help!
The following is my code snippet:
1. Web Config:
<extensions>
<behaviorExtensions>
<add name="databaseWorkflowInstanceStore" type="Practices.Framework.Workflow.Configuration.DatabaseInstanceStoreElement, Practices.Framework" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior>
<!--<sqlWorkflowInstanceStore connectionStringName="practicesDatabase" instanceCompletionAction="DeleteAll" instanceEncodingOption="GZip" />-->
<databaseWorkflowInstanceStore database="practicesDatabase" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
2. DatabaseInstanceStore
public class DatabaseInstanceStore : InstanceStore
{
protected override IAsyncResult BeginTryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
{
if (command is CreateWorkflowOwnerCommand)
{
var instanceOwnerId = Guid.NewGuid();
context.BindInstanceOwner(instanceOwnerId, instanceOwnerId);
}
else if (command is LoadWorkflowCommand)
{
var instanceData = this.LoadInstanceData();
context.LoadedInstance(InstanceState.Initialized, instanceData, null, null, null);
}
else if (command is SaveWorkflowCommand)
{
var saveCommand = (SaveWorkflowCommand)command;
var instanceData = saveCommand.InstanceData;
this.SaveInstanceData(instanceData);
}
return new CompletedAsyncResult<bool>(true, callback, state);
}
......
}
The MSDN article only shows the bare minimum of commands you need to implement. It sounds like you need to support more command so I would check which commands are dispatched that you don't support yet. See here for a list of commands.
The BeginTryCommand is called with the following commands executed in this order:
1. CreateWorkflowOwnerCommand
2. LoadWorkflowCommand
3. SaveWorkflowCommand
4. SaveWorkflowCommand
So for LoadWorkflowCommand:
I need Create Instance before Load
http://social.msdn.microsoft.com/Forums/en/dublin/thread/e51d7b18-1e27-4335-8ad0-4ce76b9f8b91

Basic Auth for WSO2 EI API service

I am using WSO2-EI 6.4.0. I have tried this development with link. It work for me. But I need to get user name and password from other back end service. In this example was showed the hard corded user and password. I have added that code for your reference. Please help me to get those user name and password from property file.
public boolean processSecurity(String credentials) {
String decodedCredentials = new String(new Base64().decode(credentials.getBytes()));
String usernName = decodedCredentials.split(":")[0];
String password = decodedCredentials.split(":")[1];
if ("admin".equals(username) && "admin".equals(password)) {
return true;
} else {
return false;
}
}
I have added WSO2 EI handler like following. I need to pass the value from back service or call other sequence and load.
<api context="/test">
<resource methods="POST">
<inSequence>
................
</inSequence>
<outSequence>
................
</outSequence>
</resource>
<handlers>
<handler class="rezg.ride.common.BasicAuthHandler">
<property name="cm_password" value="admin"/>
<property name="cm_userName" value="admin"/>
</handler>
</handlers>
</api>
When we run the above API, handlers are running first and then running in and out sequences. So I need to get user name and password calling Sequence or any other method before run this BasicAuthHandler.
If you need to read the property file from the class mediator it's just straight forward java property file reading. Please refer the following call sample of reading a property file. In this scenario, Just read the carbon.properties file exists in the conf directory.
public boolean mediate(MessageContext context) {
String passwordFileLocation = System.getProperty("conf.location")+"/carbon.properties";
try (FileInputStream input = new FileInputStream(passwordFileLocation)) {
Properties prop = new Properties();
// load a properties file
prop.load(input);
log.info("------org.wso2.CipherTransformation : " + prop.getProperty("org.wso2.CipherTransformation"));
} catch (IOException ex) {
ex.printStackTrace();
}
return true;
}
To get the server location and the conf locating, There are JAVA system properties are set at the time wso2 server starts. Following are some of the useful System system properties.
carbon.local.ip
carbon.home
conf.location

Resources