I need an API that will let me download a (e.g. CSV file). I tried to do something like this:
#RequestMapping(value="/{cartId}/export", method = RequestMethod.GET, produces = { "application/octet-stream" })
#ApiOperation(hidden = false, value = "Exports the contents of a cart as a CSV file.", notes = "Exports the contents of a cart as a CSV file.")
#ApiBaseSiteIdUserIdAndCartIdParam
public #ResponseBody ResponseEntity export() {
File file = new File("test.csv");
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=" + "test" + ".csv")
.contentLength(file.length())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new FileSystemResource(file));
}
However, I would get errors like this:
0203_17:48:01,878 INFO [hybrisHTTP21]
[de.hybris.platform.webservicescommons.resolver.RestHandlerExceptionResolver.doResolveException:73]
Translating exception
[org.springframework.web.HttpMediaTypeNotAcceptableException]: Could
not find acceptable representation 0203_17:48:01,879 WARN
[hybrisHTTP21]
[de.hybris.platform.webservicescommons.resolver.AbstractRestHandlerExceptionResolver.writeWithMessageConverters:72]
Could not find HttpMessageConverter that supports return type [class
de.hybris.platform.webservicescommons.dto.error.ErrorListWsDTO] and
[application/octet-stream] 0203_17:48:01,879 WARN [hybrisHTTP21]
[org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException:197]
Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException:
Could not find acceptable representation]
I understand this is because springmvc-servlet.xml only has resolverXStreamJSONConverter and resolverXStreamXmlConverter.
If I were to do it the MVC way, I'd write the file to a HttpServletResponse, but that doesn't seem ideal for OCC. So, how should I implement the file download? (Code sample also appreciated)
NOTE: The API/method will be used with Spartacus storefront.
A workaround is to add org.springframework.http.converter.ResourceHttpMessageConverter in messageConvertersV2 in web/webroot/WEB-INF/config/v2/jaxb-converters-spring.xml:
<util:list id="messageConvertersV2">
<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
<ref bean="customJsonHttpMessageConverter"/>
<ref bean="customXmlHttpMessageConverter"/>
</util:list>
However, not sure if this is correct or a good idea.
I needed a similar implementation to offer a PDF download. The controller now returns ResponseEntity<byte[]> and I appended the corresponding MessageConverter. I also didnt find a 'better' way, but it works.
<bean id="myByteArrayHttpMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean depends-on="messageConvertersV2" parent="listMergeDirective">
<property name="add" ref="myByteArrayHttpMessageConverter"/>
</bean>
Related
I am very new to Spring Integration and my project is using File Support to read a file and load into data base.
I have XML config , trying to understand it's content.
<int-file:inbound-channel-adapter auto-startup= true channel="channelOne" directory="${xx}" filename-regex="${xx}" id="id" prevent-duplicates="false">
<int:poller fixed-delay="1000" receive-timeout="5000"/>
</int-file:inbound-channel-adapter>
<int:channel id="channelOne"/>
From the above piece, my understanding is :
We define a channel and
Then define inbound-channel-adapter - this will look into directory for the file and create a message with file as a payload.
I was able to convert this in JavaConfig as below :
#Bean
public MessageChannel fileInputChannel() {
return new DirectChannel();
}
#Bean
#InboundChannelAdapter(value = "fileInputChannel", poller = #Poller(fixedDelay = "1000"))
public MessageSource<File> fileReadingMessageSource() {
FileReadingMessageSource sourceReader= new FileReadingMessageSource();
RegexPatternFileListFilter regexPatternFileListFilter = new RegexPatternFileListFilter(
file-regex);
//List<FileListFilter<File>> fileListFilter = new ArrayList<FileListFilter<File>>();
fileListFilter.add(regexPatternFileListFilter);
//CompositeFileListFilter compositeFileListFilter = new CompositeFileListFilter<File>(
fileListFilter);
sourceReader.setDirectory(new File(inputDirectorywhereFileComes));
sourceReader.setFilter(regexPatternFileListFilter );
return sourceReader;
}
Then the next piece of code , which literally I am struggling to understand and moreover to convert to JavaConfig.
Here is the next piece:
<int-file:outbound-gateway
delete-source-files="true"
directory="file:${pp}"
id="id"
reply-channel="channelTwo"
request-channel="channelOne"
temporary-file-suffix=".tmp"/>
<int:channel id="channelTwo"/>
<int:outbound-channel-adapter channel="channelTwo" id="id" method="load" ref="beanClass"/>
So from this piece , my understanding :
1: Define an output channel.
2: Define an outbound-gateway, which will write that message as a file again in directory(other one), also remove file from source directory. And finally it will call the method Load of Bean Class. This is our class and has load method which takes file as input and load it to DB.
I tried to covert it into Java Config. Here is my code:
#Bean
#ServiceActivator(inputChannel= "fileInputChannel")
public MessageHandler fileWritingMessageHandler() throws IOException, ParseException {
FileWritingMessageHandler handler = new FileWritingMessageHandler(new File(path to output directory));
handler.setFileExistsMode(FileExistsMode.REPLACE);
beaObject.load(new File(path to output directory or input directory:: Nothing Worked));
handler.setDeleteSourceFiles(true);
handler.setOutputChannel(fileOutputChannel());
return handler;
}
I am able to write this file to output folder also was able to delete from source. After that I am totally lost. I have to call method Load of my BeanClass(ref=class in XML ).
I tried a lot, but not able to get it. Read multiple times the integration File Support doc, but couldn't make it.
Note: When I tried , I got one error saying , the File Not Found Exception. I believe , I am able to call my method , but can not get the file.
This XML config is working perfectly fine.
Spring Integration with DSL also anyone can suggest, if possible.
Please help me to understand the basic flow and get this thing done. Any help and comments is really appreciable.
Thanks in advance.
First of all you need to understand that #Bean method is exactly for configuration and components definitions which are going to be used later at runtime. You definitely must not call a business logic in the #Bean. I mean that your beaObject.load() is totally wrong.
So, please, go first to Spring Framework Docs to understand what is #Bean and its parent #Configuration: https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/core.html#beans-java
Your #ServiceActivator for the FileWritingMessageHandler is really correct (when you remove that beaObject.load()). What you just need is to declare one more #ServiceActivator for calling your beaObject.load() at runtime when message appears in the fileOutputChannel:
#ServiceActivator(inputChannel= "fileOutputChannel")
public void loadFileIntoDb(File payload) {
this.beaObject.load(payload);
}
See https://docs.spring.io/spring-integration/docs/5.1.1.BUILD-SNAPSHOT/reference/html/configuration.html#annotations for more info.
i am saving files on path C:\Cdr\core\year\month\date\fileName
and i want to zip all the files created on previous day and before that.I am unable to find a way to provide dynamic file path and file name to file Consumer for zipping files. Even when using filename=${beans:utility.generateFileName}, i am only able to provide file name not the file path.Is there a way to do this using apache Zip file data format.
I found the way for same by using filter
<routeContext id="zipFileRoute" xmlns="http://camel.apache.org/schema/spring">
<route id="zipFile">
<from uri="file://C:/CdrJson?recursive=true&delete=true&filter=#myFilter/>
<log message="reading from ${in.header.CamelFileName} and file path is ${file:path}"/>
<setHeader headerName="CamelFileName">
<simple>${bean:utility?method=processFileName}</simple>
</setHeader>
<marshal>
<zipFile/>
</marshal>
<to uri="file://C:/CdrJson"/>
<log message="This route finished zipping files"/>
</route>
</routeContext>
Code for myFilter:
public class MyFileFilter<T> implements GenericFileFilter<T> {
public boolean accept(GenericFile<T> file) {
// we want all directories
if (file.isDirectory()) {
return true;
}
Calendar date = new GregorianCalendar();
String fileName = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
if(file.getFileNameOnly().startsWith(fileName)){
return false;
}
return !file.getFileName().endsWith(".zip");
}
}
Use GenericFileFilter to filter the file based on created date. Using this at run time you can skip the files not matched with desired criteria.
Ref: http://camel.apache.org/file2
Dynamic path: I am not sure about what level of dynamism you are expecting. File consumer is a polling consumer, one that explicitly makes a call when it wants to receive a message. So you can either hard-code the file path or set dynamic path resolved at run time from property file (Ref:Proertyplaceholder) as below,
<from uri="file:{{consumer.file.path}}"/>
To Zip files: You have to use aggregate pattern as below,
from("file:{{consumer.file.path}}")
.aggregate(new ZipAggregationStrategy())
.constant(true)
.completionFromBatchConsumer()
.eagerCheckCompletion()
.setHeader(Exchange.FILE_NAME, constant("<DesiredFileName>.zip"))
.to("file:output/directory");
Hope it helps!!
I have read the documentation but I can't quite seem to get the skipPrefix to work with xml2js. What I would like do to do is given the following xml remove the namespace prefix.
<root>
<part:tire>A</part:tire>
</root>
I would like the json object to exclude "part:".
Thanks
From the source code
prefixMatch = new RegExp(/(?!xmlns)^.*:/);
...
exports.stripPrefix = function(str) {
return str.replace(prefixMatch, '');
};
i think your xml string need to have proper namespace in order for that feature to work.
With Spring Integration 4.2.0, it mentioned that 'filter' and 'locker' must be present if custom Scanner is being used (https://jira.spring.io/browse/INT-3619).
I don't know how to set this with XML config if I simply override the listEligibleFiles() method and use the default filters provided by DefaultDirectoryScanner.
e.g.
// using the default filters
public class MyDirectoryScanner extends DefaultDirectoryScanner {
#Override
protected File[] listEligibleFiles(File directory) throws IllegalArgumentException {
return super.listEligibleFiles(directory);
}
}
<bean id="myCustomScanner"
class="com.company.MyDirectoryScanner" />
<int-file:inbound-channel-adapter directory="my_directory"
prevent-duplicates="true"
scanner="myCustomScanner"
channel="myChannel">
<int:poller fixed-rate="10"
time-unit="SECONDS" max-messages-per-poll="5" />
</int-file:inbound-channel-adapter>
It's not clear what you mean; that JIRA was to fix a bug where those properties were incorrectly overridden.
When injecting a custom scanner, you need to set those properties on your scanner rather than via the namespace.
use the default filters provided by DefaultDirectoryScanner.
The DefaultDirectoryScanner has the code:
public DefaultDirectoryScanner() {
final List<FileListFilter<File>> defaultFilters = new ArrayList<FileListFilter<File>>(2);
defaultFilters.add(new IgnoreHiddenFileListFilter());
defaultFilters.add(new AcceptOnceFileListFilter<File>());
this.filter = new CompositeFileListFilter<File>(defaultFilters);
}
So, if you would like do not use AcceptOnceFileListFilter (or any other default) you should follow with the recommendation from the Docs and use setFilter() of the DirectoryScanner contract. For this purpose there is FileListFilterFactoryBean with the setPreventDuplicates() to be set to false.
And yes, remove, please, prevent-duplicates="true" from your configuration, because it is prohibited, when scanner is in use:
Assert.state(!(this.scannerExplicitlySet && (this.filter != null || this.locker != null)),
"The 'filter' and 'locker' options must be present on the provided external 'scanner': "
+ this.scanner);
The filter can be set to null on the DefaultDirectoryScanner by the way...
I'm converting the JIRA to Documentation just to be more clear on the matter.
Doing exploratory work before I drink the kool-aid.
I am trying to create a simple inbound channel adapter to monitor a directory for new ZIP files.
In order to deal with the ever-present "is it complete?" question, I am trying to adapt the example posted here to incorporate a FileListFilter which checks the modified time of the file.
However, I am getting the following exception:
a boolean result is requiredclass java.util.ArrayList is not assignable to class java.lang.Boolean
at org.springframework.util.Assert.isAssignable(Assert.java:368)
at org.springframework.integration.filter.AbstractMessageProcessingSelector.accept(AbstractMessageProcessingSelector.java:61)
at org.springframework.integration.filter.MessageFilter.handleRequestMessage(MessageFilter.java:103)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:134)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:73)
I had this working well with a simple router based on the file extension, but when I replaced it with this filter it fell apart. Seems the actual list of files is what the Assert is trying to cast to Boolean.
Is it not possible to have a filter wired between an inbound and an outbound adapter? Or must I do the file move to the destination myself in the filter? (The way it is done in the linked example)
Here is the config:
<int-file:inbound-channel-adapter id="filePoller" directory="file:input" channel="filesChannel" filename-pattern="*.zip">
<int:poller fixed-rate="2000" max-messages-per-poll="10" />
</int-file:inbound-channel-adapter>
<int:filter input-channel="filesChannel" ref="lastModifiedFileFilter" output-channel="zipFilesOut"/>
<bean id="lastModifiedFileFilter" class="FileFilterOnLastModifiedTime">
<property name="timeDifference" value="10000"/>
</bean>
<int-file:outbound-channel-adapter id="zipFilesOut" directory="file:target/output/zip" delete-source-files="true" />
Here is the filter:
import java.io.File;
import org.springframework.integration.file.filters.AbstractFileListFilter;
public class FileFilterOnLastModifiedTime extends AbstractFileListFilter<File> {
Long timeDifference = 1000L;
#Override
protected boolean accept(File file) {
long lastModified = file.lastModified();
long currentTime = System.currentTimeMillis();
return (currentTime - lastModified) > timeDifference ;
}
public void setTimeDifference(Long timeDifference) {
this.timeDifference = timeDifference;
}
}
Your FileFilterOnLastModifiedTime bean should be provided to the inbound adapter using the filter attribute.
<int-file:inbound-channel-adapter id="filePoller" directory="file:input" channel="zipFilesOut" filename-pattern="*.zip"
filter="lastModifiedFileFilter">
<int:poller fixed-rate="2000" max-messages-per-poll="10" />
</int-file:inbound-channel-adapter>
An inline <filter/> element is a simple POJO that takes some argument and returns a boolean.
Since you are providing an AbstractFileListFilter, the framework is trying to invoke filterFiles which takes an array and returns a List, not a boolean.