transform mule message payload to snmp trap or pdu object - mule

I've applied a sample of using snmp4j for sending and receiving traps and everything is ok.
but the issue is :
when using mule esb for receiving snmp traps, I can't convert the incoming message payload to PDU (or any snmp4j suitable object) to extract data from, I've done a lot of search but in vain.
can anyone assist me to :
convert mule esb message payload that I've received from udp endpoint to org.snmp4j.PDU object to extract trap data from?
here is my code :
public synchronized MuleEvent process(MuleEvent event) throws MuleException {
byte[] encodedMessage = event.getMessage().getPayload(byte[].class);
//next line is not working but its only sample of what I Am looking for
PDU pdu = new PDU(encodedMessage );
.....
any assistance is highly appreciated

public class SNMP4JParser implements Callable {
/**
* The following objects are all necessary in order to use SNMP4j as a parser for raw messages.
* This was all inspired by SNMP4j source code, in particular MessageDispatcherImpl.java
*/
private MessageProcessingModel model = null;
private MessageDispatcher dispatcher = null;
private Address listenAddress = null;
private Integer32 messageProcessingModel = null;
private Integer32 securityModel = null;
private OctetString securityName = null;
private Integer32 securityLevel = null;
private PduHandle handle = null;
private StatusInformation statusInfo = null;
private MutableStateReference mutableStateReference = null;
/**
* Taken from org.snmp4j.transport.AbstractTransportMapping class
*/
protected Integer32 maxInboundMessageSize = new Integer32 ( (1 << 16) - 1 );
/**
* Taken from org.snmp4j.MessageDispatcherImpl class
*/
private int transactionID = new Random().nextInt(Integer.MAX_VALUE - 2) + 1;
/**
* Create all objects that SNMP4j needs to parse a raw SNMP message
*/
public SNMP4JParser()
{
model = new MPv1();
dispatcher = new MessageDispatcherImpl();
listenAddress = GenericAddress.parse("udp:0.0.0.0/2001");
messageProcessingModel = new Integer32();
securityModel = new Integer32();
securityName = new OctetString();
securityLevel = new Integer32();
handle = new PduHandle(transactionID);
statusInfo = new StatusInformation();
mutableStateReference = new MutableStateReference();
}
/**
* #see org.mule.api.lifecycle.Callable#onCall(org.mule.api.MuleEventContext)
*/
#Override
public Object onCall(MuleEventContext eventContext) throws Exception
{
byte[] payloadBytes = eventContext.getMessage().getPayloadAsBytes();
ByteBuffer buffer = ByteBuffer.wrap(payloadBytes);
BERInputStream wholeMessage = new BERInputStream(buffer);
MutablePDU mutablePdu = new MutablePDU();
int status = model.prepareDataElements(
dispatcher,
listenAddress,
wholeMessage,
messageProcessingModel,
securityModel,
securityName,
securityLevel,
mutablePdu,
handle,
maxInboundMessageSize,
statusInfo,
mutableStateReference);
if ( status != SnmpConstants.SNMP_MP_OK )
throw new RuntimeException(
"Couldn't parse SNMP message. model.prepareDataElements() returned " + status);
return mutablePdu.getPdu();
}
}
I've tested it with a flow like this ( I used snmp4j-1.11.5 and mule-standalone-3.4.0 )
<udp:connector name="connector" doc:name="UDP"/>
<flow name="snmp-demo-trapHandlingFlow" doc:name="snmp-demo-trapHandlingFlow">
<udp:inbound-endpoint host="0.0.0.0" port="2001" responseTimeout="10000" doc:name="UDP"/>
<logger message="TRAP RECEIVED - #[System.currentTimeMillis()]" level="DEBUG" doc:name="Inbound timestamp"/>
<component class="com.netboss.flow.demo.SNMP4JParser" doc:name="SNMP4JParser"/>
[...]
And it works.
Now, I realize there are still some open questions:
Is there a better/more efficient way of doing it?
This works only for SNMP v1, how do I modify the above code to make it work with v2 and v3 as well?

You can convert a BER stream to a SNMP4J PDU much easier when you implement your own TransportMapping and associate that with the SNMP4J MessageDispatcherImpl. Then add all the necessary MessageProcessingModels and SecurityProtocols to the message dispatcher.
Finally, add your implementation of the CommandResponder interface to the message dispatcher and you are done.

You need to create a custom transformer that transforms the message payload in the relevant SNMP4J object. Alternatively this can be done with an expression transformer if the SNMP4J API is simple enough.

Related

Plc4x addressing system

I am discovering the Plc4x java implementation which seems to be of great interest in our field. But the youth of the project and the documentation makes us hesitate. I have been able to implement the basic hello world for reading out of our PLCs, but I was unable to write. I could not find how the addresses are handled and what the maskwrite, andMask and orMask fields mean.
Please can somebody explain to me the following example and detail how the addresses should be used?
#Test
void testWriteToPlc() {
// Establish a connection to the plc using the url provided as first argument
try( PlcConnection plcConnection = new PlcDriverManager().getConnection( "modbus:tcp://1.1.2.1" ) ){
// Create a new read request:
// - Give the single item requested the alias name "value"
var builder = plcConnection.writeRequestBuilder();
builder.addItem( "value-" + 1, "maskwrite:1[1]/2/3", 2 );
var writeRequest = builder.build();
LOGGER.info( "Synchronous request ..." );
var syncResponse = writeRequest.execute().get();
}catch(Exception e){
e.printStackTrace();
}
}
I have used PLC4x for writing using the modbus driver with success. Here is some sample code I am using:
public static void writePlc4x(ProtocolConnection connection, String registerName, byte[] writeRegister, int offset)
throws InterruptedException {
// modbus write works ok writing one record per request/item
int size = 1;
PlcWriteRequest.Builder writeBuilder = connection.writeRequestBuilder();
if (writeRegister.length == 2) {
writeBuilder.addItem(registerName, "register:" + offset + "[" + size + "]", writeRegister);
}
...
PlcWriteRequest request = writeBuilder.build();
request.execute().whenComplete((writeResponse, error) -> {
assertNotNull(writeResponse);
});
Thread.sleep((long) (sleepWait4Write * writeRegister.length * 1000));
}
In the case of modbus writing there is an issue regarding the return of the writer Future, but the write is done. In the modbus use case I don't need any mask stuff.

what is correct use of consumer groups in spring cloud stream dataflow and rabbitmq?

A follow up to this:
one SCDF source, 2 processors but only 1 processes each item
The 2 processors (del-1 and del-2) in the picture are receiving the same data within milliseconds of each other. I'm trying to rig this so del-2 never receives the same thing as del-1 and vice versa. So obviously I've got something configured incorrectly but I'm not sure where.
My processor has the following application.properties
spring.application.name=${vcap.application.name:sample-processor}
info.app.name=#project.artifactId#
info.app.description=#project.description#
info.app.version=#project.version#
management.endpoints.web.exposure.include=health,info,bindings
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
spring.cloud.stream.bindings.input.group=input
Is "spring.cloud.stream.bindings.input.group" specified correctly?
Here's the processor code:
#Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
public Object transform(String inputStr) throws InterruptedException{
ApplicationLog log = new ApplicationLog(this, "timerMessageSource");
String message = " I AM [" + inputStr + "] AND I HAVE BEEN PROCESSED!!!!!!!";
log.info("SampleProcessor.transform() incoming inputStr="+inputStr);
return message;
}
Is the #Transformer annotation the proper way to link this bit of code with "spring.cloud.stream.bindings.input.group" from application.properties? Are there any other annotations necessary?
Here's my source:
private String format = "EEEEE dd MMMMM yyyy HH:mm:ss.SSSZ";
#Bean
#InboundChannelAdapter(value = Source.OUTPUT, poller = #Poller(fixedDelay = "1000", maxMessagesPerPoll = "1"))
public MessageSource<String> timerMessageSource() {
ApplicationLog log = new ApplicationLog(this, "timerMessageSource");
String message = new SimpleDateFormat(format).format(new Date());
log.info("SampleSource.timeMessageSource() message=["+message+"]");
return () -> new GenericMessage<>(new SimpleDateFormat(format).format(new Date()));
}
I'm confused about the "value = Source.OUTPUT". Does this mean my processor needs to be named differently?
Is the inclusion of #Poller causing me a problem somehow?
This is how I define the 2 processor streams (del-1 and del-2) in SCDF shell:
stream create del-1 --definition ":split > processor-that-does-everything-sleeps5 --spring.cloud.stream.bindings.applicationMetrics.destination=metrics > :merge"
stream create del-2 --definition ":split > processor-that-does-everything-sleeps5 --spring.cloud.stream.bindings.applicationMetrics.destination=metrics > :merge"
Do I need to do anything differently there?
All of this is running in Docker/K8s.
RabbitMQ is given by bitnami/rabbitmq:3.7.2-r1 and is configured with the following props:
RABBITMQ_USERNAME: user
RABBITMQ_PASSWORD <redacted>:
RABBITMQ_ERL_COOKIE <redacted>:
RABBITMQ_NODE_PORT_NUMBER: 5672
RABBITMQ_NODE_TYPE: stats
RABBITMQ_NODE_NAME: rabbit#localhost
RABBITMQ_CLUSTER_NODE_NAME:
RABBITMQ_DEFAULT_VHOST: /
RABBITMQ_MANAGER_PORT_NUMBER: 15672
RABBITMQ_DISK_FREE_LIMIT: "6GiB"
Are any other environment variables necessary?

How to keep HTTP/2 connection alive till the request / response session is complete?

I am currently using HttpDeclarePushto exploit the Server Push feature in HTTP/2.
I am able to successfully create all the parameters that this function accepts. But the issue is when HttpDeclarePushexecutes it returns a value of 1229 (ERROR_CONNECTION_INVALID) - https://learn.microsoft.com/en-us/windows/desktop/debug/system-error-codes--1000-1299-.
On further investigation I found that the HttpHeaderConnection in _HTTP_HEADER_ID (https://learn.microsoft.com/en-us/windows/desktop/api/http/ne-http-_http_header_id) is actually passed in the function as 'close'. That implies that on every request response the server closes the connection and that is also happening in my case, I checked it in the log.
Here is the code.
class http2_native_module : public CHttpModule
{
public:
REQUEST_NOTIFICATION_STATUS OnBeginRequest(IN IHttpContext * p_http_context, IN IHttpEventProvider * p_provider)
{
HTTP_REQUEST_ID request_id;
const HTTPAPI_VERSION version = HTTPAPI_VERSION_2;
auto pHttpRequest = p_http_context->GetRequest();
auto phttpRequestRaw = pHttpRequest->GetRawHttpRequest();
HANDLE p_req_queue_handle = nullptr;
auto isHttp2 = phttpRequestRaw->Flags;
try {
const auto request_queue_handle = HttpCreateRequestQueue(version, nullptr, nullptr, NULL, &p_req_queue_handle);
const auto verb = phttpRequestRaw->Verb;
const auto http_path = L"/polyfills.0d74a55d0dbab6b8c32c.js"; //ITEM that I want to PUSH to client
const auto query = nullptr;
request_id = phttpRequestRaw->RequestId;
auto headers = phttpRequestRaw->Headers;
auto connId = phttpRequestRaw->ConnectionId;
WriteEventViewerLog(L"OnBeginRequest - Entering HTTPDECLAREPUSH");
headers.KnownHeaders[1].pRawValue = NULL;
headers.KnownHeaders[1].RawValueLength = 0;
const auto is_success = HttpDeclarePush(p_req_queue_handle, request_id, verb, http_path, query, &headers);
sprintf_s(szBuffer, "%lu", is_success);
Log("is_success value", szBuffer); //ERROR CODE 1229 here
HttpCloseRequestQueue(p_req_queue_handle);
}
catch (std::bad_alloc & e)
{
auto something = e;
}
return RQ_NOTIFICATION_CONTINUE;
}
I even tried to update the header connection value as below but it still gives me 1229.
headers.KnownHeaders[1].pRawValue = NULL;
headers.KnownHeaders[1].RawValueLength = 0;
I understand from https://http2.github.io/http2-spec/ that HTTP/2 actually ignores the content in HTTP HEADERs and uses some other mechanism as part of its FRAME.
This brings us to the next question on how we can keep the connection OPEN and is it something related to the FRAME (similar to HEADER) that HTTP2 uses, if so, how C++ or rather Microsoft helps us to play and exploit with the FRAME in HTTP2?

How do i create a TCP receiver that only consumes messages using akka streams?

We are on: akka-stream-experimental_2.11 1.0.
Inspired by the example
We wrote a TCP receiver as follows:
def bind(address: String, port: Int, target: ActorRef)
(implicit system: ActorSystem, actorMaterializer: ActorMaterializer): Future[ServerBinding] = {
val sink = Sink.foreach[Tcp.IncomingConnection] { conn =>
val serverFlow = Flow[ByteString]
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true))
.map(message => {
target ? new Message(message); ByteString.empty
})
conn handleWith serverFlow
}
val connections = Tcp().bind(address, port)
connections.to(sink).run()
}
However, our intention was to have the receiver not respond at all and only sink the message. (The TCP message publisher does not care about response ).
Is it even possible? to not respond at all since akka.stream.scaladsl.Tcp.IncomingConnection takes a flow of type: Flow[ByteString, ByteString, Unit]
If yes, some guidance will be much appreciated. Thanks in advance.
One attempt as follows passes my unit tests but not sure if its the best idea:
def bind(address: String, port: Int, target: ActorRef)
(implicit system: ActorSystem, actorMaterializer: ActorMaterializer): Future[ServerBinding] = {
val sink = Sink.foreach[Tcp.IncomingConnection] { conn =>
val targetSubscriber = ActorSubscriber[Message](system.actorOf(Props(new TargetSubscriber(target))))
val targetSink = Flow[ByteString]
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true))
.map(Message(_))
.to(Sink(targetSubscriber))
conn.flow.to(targetSink).runWith(Source(Promise().future))
}
val connections = Tcp().bind(address, port)
connections.to(sink).run()
}
You are on the right track. To keep the possibility to close the connection at some point you may want to keep the promise and complete it later on. Once completed with an element this element published by the source. However, as you don't want any element to be published on the connection, you can use drop(1) to make sure the source will never emit any element.
Here's an updated version of your example (untested):
val promise = Promise[ByteString]()
// this source will complete when the promise is fulfilled
// or it will complete with an error if the promise is completed with an error
val completionSource = Source(promise.future).drop(1)
completionSource // only used to complete later
.via(conn.flow) // I reordered the flow for better readability (arguably)
.runWith(targetSink)
// to close the connection later complete the promise:
def closeConnection() = promise.success(ByteString.empty) // dummy element, will be dropped
// alternatively to fail the connection later, complete with an error
def failConnection() = promise.failure(new RuntimeException)

