How to access the asset in workflow process step - java

I am trying to write a workflow process step for the DAM update asset such that the uploaded asset will be sent to an external service that will modify the asset and then the modified asset can be sent to the Metadata extraction step. So I've added my process step to the DAM update asset like this:
And my code looks like this so far:
public void execute(WorkItem item, WorkflowSession wfsession,MetaDataMap args) throws WorkflowException {
try
{
log.info("Here2 in execute method"); //ensure that the execute method is invoked
final Map<String, Object> map = new HashMap<String, Object>();
map.put( "user.jcr.session", wfsession.getSession());
ResourceResolver rr = resolverFactory.getResourceResolver(map);
String path = item.getWorkflowData().getPayload().toString();
log.info("Here2 path: " + path);
Resource resource = rr.getResource(path);
log.info("Here2 resource: " + resource);
InputStream is = resource.adaptTo(InputStream.class);
log.info("Here2 assets IS: " + is);
}
catch (Exception e)
{
log.info("Here Error");
e.printStackTrace();
}
}
This is what I see in the logs when I upload an asset:
Here2 in execute method
Here2 path: /content/dam/photo1.JPG/jcr:content/renditions/original
Here2 asset: null
Question
My external service has an API accepting requests over HTTP. How should I send over the asset to the external service?
Once the external service modifies the asset, what should I do so that the Metadata extraction step reads the modified asset instead of the original?

In order to access your external service via HTTP, you have to write a client. CQ provides commons-httpclient bundle and you may use it to access the service. Documentation for the library can be found here. I don't know if the service expects that the file will be send using PUT or POST, but httpclient provides all these methods. All you have to do is to provide appropriate InputStream. Adapt your resource to Rendition and use getStream() method to get the InputStream.
When you'll get the modified asset from the webservice, you need to replace the original one:
// rendition = ...; // original rendition object created as above
// newInputStream = ...; // new asset received from your webservice
Asset asset = rendition.getAsset();
asset.addRendition("original", newInputStream, rendition.getMimeType());

Related

Can static content on spring-boot-web application be dynamic (refreshed)?

