Add STOMP header without recreating Message on ChannelInterceptorAdapter - spring-websocket

I need to add header to a STOMP message currently it is working as below but i am recreating the message , is it possible to just add native header without having to recreate the message for performance .
public class MyChannelInterceptor extends ChannelInterceptorAdapter {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
StompCommand command = accessor.getCommand();
if(command != null) {
log.debug("Receiving msg {} from {}",command,accessor.getUser().getName());
if(command == StompCommand.SEND) {
log.debug("Adding expires header to msg {} from {}",command,accessor.getUser().getName());
String ttlString = accessor.getFirstNativeHeader("ttl");
long ttl = 30000;
try {
ttl = Long.parseLong(ttlString);
}
catch(Exception ex) {
log.error("TTL header received but not in correct format {}",ttlString);
}
accessor.addNativeHeader("expires", Long.toString(System.currentTimeMillis() + ttl));
return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
}
}
return message;
}
}

This is what i was looking for
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
The above code will get the actual StompHeaderAccessor of the message so if you manipulate the native headers they are directly reflected on the message while
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
will get a clone of the headers and you have to create a new message with the new cloned headers
full fixed code below
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
// StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
if(accessor != null) {
StompCommand command = accessor.getCommand();
if(command != null) {
log.debug("Receiving msg {} from {}",command,accessor.getUser().getName());
if(command == StompCommand.SEND) {
log.debug("Adding expires header to msg {} from {}",command,accessor.getUser().getName());
String ttlString = accessor.getFirstNativeHeader("ttl");
long ttl = 30000;
if(ttlString != null) {
try {
ttl = Long.parseLong(ttlString);
}
catch(Exception ex) {
log.error("TTL header received but not in correct format {}",ttlString);
}
}
accessor.addNativeHeader("expires", Long.toString(System.currentTimeMillis() + ttl));
// I don't need any more to create a new message
//return MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
}
}
}
return message;
}

Since addNativeHeader succeeds, that indicates the message is still mutable - see addNativeHeader().
In any case, since the NATIVE_HEADERS message header is a MultiValueMap-valued header, you can update the header contents in-place.
Hence, there is no need to create a new message.
You would have to create a new message if you add a new header to the message itself (rather than updating the mutable contents of an existing header).
EDIT
I just ran a test; as long as the message is still mutable, you can change it...
#Test
public void test() {
Map<String, Object> map = new HashMap<String, Object>();
MutableMessageHeaders headers = new MutableMessageHeaders(map);
Message<String> message = MessageBuilder.createMessage("foo", headers);
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
accessor.addNativeHeader("foo", "bar");
System.out.println(message.getHeaders().get(NativeMessageHeaderAccessor.NATIVE_HEADERS));
accessor.setImmutable();
try {
accessor.addNativeHeader("baz", "qux");
fail("expected IllegalStateException");
}
catch (IllegalStateException e) {
}
}
That said, are you experiencing a performance problem or is this just a perceived issue. Message creation is not expensive.

Related

FileNet retrieving the GUID without SQL Query?

