I've created a custom command to retrieve multiple objects in the same request (in order to solve some performance issues), instead of using the folder method .getMessage(..) which in my case retrieved an ImapMessage object:
Argument args = new Argument();
args.writeString(Integer.toString(start) + ":" + Integer.toString(end));
args.writeString("BODY[]");
FetchResponse fetch;
BODY body;
MimeMessage mm;
ByteArrayInputStream is = null;
Response[] r = protocol.command("FETCH", args);
Response status = r[r.length-1];
if(status.isOK()) {
for (int i = 0; i < r.length - 1; i++) {
...
}
}
Currently I'm validating if the object is a ImapResponse like this:
if (r[i] instanceof IMAPResponse) {
IMAPResponse imr = (IMAPResponse)r[i];
My question is, how can I turn this response into an ImapMessage?
Thank you.
Are you trying to download the entire message content for multiple messages at once? Have you tried using IMAPFolder.FetchProfileItem.MESSAGE? That will cause Folder.fetch to download the entire message content, which you can then access using the Message objects.
I haven't succeeded yet to convert it into a IMAPMessage but I'm now able transform it into a MIME Message. It isn't perfect but I guess it will have to work for now:
FetchResponse fetch = (FetchResponse) r[i];
BODY body = (BODY) fetch.getItem(0);
ByteArrayInputStream is = body.getByteArrayInputStream();
MimeMessage mm = new MimeMessage(session, is);
Then, it can be used to get information like this:
String contentType = mm.getContentType();
Object contentObject = mm.getContent();
There are also other methods to get information like the sender, date, etc.
Related
I need to copy the entire body content of an Internet mail to a new Notes document.
Object internetMsgBody = internetMsg.getContent();
MIMEEntity notesBodyItem = notesDocument.createMIMEEntity("Body");
Stream mimeStream = dominoSession.createStream();
...?...
notesBodyItem.setContentFromBytes(mimeStream, msgContentType,
MIMEEntity.ENC_NONE);
The internetMsgBody can be String, MimeMultiPart or InputStream (according to the documentation). I know how I can handle String :) but for the other Object types, I need some help. There is no need for any MIME or Parts content type handling.
Thanks!
In Notes, a multi-part MIME message is represented as a set of items of the same name (usually Body) each of which is TYPE_MIME. Think of a MIMEEntity instance as corresponding to a single one of these items. In other words, a MIMEEntity represents a single MIME part.
So if your input is a multi-part MIME message, you may have to parse the message into individual parts and create a MIMEEntity for each. Unfortunately, the Java back-end classes don't include a MIME parser. Of course, your question states that internetMsg.getContent() may return a MimeMultipart. If that's the case, it sounds like the MIME is already parsed for you.
Either way -- whether your input is a stream of many parts or a MimeMultipart -- I suggest you look at MimeMessageParser.java from the XPages Extension Library. It uses mime4j to parse an input stream. If there are multiple parts in the stream, it uses MIMEEntity to write each part as a separate item. Although the use of mime4j doesn't sound relevant, you may find some useful hints in that code. It implements a very similar use case.
Thanks Dave, you pointed me to the missing part. My solution (so far) is to create a parent Domino MIMEEntity which holds all MIME Parts as children.
Code excerpt:
MimeMultipart mimeMultiparts = (MimeMultipart) message.getContent();
int partCount = mimeMultiparts.getCount();
MIMEEntity dominoParentItem = mailDocument.createMIMEEntity("Body");
Stream dominoStream = dbGetSession().createStream();
for (int counter = 0; counter < partCount; counter++) {
MimeBodyPart mimeBodyPart = (MimeBodyPart) mimeMultiparts.getBodyPart(counter);
MIMEEntity dominoChildItem = dominoParentItem.createChildEntity();
InputStream input = mimeBodyPart.getRawInputStream();
byte[] buffer = new byte[4096];
int lengthTotal = 0;
int length = 0;
while (true) {
length = input.read(buffer);
if (length < 1)
break;
lengthTotal += length;
dominoStream.write(buffer);
}
String encodingType = mimeBodyPart.getEncoding();
int dominoEncoding = MIMEEntity.ENC_NONE;
if (encodingType != null) {
if (encodingType.toLowerCase().contains("base64"))
dominoEncoding = MIMEEntity.ENC_BASE64;
if (encodingType.toLowerCase().contains("7bit"))
dominoEncoding = MIMEEntity.ENC_IDENTITY_7BIT;
if (encodingType.toLowerCase().contains("8bit"))
dominoEncoding = MIMEEntity.ENC_IDENTITY_8BIT;
if (encodingType.toLowerCase().contains("binary"))
dominoEncoding = MIMEEntity.ENC_IDENTITY_BINARY;
if (encodingType.toLowerCase().contains("quoted-printable"))
dominoEncoding = MIMEEntity.ENC_QUOTED_PRINTABLE;
if (dominoEncoding == MIMEEntity.ENC_NONE)
dominoEncoding = MIMEEntity.ENC_EXTENSION;
}
dominoChildItem.setContentFromBytes(dominoStream, mimeBodyPart.getContentType(), dominoEncoding);
Just to follow up on this problem:
I came up with a much easier and more elegant solution. There is a writeTo() method which streams-out all the multipart data. This can then be streamed in again to a Domino stream which fills in the MIMEEntry body item.
case "javax.mail.internet.MimeMultipart": {
// Create input stream with content of MIME data
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
message.writeTo(outputStream);
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
outputStream.close();
// Create Domino stream and fill it with the MIME data
Stream dominoStream = dbGetSession().createStream();
byte[] buffer = new byte[4096];
int lengthTotal = 0;
int length = 0;
while (true) {
length = inputStream.read(buffer);
if (length < 1)
break;
lengthTotal += length;
dominoStream.write(buffer);
}
inputStream.close();
// Create Domino MIME "Body" item with content of MIME data
MIMEEntity dominoMIMEItem = mailDocument.createMIMEEntity("Body");
dominoMIMEItem.setContentFromBytes(dominoStream, "", MIMEEntity.ENC_NONE);
}
When my page gets hit from a third party page, I get the below data in request payload:
Content-type: multipart/form-data, boundary----------14048
Content-Length = 590
----------14048
Content-disposition: form-data; name ="xyz"
{"abc":"lmn","def":"ghi"}
----------14048
I need to read the JSON string from this parameter in my Java class. How can I do that?
My current code looks like this:
IRequestParameters requestParameters = getRequest().getPostParameters();
if (requestParameters != null && requestParameters.getParameterNames().contains( "abc" )&&requestParameters.getParameterValue( "abc" ) != null){
value = requestParameters.getParameterValue( "abc" ).toString();
}
Thanks in advance.
First, you need to parse multipart form data in Wicket:
MultipartServletWebRequest multiPartRequest =
webRequest.newMultipartWebRequest(getMaxSize(), "ignored");
// multiPartRequest.parseFileParts(); // this is needed after Wicket 6.19.0+
IRequestParameters params = multiPartRequest.getRequestParameters();
Then you need to parse the JSON fragment, one way to do that is by using org.json.
import org.json.*;
JSONObject jsondict = new JSONObject(params.getParameter("xyz");
Then you need to get the JSON parameter you are interested in:
string payload = jsondict.getString("abc");
The below code works fine for me.
HttpSevletRequest request = (HttpSevletRequest )getRequest.getContainerRequest();
try{
InputStreamReader inputReader = new InputStreamReader(request.getInputStream());
BufferedReader reader = new BufferedReader(inputReader );
for(String line;(line = reader.readLine())!=null;){
if(line.contains("abc")){
//perform task....
}
}
}catch(IOException e){
//logs
}
I have some controller (ExampleController) that receives requests with content-type application/x-www-form-urlencoded.
I need to send all the request data to a different URL using POST request. The data needs to be in the same order as received it.
Problem is that the content does not match because request.getParameterMap() destroys the order of the data.
In ExampleController:
def method(){
String s = request.reader.text //this is empty, need a way to read this text
Map<String, String[]> vars = request.getParameterMap() //it's not good for me because the map is unordered map
//but it full of data
}
which does not work.
I need something like:
byte[] data = request.getRequestData()
wr.write(data)
btw i've tried:
InputStream = request.getInputStream()
byte [] bytes = inputStream.getBytes()
I've also tried
String s = request.reader.text
but the string is empty.
I think the main problem is that grails mechanism reads the input stream before the controller even starts and place the data in the parameters hashMap. is there a way to undo it?
Any help will be greatly appreciated
Try using request.reader.text instead.
def result = request.reader.text.split('&').inject([:]) { map, token ->
token.split('=').with { map[it[0]] = it[1] }
map
}
I have been doing some work with apache CXF(version 2.2.2) JAX-RS. I am trying to introduce data validation layer in CXF request handler before business method be invoked. Fortunately :), I am encountering an issue on input parameter handling in request handler(DataValidationHandler). I can read the JSON Object manually by following code lines in request handler. But it's duplicated with JSONProvider registered in CXF framework. Because JSON object input stream only can be read once, otherwise we will meet exception "java.io.EOFException: No content to map to Object due to end of input". Moreover, duplicated JSON object deserializing will impacts performance. Following code is sample for your reference.
Read JSON Object from HTTP body manually:
OperationResourceInfo ori = paramMessage.getExchange().get(OperationResourceInfo.class);
MultivaluedMap<String, String> values = new MetadataMap<String, String>();
List<Object> objList = JAXRSUtils.processParameters(ori, values, paramMessage);
Register JSONProvider in CXF JAX-RS framework:
<bean id="JSONProvider" class="com.accela.govxml2.jaxrs.util.JSONProvider"></bean>
Read JSON Object to Java Object from input stream:
public Object readFrom(......){
ObjectMapper objectMapper = new ObjectMapper();
Object result = objectMapper.readValue(entityStream, TypeFactory.defaultInstance().constructType(genericType));
Return result;
}
I am dealing with Path Parameter manually by following code lines.
OperationResourceInfo ori = paramMessage.getExchange().get(OperationResourceInfo.class);
URITemplate t1 = ori.getClassResourceInfo().getURITemplate();
URITemplate t2 = ori.getURITemplate();
UriInfo uriInfo = new UriInfoImpl(paramMessage, null);
MultivaluedMap<String, String> map = new MetadataMap<String, String>();
t1.match(uriInfo.getPath(), map);
String str = map.get(URITemplate.FINAL_MATCH_GROUP).get(0);
t2.match(str, map);
String pathParameter= null;
if (map.containsKey("pathParam") && !ValidationUtil.isEmpty(map.get("pathParam")))
{
pathParameter= map.get("pathParam").get(0);
}
My questions are here:
How to deal with POST/PUT input parameter of http body in request handler in general?
How to avoid performance issue to read input parameter efficiently?
Is there any way to inject the validation (handler/interceptor) layer between parameter reading by CXF(JSONProvider) and business method invoking?
Is there any elegant way to deal with path parameter?
Thanks for your help. Any comments & suggestions will be appreciated.
Regards,
Dylan
I have found another way to inject DataValidation Interceptor into reading parameter phase. We can reuse deserialized input model from message content, which be deserialized by JSONProvider registered in framework. It can improve performance, because only deserialize input model once.
public class DataValidationInInterceptor extends AbstractPhaseInterceptor<Message>
{
public DataValidationInInterceptor()
{
super(Phase.READ);
}
#Override
public void handleMessage(Message message)
{
OperationResourceInfo ori = message.getExchange().get(OperationResourceInfo.class);
Method method = ori.getMethodToInvoke();
Class<?>[] types = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (int i = 0; i < types.length; i++)
{
Class<?> type = types[i];
List obj = (List) message.getContent(List.class);
System.out.println(obj);
System.out.println(type);
}
}
}
After researching, I can read the input stream twice based on the following question's answer ( Read stream twice ).
However, JSON object deserializing performance is still my concern. Who has better solution for it?
Intercept request and change message content from CoyoteInputStream to ByteArrayInputStream, so I can read the InputStream twice.
InputStream in = message.getContent(InputStream.class);
if (in != null)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(in, baos);
byte[] bytes = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
message.setContent(InputStream.class, bais);
}
Reset ByteArrayInputStream before Read JSON Object to Java Object from input stream:
public Object readFrom(......){
ObjectMapper objectMapper = new ObjectMapper();
if (entityStream.markSupported())
{
entityStream.reset();
}
Object result = objectMapper.readValue(entityStream, TypeFactory.defaultInstance().constructType(genericType));
return result;
}
I'm writing a small proxy in Java that basically picks out 2 specific files and does some extra processing on them. One URL it just grabs some info out of the content before passing it along. The other file I want to filter the response content, which is just xml deflate encoded (I want to remove some child elements).
Now, the proxy works fine when I just pass though all content. However, when I try to filter the xml file it doesn't actually send the content to the client ???
Here is some code:
Within the Thread run() method that is spawned when accepting a Socket connection, once I determine the request is for the file I want to filter, I call:
filterRaceFile(serverIn, clientOut); // This won't send content
//streamHTTPData(serverIn, clientOut, true); // This passes through fine (but all content of course).
and here is the filtering method itself:
private void filterRaceFile(InputStream is, OutputStream os) {
// Pass through headers before using deflate and filtering xml
processHeader(is, os, new StringBuilder(), new StringBuilder());
// Seems to be 1 line left, inflater doesn't like it if we don't do this anyway...?
try {
os.write(readLine(is, false).getBytes());
} catch (IOException e) {
e.printStackTrace();
}
InflaterInputStream inflate = new InflaterInputStream(is);
DeflaterOutputStream deflate = new DeflaterOutputStream(os);
int c = 0;
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document xdoc = db.parse(inflate);
Node id = xdoc.getElementsByTagName("id").item(0);
Node msg = xdoc.getElementsByTagName("message").item(0);
StringBuilder xml_buf = new StringBuilder("<race>\n");
xml_buf.append("<id>").append(id.getTextContent()).append("</id>\n");
xml_buf.append("<message>").append(msg.getTextContent()).append("</message>\n");
xml_buf.append("<boats>\n");
NodeList allBoats = xdoc.getElementsByTagName("boat");
int N = allBoats.getLength();
for (int i = 0; i < N; ++i)
{
Node boat = allBoats.item(i);
Element boat_el = (Element)boat;
double lat = Double.parseDouble(boat_el.getElementsByTagName("lat").item(0).getTextContent());
double lon = Double.parseDouble(boat_el.getElementsByTagName("lon").item(0).getTextContent());
double dist = Geodesic.vincenty_earth_dist(proxy.userLat, proxy.userLon, lat, lon)[0];
if (dist <= LOS_DIST)
{
String boatXML = xmlToString(boat);
//<?xml version="1.0" encoding="UTF-8"?> is prepended to the xml
int pos = boatXML.indexOf("?>")+2;
boatXML = boatXML.substring(pos);
xml_buf.append(boatXML);
++c;
}
}
System.out.printf("%d boats within LOS distance\n", c);
xml_buf.append("</boats>\n");
xml_buf.append("</race>");
byte[] xml_bytes = xml_buf.toString().getBytes("UTF-8");
deflate.write(xml_bytes);
} catch (Exception e) {
e.printStackTrace();
}
// flush the OutputStream and return
try {
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
I also have a simple pass through method that simply writes the server's InputStream to the client's OutputStream, uses readLine also and works fine - ie any other url shows up in the browser no problems, so readLine is ok. The boolean parameter is to let it know it is reading from a deflate stream, as it uses mark and read internally, which isn't supported on deflate streams.
XML is very simple:
<race>
{some data to always pass thru}
<boats>
<boat>
<id>1234</id>
....
<lat>-23.3456</lat>
<lon>17.2345</lon>
</boat>
{more boat elements}
</boats>
</race>
And it produces the xml I want it to send to the client fine, but the client just doesn't receive it (shows content-length of 0 in a web-debugger, although there is no Content-Length header in the original response either)
Any ideas as to what is going on, or what I should be doing that I am not ??
I don't see any error, but if you are already using a Document, can't you just modify the document and then use your XML library to write the whole Document out? This seems more robust than manual XML formatting.
You need to add a call to deflate.close() after deflate.write() in order to flush the output and correctly close the stream.