In Bloomberg API how do you specify to get FX forwards as a spread rather than absolute values?

How do you explicitly request fx forwards as outrights using the bloomberg API?
In the Bloomberg terminal you can choose whether to get FX Forwards as absolute rates (outrights) or as offsets from Spots (Points) by doing XDF, hitting 7, then the option is about half way down. 0 means outrights, and 1 means offfsets.
With most defaults you can explicitly set them in the API, so your code gives the same result whichever computer you run on. How do you set this one in a V3 API query?
Having had a colleague told by the help desk this is impossible, it turns out they are wrong and it is possible. You override the FWD_CURVE_QUOTE_FORMAT to be RATES for absolute and POINTS as offsets.
Example code (Java):
public static void main(String [] args) throws Exception{
Session session = BlpUtil.connectToReferenceData();
Service refDataService = session.getService("//blp/refdata");
Request request = refDataService.createRequest("HistoricalDataRequest");
Element securities = request.getElement("securities");
securities.appendValue("JPY10Y CMPL Curncy");
Element fields = request.getElement("fields");
fields.appendValue("PX_LAST");
request.set("startDate", "20100527");
request.set("endDate", "20100527");
Element overrides = request.getElement("overrides");
Element override1 = overrides.appendElement();
override1.setElement("fieldId", "FWD_CURVE_QUOTE_FORMAT");
override1.setElement("value", "POINTS");
CorrelationID cid = session.sendRequest(request, null);
while (true) {
Event event = session.nextEvent();
MessageIterator msgIter = event.messageIterator();
while (msgIter.hasNext()) {
Message msg = msgIter.next();
if (msg.correlationID() == cid) {
System.out.println("msg = " + msg);
}
}
}
}