I am working on a program that upload PDF files to FileNet, and was wondering if there is a way to retrieve the GUID without running a SQL Query. I also tried Retrieving a Document (fetchInstance) from https://www.ibm.com/support/knowledgecenter/en/SSNW2F_5.5.0/com.ibm.p8.ce.dev.ce.doc/document_procedures.htm without the new Id part of the code and that did not work.
public class DocIdGenerator implements EventActionHandler {
private static final String DS = "ECMSvcsXA";
private static final String INSERT_SQL = "Insert into dbo.ICNLegalDocID_S (object_id) values (?)";
private static final String SELECT_SQL = "Select DocId From dbo.ICNLegalDocID_S Where object_id = ?";
#Override
public void onEvent(ObjectChangeEvent event, Id subscriptionId)
throws EngineRuntimeException {
// Get the Document object from the event
ObjectStore os = event.getObjectStore();
Id objectId = event.get_SourceObjectId();
PropertyFilter pf = new PropertyFilter();
pf.addIncludeProperty(new FilterElement(null, null, null, "DocumentID", null));
pf.addIncludeProperty(new FilterElement(null, null, null, PropertyNames.VERSION_SERIES, null));
Document sourceDoc = Factory.Document.fetchInstance(os, objectId, pf);
Properties props = sourceDoc.getProperties();
String documentId = props.getStringValue("DocumentID");
VersionSeries vs = sourceDoc.get_VersionSeries();
Id versionSeriesId = vs.get_Id();
if (documentId == null || documentId.isEmpty()) {
// Get the JNDI Context to lookup DataSource and Insert the objectId to get the auto generated docId
Context ctx;
DataSource ds;
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
ctx = new InitialContext();
ds = (DataSource) ctx.lookup(DS);
con = ds.getConnection();
ps = con.prepareStatement(SELECT_SQL);
ps.setString(1, versionSeriesId.toString());
rs = ps.executeQuery();
BigDecimal docId = null;
if (rs.next()) {
// Document Id already exists
docId = rs.getBigDecimal(1);
} else {
// Document Id doesn't exist inert to get it
ps = con.prepareStatement(INSERT_SQL, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, versionSeriesId.toString());
ps.executeUpdate();
rs = ps.getGeneratedKeys();
if (rs.next()) {
docId = rs.getBigDecimal(1);
}
}
props.putValue("DocumentID", docId.toString());
sourceDoc.save(RefreshMode.REFRESH);
} catch (Exception e) {
e.printStackTrace();
if (e instanceof EngineRuntimeException) {
throw (EngineRuntimeException)e;
} else {
ErrorStack es = new ErrorStack("DocIdGeneratorSub", new ErrorRecord[] {new ErrorRecord(e)});
throw new EngineRuntimeException(es);
}
} finally {
close(con, ps, rs);
}
}
}
private void close(Connection con, PreparedStatement ps, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// Ignore
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
// Ignore
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
// Ignore
}
}
}
}
I am not sure that I understand what to you trying to do.
When you create new document instance (Document doc = Factory.Document.newInstance()) and invoke doc.save(Refresh.REFRESH) options, FileNet inserts new DB record for document instance, generate new ID and populate it backward to the document instance on your side.
So, to get Id for new Document you just need to call doc.getId() to get it.
As I understand from your sample, you try to use event handler, If you invoke handler on the event before creation, you don't have any Id at this time, you cant try to handle the event after document creation.

Why setDefaultErrorHandler crash my view in vaadin flow?

I wrote my own custom error handler for the UI in Vaadin flow. But when I throw the exception my view crash and not show my human readable error message.
I did this in other application using Vaadin 8 and works perfectly. The idea its throw a SgiException in my backend services like:
Product not found
Incorrect value for field "XXX"
Not available stock for the product.
etc.
And then show a system notification
public static void setDefaultErrorHandler(ErrorEvent errorEvent) {
Throwable t = DefaultErrorHandler.findRelevantThrowable(errorEvent.getThrowable());
String message;
if (t != null) {
message = t.getMessage();
} else {
message = "";
}
log.error(message, t);
SgiException sgiException = getCauseOfType(t, SgiException.class);
if (sgiException != null) {
NotificationBuilder.exception(sgiException.getCode(), sgiException.getMessage());
return;
} else {
NotificationBuilder.exception(UNKNOW_ERROR, (message == null ? "" : message));
return;
}
}
private static <T extends Throwable> T getCauseOfType(Throwable th, Class<T> type) {
while (th != null) {
if (type.isAssignableFrom(th.getClass())) {
return (T) th;
} else {
th = th.getCause();
}
}
return null;
}
And this is how I set the custom error handler:
#PostConstruct
public void configBaseView() {
VaadinSession.getCurrent().setErrorHandler(Util::setDefaultErrorHandler);
}
In the view show this:
Note:
Debugging the application, seeing the code it's running, looks the method its called for some reason not show the notification.
This is a nasty behaviour that can't currently be overridden in Vaadin 10. Follow and vote (thumb up or comment) this issue to get it solved: https://github.com/vaadin/flow/issues/801

Grails 2.4.4 request object logging

