Generate and download file with jboss seam - file

I need to add an 'export' function to an existing web app using seam. The purpose is to export search results to a csv file. I have no problem generating a csv, but I do not know how the send the csv back to the user.
I do not want to store the csv on the server because that would be waisted storage space. How could I achieve this in jboss seam?

Use the Document Store Servlet provided by Seam.
Almost copying and pasting from the reference doc, declare the servlet in web.xml like this:
<servlet>
<servlet-name>Document Store Servlet</servlet-name>
<servlet-class>org.jboss.seam.document.DocumentStoreServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Document Store Servlet</servlet-name>
<url-pattern>/seam/docstore/*</url-pattern>
</servlet-mapping>
Then create a export.xhtml file with only <s:resource> tag:
<s:resource xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
data="#{myComponent.csvData}"
contentType="application/vnd.ms-excel"
fileName="#{myComponent.csvFileName}"/>
Generate link for downloading the file in your page with <s:download>:
<s:download src="/csv/export.xhtml">
<h:outputText value="Download CSV"/>
<f:param name="param1" value="somevalue"/>
<f:param name="param2" value="someOtherValue"/>
</s:download>
Finally, implement getCsvData() and getCsvFileName() methods in your component:
// could be byte[], File or InputStream
public InputStream getCsvData() {
// generate data to be downloaded
}
public String getCsvFileName() {
return "myfile.csv";
}
Note that <s:download> propagates conversation (unless you set propagation=none). If you propagate the conversation context probably you won't need to pass any parameter. For large data set it may be preferable to not propagate the conversation and pass parameter to select the data in a request scoped component.

There's a couple of ways:
1) Check the Seam docs for info on using Seam-Excel to programmatically generate your file and then write it out using a mime-type set for CSV - this is all detailed in the docs.
However, I could not get this to work in the latest version of Seam, as it requires a response object, which used to be available from the Seam context but now only returns null.
2) Code the CSV file you want as an Excel xhtml template (see the Seam docs and example projects) and simply render this as normal using an tag.
I do this regularly and it works well, bar the restriction that you cannot supply a filename.
HTH.

Related

Azure Logic App outputs XML all in single line

I'm trying to convert a CSV to XML, which works perfectly fine when I test the map file in Visual Studio as I made sure the XSLT1.0 contains indent="yes". But for some reason when I repeat the steps in my Azure Logic App everything gets exported to one line:
<?xml version="1.0" encoding="utf-8"?><enfinity xsi:schemaLocation="http://www.fakewebsite.com/xml/ns/enfinity/7.1/bc_pricing/impex bc_pricing.xsd" major="6" minor="1" family="enfinity" branch="enterprise" build="build" xmlns="http://www.fakewebsite.com/xml/ns/enfinity/7.1/bc_pricing/impex" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dt="http://www.fakewebsite.com/xml/ns/enfinity/6.5/core/impex-dt"><product-price-list id="DEMarkdown" priceType="ES_SalePrice" import-mode="UPDATE"><display-name>DE Markdown</display-name><description /><enabled>true</enabled><priority>1</priority><target-groups><customer-segments><customer-segment id="Everyone" repository-id="WhiteStuff-DE-Anonymous" /><customer-segment id="IG_RegisteredUsers" repository-id="WhiteStuff-DE-Anonymous" /></customer-segments></target-groups><product-price-list-entry sku="433493126" import-mode="REPLACE"><price-scale-table type-code="1" currency="EUR"><valid-from>2021-07-06T00:00:00+00:00</valid-from><valid-to>2099-07-13T00:00:00+00:00</valid-to><price-scale-entries><fixed-price-entry quantity="1" unit=""><value>44.95</value></fixed-price-entry></price-scale-entries></price-scale-table><price-scale-table type-code="1" currency="GBP"><valid-from>2021-07-06T00:00:00+00:00</valid-from><valid-to>2099-07-13T00:00:00+00:00</valid-to><price-scale-entries><fixed-price-entry quantity="1" unit=""><value>39.50</value></fixed-price-entry></price-scale-entries></price-scale-table></product-price-list-entry></product-price-list></enfinity>
I have 2 ways for this
WAY-1
You can use the XML Validation connector before the process which parses the content into the right format.
WAY-2
Just for viewing purposes, you can do a request-response method using Postman then use XML format in order to achieve your requirement.
For those interested, I've found a setting within the Logic App, simply select Apply XSLT output attributes and that's it, no validation needed either!

Logic App Function to remove file extension

I have a logic app in Azure. The trigger is to check when an email arrives into a specific folder in a mailbox. An email may contain 1 or more attachments.
Once triggered, I have an HTTP request that gets sent to a SOAP service. The idea is that I want to check if the filename exists at the SOAP Service.
Its all working perfectly, with the exception, when I reference the filename from the trigger, it includes the file extension. I need to somehow ignore the ".PDF" part of the filename
Below is the XML I post to the SOAP service. Lets say the filename is 12345.pdf, then I need /UniversalEvent/Event/ContextCollection/Context/Value to = "12345" and not "12345.pdf":
<UniversalEvent xmlns="http://www.cargowise.com/Schemas/Universal/2011/11" version="1.1">
<Event>
<DataContext>
<DataTargetCollection>
<DataTarget>
<Type>ForwardingShipment</Type>
</DataTarget>
</DataTargetCollection>
</DataContext>
<EventTime>#{utcNow()}</EventTime>
<EventType>Z77</EventType>
<EventReference>Requesting Shipment ID</EventReference>
<IsEstimate>false</IsEstimate>
<ContextCollection>
<Context>
<Type>HAWBNumber</Type>
<Value>#{items('For_each')?['name']}</Value>
</Context>
</ContextCollection>
</Event>
</UniversalEvent>
do you have any suggestions on what function to use to achieve this?
You can change the expression #{items('For_each')?['name']} in your xml to:
#{substring(items('For_each')?['name'], 0, indexOf(items('For_each')?['name'], '.'))}

SAML error "SignatureStatus: NOT_PRESENT"

UPDATE
I found that if I add a trusted cert to SPOptions.ServiceCertificates and set SPOptions.AuthenticateRequestSigningBehavior = Sustainsys.Saml2.Configuration.SigningBehavior.IfIdpWantAuthnRequestsSigned; and set IdentityProvider.WantAuthnRequestsSigned = true, the signature element is included.
Original Question:
Having troubles connecting to an IDP with the following AuthnRequest:
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="idf299bf8aa08542d193e022cb047e5ecc" Version="2.0" IssueInstant="2019-07-23T00:10:13Z" Destination="https://example-idp.com" AssertionConsumerServiceURL="https://example-sp.com/Acs">
<saml2:Issuer>https://example-sp.com</saml2:Issuer>
</saml2p:AuthnRequest>
The IDP says: "SignatureStatus: NOT_PRESENT". I'm guessing that means that the authnrequest should have a <ds:Signature section? If so, how do I configure Sustainsys.Saml2.AspNetCore2 to include it?
The metadata xml I received from the idp contains a <ds:Signature section, but looking at the source code for Sustainsys.Saml2.AspNetCore2, it looks like that part of the metadata gets ignored when deserializing?
I'm not very familiar with the internals of SAML, so sorry if this is a silly question.
You'll want to generate a self-signed .pfx file that contains both your public cert and private key. We use azure key vault, but you could also use openssl. Lots of resources that explain how to generate one of those and load it into a c# X509Certificate2 instance.
Once you have an instance of X509Certificate2, set options.SPOptions.AuthenticateRequestSigningBehavior = Sustainsys.Saml2.Configuration.SigningBehavior.IfIdpWantAuthnRequestsSigned;
And set IdentityProvider.WantAuthnRequestsSigned = true.
And then add the X509Certificate2 instance like so: options.SPOptions.ServiceCertificates.Add(myX509Certificate2);
Then run your app and start the SAML SSO process. You can use hookbin or the like to see what it sends in the AuthnRequest for SAMLRequest. You can extract the xml from that by url decoding it and then base64 decoding it like so in javascript, for instance to confirm signature xml is set and correct: atob(decodeURIComponent(samlRequestValue))

How to log as jsonPayload to stackdriver from google app engine using logback?

My spring boot app uses logback to log messages in json format. The app is configured to use consolelogappender (stdout).When the logs appear in stackdriver, they appear as textPayload instead of jsonPayload. Is it possible to write message to jsonPayload field in stackdriver using logback? If not, what are my options to log in json format?
Based on this Github Link it seems the issue all log entries are seen as textpayload. It has been added as a Feature Request but we do not have an ETA on when it will be available.
I'm not entirely sure if an alternative exist as Logback seems to be giving extensive log information, but if you are able to use the Stackdriver Logging Client instead, you could format the entry in order to get your object as a JsonPayLoad, although you will have specify most of the log categories yourself which can be an extra amount of work.
The easy way to do this, is to implements the transformation of TextPayload(JSON Format) to JSONPayload on the LoggingEnhacer
Check this answer How to use Stackdriver Structured Logging in App Engine Flex Java environment
It is possible via google-cloud-logging-logback library.
However, please note following (from https://cloud.google.com/logging/docs/structured-logging):
Note: message is saved as textPayload if it is the only field remaining
after the Logging agent moves the other special-purpose fields and
detect_json wasn't enabled; otherwise message remains in jsonPayload.
detect_json is not applicable to managed logging environments like
Google Kubernetes Engine.
To add more data to json add an enhancer. Example:
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.google.cloud.logging.LogEntry;
import com.google.cloud.logging.Payload;
import com.google.cloud.logging.logback.LoggingEventEnhancer;
import java.util.HashMap;
public class EventEnhancer implements LoggingEventEnhancer {
#Override
public void enhanceLogEntry(
LogEntry.Builder logEntry,
ILoggingEvent e
) {
HashMap<String, Object> map = new HashMap<>();
map.put("thread", e.getThreadName());
map.put("context", e.getLoggerContextVO().getName());
map.put("logger", e.getLoggerName());
Payload.JsonPayload payload = logEntry.build().getPayload();
map.putAll(payload.getDataAsMap());
logEntry.setPayload(
Payload.JsonPayload.of(map)
);
}
}
Configuration:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration scan="true">
<appender name="CLOUD" class="com.google.cloud.logging.logback.LoggingAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<log>application.log</log>
<redirectToStdout>true</redirectToStdout>
<resourceType>gae_app</resourceType>
<loggingEventEnhancer>EventEnhancer</loggingEventEnhancer>
<flushLevel>INFO</flushLevel>
</appender>
<root level="INFO">
<appender-ref ref="CLOUD"/>
</root>
</configuration>

GWT Upload fails to App Engine

I want to provide a file upload to Google App Engine with the "GWT Upload" (https://code.google.com/p/gwtupload/). During the upload I get an error. As UploadAction servlet I use the build in: gwtupload.server.gae.AppEngineUploadAction
The servlet is configured in the web.xml in the following way:
<context-param>
<!-- max size of the upload request -->
<param-name>maxSize</param-name>
<param-value>3145728</param-value>
</context-param>
<context-param>
<!-- Useful in development mode to slow down the uploads in fast networks.
Put the number of milliseconds to sleep in each block received in the server.
false or 0, means don't use slow uploads -->
<param-name>slowUploads</param-name>
<param-value>200</param-value>
</context-param>
<servlet>
<servlet-name>uploadServlet</servlet-name>
<!-- This is the default servlet, it puts files in session -->
<servlet-class>gwtupload.server.gae.AppEngineUploadAction</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>uploadServlet</servlet-name>
<url-pattern>*.gupld</url-pattern>
</servlet-mapping>
During upload the progress bar progresses some percentages and then shows the following error:
But there are no more details in the logs.
The error message shows the class gwtupload.server.gae.MemCacheFileItemFactory$CacheableFileItem with the method setHeader(). That's strange because I can't find the method in that class. What's happening here?
Edit:
This is basically all the custom code i use. On the server side i use the build in gwtupload.server.gae.AppEngineUploadAction servlet.
package com.uploadtest.client;
import gwtupload.client.IUploadStatus.Status;
import gwtupload.client.IUploader;
import gwtupload.client.IUploader.UploadedInfo;
import gwtupload.client.MultiUploader;
import gwtupload.client.PreloadedImage;
import gwtupload.client.PreloadedImage.OnLoadPreloadedImageHandler;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.RootPanel;
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class GWTUploadTest2 implements EntryPoint {
// A panel where the thumbnails of uploaded images will be shown
private FlowPanel panelImages = new FlowPanel();
public void onModuleLoad() {
// Attach the image viewer to the document
RootPanel.get("thumbnails").add(panelImages);
// Create a new uploader panel and attach it to the document
MultiUploader defaultUploader = new MultiUploader();
RootPanel.get("default").add(defaultUploader);
// Add a finish handler which will load the image once the upload finishes
defaultUploader.addOnFinishUploadHandler(onFinishUploaderHandler);
}
// Load the image in the document and in the case of success attach it to the viewer
private IUploader.OnFinishUploaderHandler onFinishUploaderHandler = new IUploader.OnFinishUploaderHandler() {
public void onFinish(IUploader uploader) {
if (uploader.getStatus() == Status.SUCCESS) {
new PreloadedImage(uploader.fileUrl(), showImage);
// The server sends useful information to the client by default
UploadedInfo info = uploader.getServerInfo();
System.out.println("File name " + info.name);
System.out.println("File content-type " + info.ctype);
System.out.println("File size " + info.size);
// You can send any customized message and parse it
System.out.println("Server message " + info.message);
}
}
};
// Attach an image to the pictures viewer
private OnLoadPreloadedImageHandler showImage = new OnLoadPreloadedImageHandler() {
public void onLoad(PreloadedImage image) {
image.setWidth("75px");
panelImages.add(image);
}
};
}
In addition to that i added the following jars to my clath path:
log4j-1.2.17.jar
gwtupload-gae-0.6.6.jar
gwtupload-0.6.6.jar
commons-fileupload-1.3.jar
commons-io-2.4.jar
Also zipped my whole sample project and uploaded it here:
https://skydrive.live.com/redir?resid=60B826E451F52B4D!118&authkey=!ALa1n2mL2sRR0wU
Edit 2:
Like Manolo pointed out: I was using "commons-fileupload-1.3.jar" instead of "commons-fileupload-1.2.1.jar". Changing the jar fixed my problem!
The problem is in the version of the commons-fileupload you are using, change it to the version 1.2.1, which is the one pointed in the gwtupload documentation.
It should work with 1.2.2 as well, but to use 1.3 requires new methods (setHeaders) which are not in the UploadListeners provided with gwtupload.
You should change in your project the target java (JDK compliance) to 1.6, since it is the last one supported in GWT to avoid problems, although it runs in 1.7.

Resources