Handling multipart attachments in CXF APIs - cxf

I am trying to develop an API call using Apache CXF that takes in an attachment along with the request. I followed this tutorial and this is what I have got so far.
#POST
#Path("/upload")
#RequireAuthentication(false)
public Response uploadWadl(MultipartBody multipartBody){
List<Attachment> attachments = multipartBody.getAllAttachments();
DataHandler dataHandler = attachments.get(0).getDataHandler();
try {
InputStream is = dataHandler.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
return Response("OK");
}
I am getting an InputStream object to the attachment and everything is working fine. However I need to pass the attachment as a java.io.File object to another function. I know I can create a file here, read from the inputstream and write to it. But is there a better solution? Has the CXF already stored it as a File? If so I could just go ahead and use that. Any suggestions?

I'm also interested on this matter. While discussing with Sergey on the CXF mailing list, I learned that CXF is using a temporary file if the attachment is over a certain threshold.
In the process I discovered this blogpost that explains how to use CXF attachment safely.
You can be interested by the exemple on this page as well.
That's all I can say at the moment as I'm investigating right now, I hope that helps.
EDIT : At the moment here's how we handle attachment with CXF 2.6.x. About uploading a file using multipart content type.
In our REST resource we have defined the following method :
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Path("/")
public Response archive(
#Multipart(value = "title", required = false) String title,
#Multipart(value = "hash", required = false) #Hash(optional = true) String hash,
#Multipart(value = "file") #NotNull Attachment attachment) {
...
IncomingFile incomingFile = attachment.getObject(IncomingFile.class);
...
}
A few notes on that snippet :
#Multipart is not standard to JAXRS, it's not even in JAXRS 2, it's part of CXF.
In our code we have implemented bean validation (you have to do it yourself in JAXRS 1)
You don't have to use a MultipartBody, the key here is to use an argument of type Attachment
So yes as far as we know there is not yet a possibility to get directly the type we want in the method signature. So for example if you just want the InputStream of the attachment you cannot put it in the signature of the method. You have to use the org.apache.cxf.jaxrs.ext.multipart.Attachment type and write the following statement :
InputStream inputStream = attachment.getObject(InputStream.class);
Also we discovered with the help of Sergey Beryozkin that we could transform or wrap this InputStream, that's why in the above snippet we wrote :
IncomingFile incomingFile = attachment.getObject(IncomingFile.class);
IncomingFile is our custom wrapper around the InputStream, for that you have to register a MessageBodyReader, ParamHandler won't help as they don't work with streams but with String.
#Component
#Provider
#Consumes
public class IncomingFileAttachmentProvider implements MessageBodyReader<IncomingFile> {
#Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return type != null && type.isAssignableFrom(IncomingFile.class);
}
#Override
public IncomingFile readFrom(Class<IncomingFile> type,
Type genericType,
Annotation[] annotations,
MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream entityStream
) throws IOException, WebApplicationException {
return createIncomingFile(entityStream, fixedContentHeaders(httpHeaders)); // the code that will return an IncomingFile
}
}
Note however that there have been a few trials to understand what was passed, how, and the way to hot-fix bugs (For example the first letter of the first header of the attachment part was eat so you had ontent-Type instead of Content-Type).
Of course the entityStream represents the actual InputStream of the attachment. This stream will read data either from memory or from disk, depending on where CXF put the data ; there is a size threshold property (attachment-memory-threshold) for that matter. You can also say where the temporary attachments will go (attachment-directory).
Just don't forget to close the stream when you are done (some tool do it for you).
Once everything was configured we tested it with Rest-Assured from Johan Haleby. (Some code are part of our test utils though) :
given().log().all()
.multiPart("title", "the.title")
.multiPart("file", file.getName(), file.getBytes(), file.getMimeType())
.expect().log().all()
.statusCode(200)
.body("store_event_id", equalTo("1111111111"))
.when()
.post(host().base().endWith("/store").toStringUrl());
Or if you need to upload the file via curl in such a way :
curl --trace -v -k -f
--header "Authorization: Bearer b46704ff-fd1d-4225-9dd4-e29065532b73"
--header "Content-Type: multipart/form-data"
--form "hash={SHA256}3e954efb149aeaa99e321ffe6fd581f84d5a497b6fab5c86e0d5ab20201f7eb5"
--form "title=fantastic-video.mp4"
--form "archive=#/the/path/to/the/file/fantastic-video.mp4;type=video/mp4"
-X POST http://localhost:8080/api/video/event/store
To finish this answer, I'd like to mention it is possible to have JSON payload in multipart, for that you can use an Attachment type in the signature and then write
Book book = attachment.getObject(Book.class)
Or you can write an argument like :
#Multipart(value="book", type="application/json") Book book
Just don't forget to add the Content-Type header to the relevant part when performing the request.
It might be worth to say that it is possible to have all the parts in a list, just write a method with a single argument of type List<Attachment>. However I prefer to have the actual arguments in the method signature as it's cleaner and less boilerplate.
#POST
void takeAllParts(List<Attachment> attachments)