I am still searching around this subject, but I cannot find a simple solution, and I don't sure it doesn't exist.
Part 1
I have a service on my application that's generating an excel doc, by the dynamic DB data.
public static void
notiSubscribersToExcel(List<NotificationsSubscriber>
data) {
//generating the file dynamically from DB's data
String prefix = "./src/main/resources/static";
String directoryName = prefix + "/documents/";
String fileName = directoryName + "subscribers_list.xlsx";
File directory = new File(directoryName);
if (! directory.exists()){
directory.mkdir();
// If you require it to make the entire directory path including parents,
// use directory.mkdirs(); here instead.
}
try (OutputStream fileOut = new FileOutputStream(fileName)) {
wb.write(fileOut);
fileOut.close();
wb.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Part 2
I want to access it from the browser, so when I call it will get downloaded.
I know that for the static content, all I need to do is to call to the file, from the browser like that:
http://localhost:8080/documents/myfile.xlsx
After I would be able to do it, all I need is to create link to this url from my client app.
The problem -
Currently if I call to the file as above, it will download only the file which have been there in the compiling stage, but if I am generating a new files after the app is running the content won't be available.
It seems that the content is (as it's called) "static" and cannot be changed after startup.
So my question is
is there is a way to define a folder on the app structure that will be dynamic? I just want to access the new generated file.
BTW I found this answer and others which doing configuration methods, or web services, but I don't want all this. And I have tried some of them, but the result is the same.
FYI I don't bundle my client app with the server app, I run them from different hosts
The problem is to download the file with the dynamic content from a Spring app.
This can be solved with Spring BOOT. Here is the solution as shown in this illustration - when i click Download report, my app generates a dynamic Excel report and its downloaded to the browser:
From a JS, make a get request to a Spring Controller:
function DownloadReport(e){
//Post the values to the controller
window.location="../report" ;
}
Here is the Spring Controller GET Method with /report:
#RequestMapping(value = ["/report"], method = [RequestMethod.GET])
#ResponseBody
fun report(request: HttpServletRequest, response: HttpServletResponse) {
// Call exportExcel to generate an EXCEL doc with data using jxl.Workbook
val excelData = excel.exportExcel(myList)
try {
// Download the report.
val reportName = "ExcelReport.xls"
response.contentType = "application/vnd.ms-excel"
response.setHeader("Content-disposition", "attachment; filename=$reportName")
org.apache.commons.io.IOUtils.copy(excelData, response.outputStream)
response.flushBuffer()
} catch (e: Exception) {
e.printStackTrace()
}
}
This code is implemented in Kotlin - but you can implement it as easily in Java too.

How to write byte or stream to Apache Camel FTP to transfer file

In my code currently, I get data from the database and then I write a file out of the data. I have this kind of camel route and working solution:-
private static final String INPUT_FILE_DIRECTORY_URI = "file:" + System.getProperty("user.home")
+ "/data/cdr/?noop=false";
private static final String SFTP_SERVER = "sftp://" +System.getProperty("user.name")
+ "#sftp_server_url/data/cdr/?privateKeyFile=~/.ssh/id_rsa&passiveMode=true";
from(INPUT_FILE_DIRECTORY_URI)
.streamCaching()
.log("Sending file to local sftp")
.to(SFTP_SERVER);
I don't want to write a file in the local disk. Instead, I want to write file data directly to the SFTP server. I don't know how to do it? But I imagine it should be possible to do it. Can you tell me is it possible? If yes, how to do it?
I managed to solve this problem in another way. It is more suitable for my particular problem.
byte[] csvData = csvStringBuilder.toString().getBytes();
Routes.withProducer(producer)
.withHeader(Exchange.FILE_NAME, myCsvFile.csv)
.withBody(csvData)
.to(SFTP_SERVER).request(byte[].class);
You shouldn't use streamCaching unless you really using it. It store your file in memory, use it if you need to consume multiples times your input.
You can use Jpa component or a custom bean getting your data. Load it from database and then send it to your ftp server.
With Jpa :
#Entity
#NamedQuery(name = "data", query = "select x from Data x where x.id = 1")
public class Data { ... }
After that you can define a consumer uri like this one:
from("jpa://org.examples.Data?consumer.namedQuery=data")
.to("SFTP_SERVER");
EDIT : to convert a list to csv and send it to ftp :
from("jpa://org.examples.Data?consumer.namedQuery=data")
.marshal()
.csv()
.to("sftp://" +System.getProperty("user.name") +
"#sftp_server_url/data/cdr/myFile.csv?" +"privateKeyFile=~/.ssh/id_rsa&passiveMode=true");
See CSV component who convert a list to a csv file.
Yes it is possible :) To do this send the file inputStream in a camel DIRECT component and in the associated route make the copy to FTP. I use this case, to upload a file and directly copy it to ftp with from(directInputStreamName).to(yourFtpUri). This is an sample code :
Your service
#Service
public class FileService {
#Produce(uri = PfnumDownloadConstants.CAMEL_DIRECT_UPLOAD)
private ProducerTemplate producer;
public void sendFileToFtp(File fileToSend, String ftpDestinationUri) throws IOException {
Map<String, Object> headers = new HashMap<>();
//In this variable you can init the ftp destination uri or you can hard code it in route
headers.put("destinationUri", ftpDestinationUri);
//set filename to name your file in ftp
headers.put(Exchange.FILE_NAME_ONLY, file.getName());
InputStream targetStream = new FileInputStream(file);
//send stream as body and list of headers to direct
producer.sendBodyAndHeaders(targetStream, headers);
}
}
Your Camel route
#Component
public class FileUploadRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
//Manage camel exception in a dedicated processor
onException(Exception.class).process(exceptionProcessor).log("error :: ${exception}");
from(CAMEL_DIRECT_UPLOAD)
.log("file copy to ftp '${header.CamelFileNameOnly}' in process")
.toD("file:/mnt?fileName=${header.CamelFileNameOnly}&delete=false")
.log("copy done");
}
}