I need to log the incomming REST request (xml).
So I create a
public class RequestWrapper extends HttpServletRequestWrapper {
private String _body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
_body = "";
BufferedReader bufferedReader = request.getReader();
String line;
while ((line = bufferedReader.readLine()) != null){
_body += line;
}
}
Now I use a filter to log out the incomming request:
class XmlogFilterFilters {
def filters = {
all(controller:'*', action:'*') {
before = {
RequestWrapper wrappedRequest = new RequestWrapper(request)
log.info(wrappedRequest.reader.text)
}
}
}
This logs the incomming request as expected.
But now in my controller the request is empty and cannot be used to Build my Domain-Object:
class InquiryHandlerController {
def save(Inquiry inquiryInstance) {
... *** inquiryInstance is null here
}
}
I guess the problem is, that th request was already read in the RequestWrapper, and therefor cannot be read again in the magic requestToDomain conversion.
So how can I pass the new RequestWrapper Object instead of the original request to the controller?
Finally found a solution:
use grailsplugin: grails-httplogger in version 1.1 do the right wrapper thing, so logging and consuming is working now.
https://github.com/prumps/grails-httplogger

Partial Unmarshalling of an XML using JAXB to skip some xmlElement

I want to unmarshal an XML file to java object using JAXB. The XML file is very large and contains some nodes which I want to skip in some cases to improve performance as these elements are non editable by client java program.
A sample XML is as follows:
<Example id="10" date="1970-01-01" version="1.0">
<Properties>...</Properties>
<Summary>...</Summary>
<RawData>
<Document id="1">...</Document>
<Document id="2">...</Document>
<Document id="3">...</Document>
------
------
</RawData>
<Location></Location>
<Title></Title>
----- // more elements
</Example>
I have two use cases:
unmarshal into Example object which contains Properties, Summaries, RawData etc. without skipping any RawData. (already done this part)
unmarshal into Example object which exclude RawData. Elements nested in RawData is very large so do not want to read this in this use case.
Now I want to unmarshal the XML such that RawData can be skipped. I have tried the technique provided at this link.
Using technique provided in above link also skips all elements which come after RawData.
I have fixed the issue with XMLEventReader with following code:
public class PartialXmlEventReader implements XMLEventReader {
private final XMLEventReader reader;
private final QName qName;
private boolean skip = false;
public PartialXmlEventReader(final XMLEventReader reader, final QName element) {
this.reader = reader;
this.qName = element;
}
#Override
public String getElementText() throws XMLStreamException {
return reader.getElementText();
}
#Override
public Object getProperty(final String name) throws IllegalArgumentException {
return reader.getProperty(name);
}
#Override
public boolean hasNext() {
return reader.hasNext();
}
#Override
public XMLEvent nextEvent() throws XMLStreamException {
while (isEof(reader.peek())) {
reader.nextEvent();
}
return reader.nextEvent();
}
#Override
public XMLEvent nextTag() throws XMLStreamException {
return reader.nextTag();
}
#Override
public XMLEvent peek() throws XMLStreamException {
return reader.peek();
}
#Override
public Object next() {
return reader.next();
}
#Override
public void remove() {
reader.remove();
}
#Override
public void close() throws XMLStreamException {
reader.close();
}
private boolean isEof(final XMLEvent e) {
boolean returnValue = skip;
switch (e.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
final StartElement se = (StartElement) e;
if (se.getName().equals(qName)) {
skip = true;
returnValue = true;
}
break;
case XMLStreamConstants.END_ELEMENT:
final EndElement ee = (EndElement) e;
if (ee.getName().equals(qName)) {
skip = false;
}
break;
}
return returnValue;
}
}
While Unmarshalling just pass this eventReader to the unmarshal method
final JAXBContext context = JAXBContext.newInstance(classes);
final Unmarshaller um = context.createUnmarshaller();
Reader reader = null;
try {
reader = new BufferedReader(new FileReader(xmlFile));
final QName qName = new QName("RawData");
final XMLInputFactory xif = XMLInputFactory.newInstance();
final XMLEventReader xmlEventReader = xif.createXMLEventReader(reader);
final Example example =
(Example) um.unmarshal(new PartialXmlEventReader(xmlEventReader, qName));
}
} finally {
IOUtils.closeQuietly(reader);
}
I hope this would help
try {
// First create a new XMLInputFactory
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
// Setup a new eventReader
InputStream in = new FileInputStream("myXml");
XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
// Read the XML document
Example example = null;
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
// If we have a example element we create a new example
if (startElement.getName().getLocalPart().equals("Example")) {
example = new Example();
// We read the attributes from this tag and add the date
// and id attribute to our object
Iterator<Attribute> attributes = startElement
.getAttributes();
while (attributes.hasNext()) {
Attribute attribute = attributes.next();
if (attribute.getName().toString().equals("date")) {
example.setDate(attribute.getValue());
} else if (attribute.getName().toString().equals("id")) {
example.setId(attribute.getValue());
}
}
}
//get the Properties tag and add to object example
if (event.isStartElement()) {
if (event.asStartElement().getName().getLocalPart()
.equals("Properties")) {
event = eventReader.nextEvent();
example.setProperites(event.asCharacters().getData());
continue;
}
}
//get the Summary tag and add to object example
if (event.asStartElement().getName().getLocalPart()
.equals("Summary")) {
event = eventReader.nextEvent();
example.setSummary(event.asCharacters().getData());
continue;
}
// when you encounter the Rawdata tag just continue
//without adding it to the object created
if (event.asStartElement().getName().getLocalPart()
.equals("Rawdata")) {
event = eventReader.nextEvent();
// don't do anything
continue;
}
//get the location tag and add to object example
if (event.asStartElement().getName().getLocalPart()
.equals("Location")) {
event = eventReader.nextEvent();
example.setLocation(event.asCharacters().getData());
continue;
}
// read and add other elements that can be added
}
// If we reach the end of an example element/tag i.e closing tag
if (event.isEndElement()) {
EndElement endElement = event.asEndElement();
if (endElement.getName().getLocalPart().equals("Example")) {
//do something
}
}
}
} catch (FileNotFoundException | XMLStreamException e) {
}