Related

Wildcards in GCS bucket Java client api

Using Wildcards in file name i am trying to read files from GCS bucket.
in gsutil command line wildcards is working in specifying file names.
but in java client api
GcsFilename filename = new GcsFilename(BUCKETNAME, "big*");
it is searching for file named "big*" instead of file starting with big .
please help me how i can use Wildcards in GCSFilename.
Thanks in advance.
Wildcard characters are a feature of gsutil, but they're not an inherent part of the Google Cloud Storage API. You can, however, handle this the same way that gsutil does.
If you want to find the name of every object that begins with a certain prefix, Google Cloud Storage's APIs provide a list method with a "prefix" argument. Only objects matching the prefix will be returned. This doesn't work for arbitrary regular expressions, but it will work for your example.
The documentation for the list method goes into more detail.
As Brandon Yarbrough mentioned, GcsFilename represent a name of a single GCS Object, which could include any valid UTF-8 character [excluding a few such as \r \n but including '*' though
not recommended). see https://developers.google.com/storage/docs/bucketnaming#objectnames for more info.
GAE GCS client does not support listing yet (though that is planned to be added), so for now you can use the GCS XML or JSON API directly (using urlfetch) or use the Java GCS api client, https://developers.google.com/api-client-library/java/apis/storage/v1
See example for the latter option:
public class ListServlet extends HttpServlet {
public static final List<String> OAUTH_SCOPES =
ImmutableList.of("https://www.googleapis.com/auth/devstorage.read_write");
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
String bucket = req.getParameter("bucket");
AppIdentityCredential cred = new AppIdentityCredential(OAUTH_SCOPES);
Storage storage = new Storage.Builder(new UrlFetchTransport(), new JacksonFactory(), cred)
.setApplicationName(SystemProperty.applicationId.get()).build();
Objects.List list = storage.objects().list(bucket);
for (StorageObject o : list.execute().getItems()) {
resp.getWriter().println(o.getName() + " -> " + o);
}
} catch (Exception ex) {
throw new ServletException(ex);
}
}
}

Put modified xml back into the message?

I'm using CXF to send messages with SOAP over JMS.
I'm trying to write a CXF Interceptor in the POST_MARSHALL phase.
I want to change some attributes when the xml is generated.
I know i can get the content from the message via
message.getContent(java.io.Writer.class).
This happens to be in the form of JMSConduit$1. Which - I think - is a StringWriter (if I debug my code I can see a buf field).
I can get the xml in String format and make my changes, but the problems is putting it back in the message.
I can not change the JMSConduit$1 to something else, otherwise CXF won't send it to the JMS Endpoint. (it must be a JMSConduit).
I can't find a way to put the modified xml back in a JMSConduit, which i can get through
message.getExchange().getConduit();
So, how can I put my modified xml back into the message/JMSConduit?
Finally found an answer. I used a FilterWriter.
public void handleMessage(Message message) throws Fault {
final Writer writer = message.getContent(Writer.class);
message.setContent(Writer.class, new OutWriter(message, writer));
}
class OutWriter extends FilterWriter {
#Override
public void close() throws IOException {
// Modify String (in xml form).
message.setContent(Writer.class, out);
}
}

Difference between param and payload for Google App Engine Pull Queue