Azure BlobNotFound when downloading in SpringBoot app

I have a task to make possibility to download simple .txt files from the application using Azure Blob Storage. The code is supposed to work. I didn't write it, but it looks OK to me and from what I'll show later in this post, it really connects to the Azure, and, what's more important, it really works only when I'm testing the app on localhost, but not on the publicly available site.
These are the steps I made:
uploaded files to the storage (the underlined is one of them):
added proper link to the button that should download the attachment via REST API
of course, I've also added reference to the attachment in the database (its ID, name etc.)
here's how it looks on frontend:
And this is what I get:
I've seen somewhere that it might be caused by Azure CORS settings that don't allow the app to access the storage. Here's what I've done so far:
went to portal.azure.com and changed CORS settings like this:
found something about putting some code into the app under this Microsoft link, but it's not Java. I guess there are some analogical ways in Java:
https://blogs.msdn.microsoft.com/windowsazurestorage/2014/02/03/windows-azure-storage-introducing-cors/ . Is it necessary after the CORS rules have been added in the Azure Portal?
Also, I've found information that it may be caused by the storage access permissions. The Public Access Level is set to Container:
Not sure if it gives anything, but these are the container's properties:
What else can be the problem with the BlobNotFound error I receive? Hope I've put enough information here, but if some more is needed say in comment and I'll provide it.
This is the code that's supposed to download the attachment of this method, contained in 3 classes:
Controller class part:
#GetMapping("/download/{id}")
#ResponseStatus(HttpStatus.OK)
public void downloadAttachment(#PathVariable long id, HttpServletResponse response) throws IOException {
dataUploadRequestAttachmentService.downloadStaticAttachment(response, id);
}
Controller service class part:
public void downloadStaticAttachment(HttpServletResponse response, long id) throws IOException {
ArticleAttachment articleAttachment = this.findAttachment(id);
String mimeType = URLConnection.guessContentTypeFromName(articleAttachment.getName());
if (mimeType == null){
mimeType = "application/octet-stream";
}
response.setContentType(mimeType);
response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", articleAttachment.getName()));
azureBlobStorageArticleAttachmentService.downloadArticleAttachment(
articleAttachment.getName(),
articleAttachment.getId(),
response.getOutputStream()
);
}
And the AzureBlobStorageArticleAttachmentService class:
public void downloadArticleAttachment(String attachmentName, Long articleId, OutputStream outputStream) {
try {
CloudBlockBlob blob = container.getBlockBlobReference(String.format("%s_%s", articleId, attachmentName));
blob.download(outputStream);
} catch (URISyntaxException | StorageException e) {
e.printStackTrace();
log.error(String.format("Download article attachment %s error", attachmentName));
}
}
According to your description, please debug to check if you get the correct blob name in the code: CloudBlockBlob blob = container.getBlockBlobReference(String.format("%s_%s", articleId, attachmentName));
Here is a demo about how to download blobs using Java SDK for your reference:
/// <summary>
/// download blob to memory
/// </summary>
/// <param name="containerName">blob container name</param>
/// <param name="blobName">blob Name</param>
public static ByteArrayOutputStream downloadBlobToMemory(String containerName, String blobName) {
CloudStorageAccount account = null;
CloudBlobContainer container = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
account = CloudStorageAccount.parse(ConnString);
CloudBlobClient client = account.createCloudBlobClient();
container = client.getContainerReference(containerName);
container.createIfNotExists();
CloudBlockBlob cloudBlockBlob = container.getBlockBlobReference(blobName);
byteArrayOutputStream=new ByteArrayOutputStream();
cloudBlockBlob.download(byteArrayOutputStream);
}catch(Exception ex) {
ex.printStackTrace();
}
return byteArrayOutputStream;
}
/// <summary>
/// download blob to local disk
/// </summary>
/// <param name="containerName">blob container name</param>
/// <param name="blobName">blob Name</param>
/// <param name="filePath"> for example: C:\\Test\test.txt</param>
public static void downloadBlobToDisk(String containerName, String blobName, String filePath) {
CloudStorageAccount account = null;
CloudBlobContainer container = null;
try {
account = CloudStorageAccount.parse(ConnString);
CloudBlobClient client = account.createCloudBlobClient();
container = client.getContainerReference(containerName);
container.createIfNotExists();
CloudBlockBlob cloudBlockBlob = container.getBlockBlobReference(blobName);
FileOutputStream fileOutputStream=new FileOutputStream(filePath);
cloudBlockBlob.download(fileOutputStream);
}catch(Exception ex) {
ex.printStackTrace();
}
}
Lee Liu's suggestion about the Blob name was correct when I managed to find out the correct application address. It turned out that domain address visible by user was ending with "azureedge.net", but there's a different one when I went into portal.azure.com. It caused the main problem. After that, I indeed found problem with correct Blob names in storage - because of String.format, I had to add their ID in database with a "_" sign, then they started to be downloaded with content instead of empty files.
It seems that the code was OK, it was the problem with improper address and file names.