Tried to read incoming SMS content but getting Error in Blackberry

Hi friends i am trying to read incoming sms but getting warning like this . Invocation of questionable method: java.lang.String.(String) found in: mypackage.MyApp$ListeningThread.run()
Here is my code is
public class MyApp extends UiApplication {
//private ListeningThread listener;
public static void main(String[] args) {
MyApp theApp = new MyApp();
theApp.enterEventDispatcher();
}
public MyApp() {
invokeAndWait(new Runnable() {
public void run() {
ListeningThread listener = new ListeningThread();
listener.start();
}
});
pushScreen(new MyScreen());
}
private static class ListeningThread extends Thread {
private boolean _stop = false;
private DatagramConnection _dc;
public synchronized void stop() {
_stop = true;
try {
_dc.close(); // Close the connection so the thread returns.
} catch (IOException e) {
System.err.println(e.toString());
}
}
public void run() {
try {
_dc = (DatagramConnection) Connector.open("sms://");
for (;;) {
if (_stop) {
return;
}
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
_dc.receive(d);
String address = new String(d.getAddress());
String msg = new String(d.getData());
if(msg.startsWith("START")){
Dialog.alert("hello");
}
System.out.println("Message received: " + msg);
System.out.println("From: " + address);
System.exit(0);
}
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
}
Please correct me where i am wrong.Is possible give me some code to read incoming sms content in blackberry.
A few points about your code:
That invokeAndWait call to launch a thread makes no sense. It doesn't harm, but is kind of waste. Use that method only to perform UI related operations.
You should try using "sms://:0" as param for Connector.open. According to the docs, a parameter with the form {protocol}://[{host}]:[{port}] will open the connection in client mode (which makes sense, since you are on the receiving part), whereas not including the host part will open it in server mode.
Finally, if you can't get it working, you could use instead the third method specified in this tutorial, which you probably have already read.
The error you quoted is complaining about the use of the String constructor that takes a string argument. Since strings are immutable in Java-ME, this is just a waste. You can use the argument string directly:
Invocation of questionable method: java.lang.String.(String) found in: mypackage.MyApp$ListeningThread.run()
//String address = new String(d.getAddress());
String address = d.getAddress();
// getData() returns a byte[], so this is a different constructor
// However, this leaves the character encoding unspecified, so it
// will default to cp1252, which may not be what you want
String msg = new String(d.getData());

Resources