My project is based on GAE/J and utilizing the recent launched PULL queue, but I think the question can also be applied to Python.
Basically, when I put a task into the PULL queue, I need to set some params of the task for the later consumer to pick it up.
I have implemented in the params setting in both ways:
1) By using param():
TaskOptions taskOptions = TaskOptions.Builder.
withMethod(TaskOptions.Method.PULL);
taskOptions.param("param", paramValue);
taskOptions.param("param2", paramValue2);
2) By using payload():
TaskOptions taskOptions = TaskOptions.Builder.
withMethod(TaskOptions.Method.PULL);
taskOptions.payload("payloadValue");
Both approaches are working, however, what I would like to know is what's the differences between the two, and which way should be the preferred way in terms of efficiency or convenience.
I can see that by using param(), it is easy to set multiple parameters and also easy to retrieve the parameters for the consumer.
But for one parameter cases, then payload may come in more handy as it saves the code to catch Exceptions throwing out when the consumer extract parameters.
However, I would be happy to know any more differences between these two apart from what I have menitoned.
Per the python documentation, I would say that in your case is exactly the same.
In PULL requests, Do not specify params if you already specified a payload. Params are encoded as application/x-www-form-urlencoded and set to the payload.
There is difference in .param() and .payload() functions of TaskOptions. You can use these functions as follows;
taskOptions.param("param1","Invoice_3344");
Now at receiver end, lets say you are calling a servlet , the in the HttpRequest, you can receive the sent parameters as request parameter.
public class MyInvoiceTask extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String invoiceNum = request.getParameter("param1");
}
}
Now Assume you wanted to serialize your entire custom class object which has huge data. In such case you would need to user .payload() function as internally, it send the payload data in a request body.
//**Custom class object
Person person = new Person("Abc", "Mumbai", 22);
//**Convert the object into JSON so that can be converted into String(required for Payload)
//**Use Gson library
Gson gson = new Gson();
String personObjString = gson.toJson(person);
//**put the payload in task option as byte array
taskOption.payload(personObjString.toByteArray());
Now at receiver end lets say using servlet, then from HttpRequest object, we would need to get the payload byte array and convert it back into cutsom object i.e. "Person" class object in our case.
private byte[] getPayloadFromHttpRequest(HttpServletRequest req) throws IOException
{
InputStream inputStream = req.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int length;
byte[] buffer = new byte[1024];
while ((length = inputStream.read(buffer)) >= 0)
byteArrayOutputStream.write(buffer, 0, length);
if (byteArrayOutputStream.size() > 0){
return byteArrayOutputStream.toByteArray();
}
return null;
}
//**Now this received byteArray can be used with Gson to convert it back into Person object
byte[] payload = getPayloadFromHttpRequest(request);
Gson gson = new Gson();
String personJsonString = new String(payload);
Person person = gson.fromJson(personJsonString, Person.class);

JAX-RS with CXF / rest-assured: Handling multiparam file upload