Test AJAX file upload

I want to test the file upload in my application. The upload itself is handled as described in section Direct file upload on http://www.playframework.org/documentation/2.0/JavaFileUpload.
I am using the latest Play20 version and build it as described here.
My current code looks like this, but obviously the part is missing which adds a test file to the request:
Test.java
FakeRequest request = fakeRequest(POST, "/measurement/123/file");
// how to append test file to this request?
Result result = routeAndCall(request);
assertOK(result);
Controller.java
public static uploadFile() {
RequestBody body = request().body();
if (body != null) {
RawBuffer rawBuffer = body.asRaw();
if (rawBuffer != null) {
File uploadedFile = rawBuffer.asFile();
// ...
}
}
return ok();
}
My solution for Play 2 Java file upload testing has been to create a UploadFakeRequest extending FakeRequest. Inside the class I have then my own withRawBody method with something like that:
RawBuffer raw = new RawBuffer(1000, data.getBytes()); // No clue what is the correct value here...
AnyContentAsRaw content = new AnyContentAsRaw(raw);
fake = new play.api.test.FakeRequest(POST, fake.path(), new play.api.test.FakeHeaders(Scala.asScala(map)), content, "127.0.0.1");
The signature of the FakeRequest call, as per source code, is:
case class FakeRequest[A](method: String, uri: String, headers: FakeHeaders, body: A) extends Request[A]
You should be able to inspect the body of a POST request that uploads a file to your application and copy that body as a parameter in your testing code. The alternative is to build it as per RFC description on multipart POST.

How to indicate the file name for saving to the browser from a REST service in Jersey?

i am trying to have a REST service return a zip file from the local harddrive .
Following is what i am doing ,
#Path("/interface3/{Ent_id}/{esf_app_id}/{esf_app_ver}")
public class Interface3Mock {
// This method is called if TEXT_PLAIN is request
#GET
#Produces("application/zip")
public Response callInterface3_text(
#PathParam("Ent_id") Integer entitlement_id,
#PathParam("eapp_id") String eapp_id,
#PathParam("eapp_ver") String eapp_ver) {
File f = new File("D:\\Documentation\\Documentation.zip");
String mt = new MimetypesFileTypeMap().getContentType(f);
return Response.ok(f, mt).build();
}
}
Now when i use the browser ie. Internet Explorer and key in the url http://localhost:9788/mockRESTServer/rest/interface3/123456/k123/l345
i see a file download dialog that says "Do you want to save the file l345`.
i want it to ask me for the zip download ie. D:\\Documentation\\Documentation.zip.
But somehow it takes up the last parameter in the request URL.
return Response.ok(f, mt)
.header("Content-Disposition", "attachment; filename=Documentation.zip")
.build();
See How to set response header in JAX-RS so that user sees download popup for Excel?

Categories