I want to upload a JPG file and a JSON-serialized Java object. On the server I am using Apache CXF, on the client I am integration testing with rest-assured.
My server code looks like:
#POST
#Path("/document")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response storeTravelDocument(
#Context UriInfo uriInfo,
#Multipart(value = "document") JsonBean bean,
#Multipart(value = "image") InputStream pictureStream)
throws IOException
{}
My client code looks like:
given().
multiPart("document", new File("./data/json.txt"), "application/json").
multiPart("image", new File("./data/image.txt"), "image/jpeg").
expect().
statusCode(Response.Status.CREATED.getStatusCode()).
when().
post("/document");
Everything works fine when I read the json part from the file as in the first multiPart line. However, when I want to serialize the json instance I come into problems. I tried many variants, but none worked.
I thought this variant should work: on the client
JsonBean json = new JsonBean();
json.setVal1("Value 1");
json.setVal2("Value 2");
given().
contentType("application/json").
formParam("document", json).
multiPart("image", new File("./data/image.txt"), "image/jpeg").
...
and on the server
public Response storeTravelDocument(
#Context UriInfo uriInfo,
#FormParam(value = "document") JsonBean bean,
#Multipart(value = "image") InputStream pictureStream)
but no. Can anyone tell me how it should be?
Try different approach (worked for me), I am not sure if this is suitable in your case.
Make JsonBean a JAXB entity, that it add #XmlRootEntity above class definition.
Then, instead of formParam
given().
contentType("application/json").
body(bean). //bean is your JsonBean
multiPart("image", new File("./data/image.txt"), "image/jpeg").
then
public Response storeTravelDocument(
#Context UriInfo uriInfo,
JsonBean bean, //should be deserialized properly
#Multipart(value = "image") InputStream pictureStream)
I've never tried that with #Multipart part, but, hopefully it would work.
Multipart/form-data follows the rules of multipart MIME data streams, see w3.org. This means that each part of the request forms a part in the stream. Rest-assured supports already simple fields (strings), files and streams, but not object serialization into a part. After asking on the mailing list, Johan Haleby (the author of rest-assured) suggested to add an issue. The issue is already accepted, see issue 166.
The server will stay as it is:
#POST
#Path("/document")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response storeTravelDocument(
#Context UriInfo uriInfo,
#Multipart(value = "document") JsonBean bean,
#Multipart(value = "image") InputStream pictureStream)
throws IOException
{}
The client code will look like:
given().
multiPartObject("document", objectToSerialize, "application/json").
multiPart("image", new File("./data/image.txt"), "image/jpeg").
expect().
statusCode(Response.Status.CREATED.getStatusCode()).
when().
post("/document");
Maybe the name "multiPartObject" will change. We will see once it is implemented.

Display dynamic image from database or remote source with p:graphicImage and StreamedContent

I'm trying to display image bytes which is saved in database as a StreamedContent in the <p:graphicImage> as follows:
<p:graphicImage value="#{item.imageF}" width="50" id="grpImage" height="80"/>
private StreamedContent content; // getter and setter
public StreamedContent getImageF() {
if (student.getImage() != null) {
InputStream is = new ByteArrayInputStream(student.getImage());
System.out.println("Byte :"+student.getImage());
content = new DefaultStreamedContent(is, "", student.getStuID());
System.out.println("ddd ------------------------------- " + content);
return content;
}
return content;
}
This returns a blank image. How is this caused and how can I solve it?
The stdout prints the following:
INFO: Byte :[B#a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#b0887b
INFO: Byte :[B#a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#1d06a92
INFO: Byte :[B#d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#39a60
INFO: Byte :[B#d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#8c3daa
INFO: Byte :[B#124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#1dbe05b
INFO: Byte :[B#124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#66a266
INFO: Byte :[B#a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#1293976
INFO: Byte :[B#a2fb48
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#17b7399
INFO: Byte :[B#d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#1e245a5
INFO: Byte :[B#d52f0b
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#4a7153
INFO: Byte :[B#124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#1561bfd
INFO: Byte :[B#124728a
INFO: ddd ------------------------------- org.primefaces.model.DefaultStreamedContent#47a8c2
The <p:graphicImage> requires a special getter method. It will namely be invoked twice per generated image, each in a completely different HTTP request.
The first HTTP request, which has requested the HTML result of a JSF page, will invoke the getter for the first time in order to generate the HTML <img> element with the right unique and auto-generated URL in the src attribute which contains information about which bean and getter exactly should be invoked whenever the webbrowser is about to request the image. Note that the getter does at this moment not need to return the image's contents. It would not be used in any way as that's not how HTML works (images are not "inlined" in HTML output, but they are instead requested separately).
Once the webbrowser retrieves the HTML result as HTTP response, it will parse the HTML source in order to present the result visually to the enduser. Once the webbrowser encounters an <img> element during parsing the HTML source, then it will send a brand new HTTP request on the URL as specified in its src attribute in order to download the content of that image and embed it in the visual presentation. This will invoke the getter method for the second time which in turn should return the actual image content.
In your particular case PrimeFaces was apparently either unable to identify and invoke the getter in order to retrieve the actual image content, or the getter didn't return the expected image content. The usage of #{item} variable name and the lot of calls in the log suggests that you were using it in an <ui:repeat> or a <h:dataTable>. Most likely the backing bean is request scoped and the datamodel isn't properly preserved during the request for the image and JSF won't be able to invoke the getter during the right iteration round. A view scoped bean would also not work as the JSF view state is nowhere available when the browser actually requests the image.
To solve this problem, your best bet is to rewrite the getter method as such so that it can be invoked on a per-request basis wherein you pass the unique image identifier as a <f:param> instead of relying on some backing bean properties which may go "out of sync" during subsequent HTTP requests. It would make completely sense to use a separate application scoped managed bean for this which doesn't have any state. Moreover, an InputStream can be read only once, not multiple times.
In other words: never declare StreamedContent nor any InputStream or even UploadedFile as a bean property; only create it brand-new in the getter of a stateless #ApplicationScoped bean when the webbrowser actually requests the image content.
E.g.
<p:dataTable value="#{bean.students}" var="student">
<p:column>
<p:graphicImage value="#{studentImages.image}">
<f:param name="studentId" value="#{student.id}" />
</p:graphicImage>
</p:column>
</p:dataTable>
Where the StudentImages backing bean can look like this:
#Named // Or #ManagedBean
#ApplicationScoped
public class StudentImages {
#EJB
private StudentService service;
public StreamedContent getImage() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
}
else {
// So, browser is requesting the image. Return a real StreamedContent with the image bytes.
String studentId = context.getExternalContext().getRequestParameterMap().get("studentId");
Student student = studentService.find(Long.valueOf(studentId));
return new DefaultStreamedContent(new ByteArrayInputStream(student.getImage()));
}
}
}
Please note that this is a very special case wherein performing business logic in a getter method is completely legit, considering how the <p:graphicImage> works under the covers. Invoking business logic in getters is namely usually frowned upon, see also Why JSF calls getters multiple times. Don't use this special case as excuse for other standard (non-special) cases. Please also note that you can't make use of EL 2.2 feature of passing method arguments like so #{studentImages.image(student.id)} because this argument won't end up in the image URL. Thus you really need to pass them as <f:param>.
If you happen to use OmniFaces 2.0 or newer, then consider using its <o:graphicImage> instead which can be used more intuitively, with an application scoped getter method directly delegating to the service method and supporting EL 2.2 method arguments.
Thus so:
<p:dataTable value="#{bean.students}" var="student">
<p:column>
<o:graphicImage value="#{studentImages.getImage(student.id)}" />
</p:column>
</p:dataTable>
With
#Named // Or #ManagedBean
#ApplicationScoped
public class StudentImages {
#EJB
private StudentService service;
public byte[] getImage(Long studentId) {
return studentService.find(studentId).getImage();
}
}
See also the blog on the subject.
Try including a mime type. In your posted example, you have it as "". The blank image may be because it doesn't recognize the stream as a image file since you made that field an empty string. So add a mime type of image/png or image/jpg and see if that works:
String mimeType = "image/jpg";
StreamedContent file = new DefaultStreamedContent(bytes, mimeType, filename);
There's a couple possibilities here (and please post the entire class if this isn't it).
1) You're not initializing the image properly
2) Your stream is empty so you're getting nothing
I'm assuming student.getImage() has a signature of byte[] so first make sure that that data is actually intact and represents an image. Secondly--you're not specifying a content-type which should be "image/jpg" or whatever you're using.
Here's some boilerplate code to check it with, I'm using Primefaces 2 for this.
/** 'test' package with 'test/test.png' on the path */
#RequestScoped
#ManagedBean(name="imageBean")
public class ImageBean
{
private DefaultStreamedContent content;
public StreamedContent getContent()
{
if(content == null)
{
/* use your database call here */
BufferedInputStream in = new BufferedInputStream(ImageBean.class.getClassLoader().getResourceAsStream("test/test.png"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
int val = -1;
/* this is a simple test method to double check values from the stream */
try
{
while((val = in.read()) != -1)
out.write(val);
}
catch(IOException e)
{
e.printStackTrace();
}
byte[] bytes = out.toByteArray();
System.out.println("Bytes -> " + bytes.length);
content = new DefaultStreamedContent(new ByteArrayInputStream(bytes), "image/png", "test.png");
}
return content;
}
}
and some markup...
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.prime.com.tr/ui"
>
<h:head>
</h:head>
<h:body>
<p:graphicImage value="#{imageBean.content}" />
</h:body>
</html>
If that code works then you're set up properly. Despite the fact it is garbage code for the streams (don't use it in production) it should give you a point to troubleshoot from. My guess is that you might have something happening in your JPA or other Database framework where you're byte[] is empty or it is formatted wrong. Alternatively you could just have a content-type problem.
Lastly, I would clone the data from the bean so that student.getImage() would only be copied into a new array and then used. This way if you have something unknown going on (something else moving the object or changing the byte[] you're not messing with your streams.
Do something like:
byte[] data = new byte[student.getImage().length]
for(int i = 0; i < data.length; i++)
data[i] = student.getImage()[i];
so that your bean has a copy (or Arrays.copy()--whatever floats your boat). I can't stress enough how something simple like this/content type is usually what's wrong. Good luck with it.
The answer from BalusC is (as usual) the correct one.
But keep one thing (as already stated by him) in mind. The final request is done from the browser to get the URL from the constructed <img> tag. This is not done in a 'jsf context'.
So if you try to e.g. access the phaseId (logging or whatever reason)
context.getCurrentPhaseId().getName()
This will result in a NullPointerException and the somehow misleading error message you will get is:
org.primefaces.application.resource.StreamedContentHandler () - Error in streaming dynamic resource. Error reading 'image' on type a.b.SomeBean
It took me quite some time to figure out what was the problem.

Resources