In the manual for ExpressionEvaluatingRequestHandlerAdvice, it clearly says, A typical use case for this advice might be with an <ftp:outbound-channel-adapter/>, perhaps to move the file to one directory if the transfer was successful, or to another directory if it fails.
But I cannot figure out expression to move payload from current directory to another one.
This example just deletes or renames the file:
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
<property name="successChannel" ref="afterSuccessDeleteChannel" />
<property name="onFailureExpression" value="payload.renameTo(new java.io.File(payload.absolutePath + '.failed.to.send'))" />
<property name="failureChannel" ref="afterFailRenameChannel" />
</bean>
How to achieve this?
Edit
As per Gary's suggestion, this is the new try:
Managed to change the expression to "T(java.nio.file.Files).move(payload.path, new java.io.File(new java.io.File('sent'), payload.name).path, T(java.nio.file.StandardCopyOption).REPLACE_EXISTING)",
but still get the error Method move(java.lang.String,java.lang.String,java.nio.file.StandardCopyOption) cannot be found on java.nio.file.Files type
The code is,
#Bean
#ServiceActivator(inputChannel = "toSftpChannel", adviceChain = "expressionAdvice")
public MessageHandler uploadHandler() {
SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
handler.setRemoteDirectoryExpression(new LiteralExpression(outRemoteDirectory));
handler.setFileNameGenerator(new FileNameGenerator() {
#Override
public String generateFileName(Message<?> message) {
if (message.getPayload() instanceof File) {
return ((File) message.getPayload()).getName();
} else {
throw new IllegalArgumentException("File expected as payload.");
}
}
});
return handler;
}
#MessagingGateway()
public interface UploadGateway {
#Gateway(requestChannel = "toSftpChannel")
void upload(File file);
}
#Bean
public String onUploadSuccessExpression() {
return "T(java.nio.file.Files).move(payload.path, new java.io.File(new java.io.File('sent'), payload.name).path, T(java.nio.file.StandardCopyOption).REPLACE_EXISTING)";
}
#Bean
public String onUploadFailedExpression() {
return "payload";
}
#Bean
public Advice expressionAdvice() {
ExpressionEvaluatingRequestHandlerAdvice expressionEvaluatingRequestHandlerAdvice = new ExpressionEvaluatingRequestHandlerAdvice();
expressionEvaluatingRequestHandlerAdvice.setOnSuccessExpressionString(onUploadSuccessExpression());
expressionEvaluatingRequestHandlerAdvice.setSuccessChannelName("uploadSuccessChannel");
expressionEvaluatingRequestHandlerAdvice.setOnFailureExpressionString(onUploadFailedExpression());
expressionEvaluatingRequestHandlerAdvice.setFailureChannelName("uploadFailedChannel");
expressionEvaluatingRequestHandlerAdvice.setTrapException(true);
expressionEvaluatingRequestHandlerAdvice.setPropagateEvaluationFailures(true);
return expressionEvaluatingRequestHandlerAdvice;
}
The upload method from UploadGateway is called.
The stack trace is,
"main#1" prio=5 tid=0x1 nid=NA runnable
java.lang.Thread.State: RUNNABLE
at org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice.evaluateSuccessExpression(ExpressionEvaluatingRequestHandlerAdvice.java:241)
at org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice.doInvoke(ExpressionEvaluatingRequestHandlerAdvice.java:214)
at org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice.invoke(AbstractRequestHandlerAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy81.handleRequestMessage(Unknown Source:-1)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.doInvokeAdvisedRequestHandler(AbstractReplyProducingMessageHandler.java:127)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:112)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:423)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:373)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:115)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:45)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:105)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:143)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.convertAndSend(AbstractMessageSendingTemplate.java:135)
at org.springframework.integration.gateway.MessagingGatewaySupport.send(MessagingGatewaySupport.java:392)
at org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(GatewayProxyFactoryBean.java:481)
at org.springframework.integration.gateway.GatewayProxyFactoryBean.doInvoke(GatewayProxyFactoryBean.java:433)
at org.springframework.integration.gateway.GatewayProxyFactoryBean.invoke(GatewayProxyFactoryBean.java:424)
at org.springframework.integration.gateway.GatewayCompletableFutureProxyFactoryBean.invoke(GatewayCompletableFutureProxyFactoryBean.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy87.upload(Unknown Source:-1)
If the new directory is on the same disk as the old one, in the 'onSuccessExpression', simply use payload.renameTo(...) similar to the way the sample does in the onFailureExpression.
`payload.renameTo(new java.io.File(new File('newDir'), payload.name))`
Creates a file with the payload's name to a directory newDir (which must exist).
If you are JDK 7 or above use...
T(java.nio.file.Files).move(payload.path, new java.io.File(new File('newDir'), payload.name).path)
...instead.
This will handle the situation of the new directory being on a different disk (which a simple File.renameTo() will not).
If you are still on JDK 6 and the new directory might be on a different disk you will need to use onSuccessExpression=payload and subscribe a service activator to the successChannel to manipulate the file itself, perhaps using Spring's FileCopyUtils.
Related
We have a Spring-Boot REST application running with Infinispan 13.0.12 caches and we see periodic seemingly random cases where the application becomes un-responsive. A thread dump indicates over 200 threads in this state:
"http-nio-8080-exec-379" #11999 daemon prio=5 os_prio=0 tid=0x00007f28900f9800 nid=0x2c68
waiting on condition [0x00007f28485c2000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000006c09af3e8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2163)
at org.jgroups.util.Credit.decrementIfEnoughCredits(Credit.java:65)
at org.jgroups.protocols.UFC.handleDownMessage(UFC.java:119)
at org.jgroups.protocols.FlowControl.down(FlowControl.java:323)
at org.jgroups.protocols.FlowControl.down(FlowControl.java:317)
at org.jgroups.protocols.FRAG3.down(FRAG3.java:139)
at org.jgroups.stack.ProtocolStack.down(ProtocolStack.java:927)
at org.jgroups.JChannel.down(JChannel.java:645)
at org.jgroups.JChannel.send(JChannel.java:484)
at org.infinispan.remoting.transport.jgroups.JGroupsTransport.send(JGroupsTransport.java:1161)
Our Java configuration looks like this:
#Autowired
#Bean
public SpringEmbeddedCacheManagerFactoryBean springEmbeddedCacheManagerFactoryBean(GlobalConfigurationBuilder gcb, ConfigurationBuilder configurationBuilder) {
SpringEmbeddedCacheManagerFactoryBean springEmbeddedCacheManagerFactoryBean = new SpringEmbeddedCacheManagerFactoryBean();
springEmbeddedCacheManagerFactoryBean.addCustomGlobalConfiguration(gcb);
springEmbeddedCacheManagerFactoryBean.addCustomCacheConfiguration(configurationBuilder);
return springEmbeddedCacheManagerFactoryBean;
}
#Autowired
#Bean
public EmbeddedCacheManager defaultCacheManager(SpringEmbeddedCacheManager springEmbeddedCacheManager) throws Exception {
return springEmbeddedCacheManager.getNativeCacheManager();
}
#Bean
public GlobalConfigurationBuilder globalConfigurationBuilder() {
GlobalConfigurationBuilder result = GlobalConfigurationBuilder.defaultClusteredBuilder();
result.transport().addProperty("configurationFile", jgroupsConfigFile);
result.cacheManagerName(IDENTITY_CACHE);
result.defaultCacheName(IDENTITY_CACHE + "-default");
result.serialization()
.marshaller(new JavaSerializationMarshaller())
.allowList()
.addClasses(
LinkedMultiValueMap.class,
String.class
);
result.globalState().enable().persistentLocation(DATA_DIR);
return result;
}
#Bean
public ConfigurationBuilder configurationBuilder() {
ConfigurationBuilder result = new ConfigurationBuilder();
result.clustering().cacheMode(CacheMode.REPL_SYNC);
return result;
}
#Bean
public org.infinispan.configuration.cache.Configuration cacheConfiguration() {
ConfigurationBuilder builder = new ConfigurationBuilder();
return builder
.clustering()
.cacheMode(CacheMode.REPL_SYNC)
.remoteTimeout(replicationTimeoutSeconds, TimeUnit.SECONDS)
.stateTransfer().timeout(stateTransferTimeoutMinutes, TimeUnit.MINUTES)
.persistence()
.addSoftIndexFileStore()
.shared(false)
.fetchPersistentState(true)
.expiration().lifespan(expirationHours, TimeUnit.HOURS)
.build();
}
#Autowired
#Bean
public Cache<String, MultiValueMap<String, String>> identityCache(EmbeddedCacheManager manager, org.infinispan.configuration.cache.Configuration cacheConfiguration) throws IOException {
Cache<String, MultiValueMap<String, String>> result = manager
.administration().withFlags(CacheContainerAdmin.AdminFlag.VOLATILE)
.getOrCreateCache(IDENTITY_CACHE, cacheConfiguration);
result.getAdvancedCache().getStats().setStatisticsEnabled(true);
return result;
}
and we run a three node cluster with the default-jgroups-udp.xml config. Can anyone suggest a likely cause? Perhaps the config is sub-optimal?
TIA
<config xmlns="urn:org:jgroups"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:org:jgroups
http://www.jgroups.org/schema/jgroups-4.0.xsd">
<UDP mcast_addr="${jgroups.udp.mcast_addr:228.6.7.9}"
mcast_port="${jgroups.udp.mcast_port:46655}"
ucast_send_buf_size="1m"
mcast_send_buf_size="1m"
ucast_recv_buf_size="20m"
mcast_recv_buf_size="25m"
ip_ttl="${jgroups.ip_ttl:2}"
thread_naming_pattern="pl"
enable_diagnostics="false"
bundler_type="no-bundler"
max_bundle_size="8500"
thread_pool.min_threads="${jgroups.thread_pool.min_threads:0}"
thread_pool.max_threads="${jgroups.thread_pool.max_threads:200}"
thread_pool.keep_alive_time="60000"
/>
<PING />
<MERGE3 min_interval="10000"
max_interval="30000"
/>
<FD_SOCK />
<FD_ALL timeout="60000"
interval="15000"
timeout_check_interval="5000"
/>
<VERIFY_SUSPECT timeout="5000"
/>
<pbcast.NAKACK2 xmit_interval="100"
xmit_table_num_rows="50"
xmit_table_msgs_per_row="1024"
xmit_table_max_compaction_time="30000"
resend_last_seqno="true"
/>
<UNICAST3 xmit_interval="100"
xmit_table_num_rows="50"
xmit_table_msgs_per_row="1024"
xmit_table_max_compaction_time="30000"
conn_expiry_timeout="0"
/>
<pbcast.STABLE stability_delay="500"
desired_avg_gossip="5000"
max_bytes="1M"
/>
<pbcast.GMS print_local_addr="false"
install_view_locally_first="true"
join_timeout="${jgroups.join_timeout:5000}"
/>
<UFC max_credits="2m"
min_threshold="0.40"
/>
<MFC max_credits="2m"
min_threshold="0.40"
/>
<FRAG3 frag_size="8000"/>
</config>
You have a replicated cache. This means that reads are always local, so the stack trace must be on a write (or rebalance).
The block means that the sender is waiting for credits from a receiver, which don't arrive, so the receiver must be stuck in sth. Also, the stack trace is not complete; can you show the entire trace?
To know what's going on, it would be good to see thread dumps of all members. I suggest zip them up and post a link to the zip here...
Cheers
Could this be related to https://issues.redhat.com/browse/ISPN-14260 ?
Are you using ACL cache authorisations?
Our temporary fix was to disable cache authorisations which solves all our locking issues. Waiting on the patch for 13 and 14 to be finalised
firstly. thanks for attention,
i defined ftp adapter in my spring integration project , i set filename-pattern to "*.in" to filter .in extension in int-ftp:outbound-gateway with ls command, i am sure that exists files with .in extension but return empty list,my code is:
<int-ftp:outbound-gateway id="gatewayLS"
session-factory="ftpSessionFactory"
request-channel="inbound"
command="ls"
filename-pattern="*.in"
command-options="-R"
expression="payload"
reply-channel="toSplitter"/>
Update
i defined a outbound-channel-adapter to consume the channel as bellow:
<int:outbound-channel-adapter channel="toSplitter" method="handle">
<bean class="ir.ali.integration.handler.Dispatcher"/>
</int:outbound-channel-adapter>
my bean is:
#Component
public class Dispatcher {
#Autowired
#Qualifier("mvChannel")
private MessageChannel messageChannel;
#Autowired
#Qualifier("toGet")
private MessageChannel messageChannel1;
public void handle(List<FileInfo> value) {
for (FileInfo fileInfo : value) {
Message<FileInfo> fileInfoMessage = MessageBuilder.withPayload(fileInfo).build();
if (FileUtil.isExists("./backup/".concat(fileInfo.getFilename()))) {
messageChannel.send(fileInfoMessage);
} else {
messageChannel1.send(fileInfoMessage);
}
}
}
}
but not working and return empty list and i added a bellow line in for to handle it:
if (!FilenameUtils.getExtension(fileInfo.getFilename()).equals("in"))
continue;
but not worked correctly
I have a requirement to read/process an archive which contains several flat files, each file should have it's own mapping and writer.
How do I go about assigning different FieldSetmappers and Writers for each file
using the bean configuration.
I have started with extending the MultiResourceItemReader and overriding the open method
as shown here:
#Override
public void open(ExecutionContext executionContext)
throws ItemStreamException {
ZipFile zipFile;
List<Resource> resources = new ArrayList<Resource>();
try {
zipFile = new ZipFile(pathtozipfile);
Enumeration zippedFile = zipFile.entries();
while (zippedFile.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry) zippedFile.nextElement();
resources.add(new InputStreamResource(zipFile
.getInputStream(zipEntry), zipEntry.getName()));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.setResources(resources.toArray(new Resource[resources.size()]));
super.open(executionContext);
}
and the bean declaration as follows:
<bean id="itemReader" class="com.proc.spring.ZipMResourceItemReader" scope="step">
<property name="pathtozipfile" value="#{jobParameters['input.pathtozipfile']}" />
<property name="delegate" ref="delegateReader" /> </bean>
<bean id="delegateReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="lineMapper1">
<bean class="org.springframework.batch.item.file.mapping.." />
</property>
</bean>
I know programmatically, I could use the getCurrentResource() during read but I expect spring must support the ability to assign different FieldSetMappers based on current resource of the MultiResourceItemReader.
I guess perhaps extracting the files as the first step then assign each file/resource to a step: FlatFileItemReader would be solution but would prefer to use delegate method if its possible to differentiate the mappers/writers based on the resource name.
First of all: extract all files in a previous step is the best pratice.
A solution can involve a custom ResourceAwareItemReaderItemStream used to dispatch to correct reader looking at current resource name; you can do dispatch manually or using a Classifier. This custom reader is used as delegate of your multiresource item reader.
class ReaderDispatcher implements ResourceAwareItemReaderItemStream<Object> {
private ItemReader<Object> delegate1;
private ItemReader<Object> delegate2;
private ItemReader<Object> delegate1;
private ItemReader<Object> currentDelegate;
private Resource resource;
public setResource(org.springframework.core.io.Resource resource) {
this.resource = resource;
currentDelegate = getDelegateFromResource();
}
public Object read() {
return currentDelegate.read();
}
// Others interfaces implementation (or extends from one of abstract reader/stream implementations)
private ItemReader getDelegateFromResource() {
// here code to detect right reader from delegateN
}
}
(sorry for untested/incomplete code; I am unable to check it, but I hope you can see the idea behind).
You can pre-configure your readers with its own fieldSetMapper or any other customization you want.
Check ResourceAwareItemWriterItemStream for writer counterpart of reader interface.
I have written a web service client (using Java Spring and JAXB Marshaller) that works with a 3rd party web service. When I send a valid request everything works well. When I send an invalid request then the web service server responds with a SOAP Fault. The client application just fails with a UnmarshallingFailureException
org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling
exception; nested exception is javax.xml.bind.UnmarshalException:
unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault").
Appears to me that my ws client isn't able to decipher the SOAP fault returned by the web service. I wrote a custom FaultMessageResolver, but it doesn't get invoked (I set a breakpoint there but it doesn't hit. The FaultMessageResolver just worked fine before I added the Wss4jSecurityInterceptor for signature, encryption/decryption stuff). Here's the code:
public class VehicleServiceClientExceptionResolver implements FaultMessageResolver {
#Override
public void resolveFault(WebServiceMessage message) throws IOException {
SoapMessage soapMessage = (SoapMessage) message;
try {
JAXBContext context = JAXBContext.newInstance(ErrorMessages.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
ErrorMessages errorMessages = (ErrorMessages)unMarshaller.unmarshal(soapMessage.getSoapBody().getFault().getFaultDetail().getDetailEntries().next().getSource());
if (errorMessages.getErrorMessage().size() > 0) {
throw new VehicleServiceClientException(errorMessages);
}
} catch (JAXBException e) {
LOGGER.debug(e.getMessage());
}
}
}
And this custom soap fault resolver is injected into client side web service template like below:
<bean id="vehicleQuotationWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="interceptors">
<list>
<ref bean="wsSecurityInterceptor"/>
</list>
</property>
<property name="marshaller" ref="vehicleQuotationMarshaller" />
<property name="unmarshaller" ref="vehicleQuotationMarshaller" />
<property name="messageSender" ref="urlMessageSender"/>
<property name="faultMessageResolver" ref="vehicleServiceClientFaultMessageResolver" />
<property name="defaultUri" value="https://*********/*********Service"/>
</bean>
The most weird thing is although I got that unmarshall exception, I did see the encrypted server response was decrypted in my eclipse console when I change the log level from INFO to DEBUG, I am not sure where this DigesterOutputStream comes from, but I think it might be the key to solve this.
Anyone got any idea? Thanks!
DEBUG p.xml.dsig.internal.DigesterOutputStream:
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-af090516-9e00-4590-b481-c78e59d6b2fc"><soapenv:Fault><faultcode>soapenv:Client.Validation</faultcode><faultstring</faultstring><detail><em:ErrorMessages xmlns:em="urn:ford/errormessage/v1.0"><em:ErrorMessage><em:ErrorCode>GLSE903100</em:ErrorCode><em:ErrorDescription> CTT System Quote Id already exists ('1041')</em:ErrorDescription><em:ErrorTime>2014-05-16T15:13:20</em:ErrorTime></em:ErrorMessage></em:ErrorMessages></detail></soapenv:Fault></soapenv:Body>
I found the solution here: Adding a WebServiceMessageExtractor<Object> to:
WebServiceTemplate.sendAndReceive(
new WebServiceMessageCallback(),
new WebServiceMessageExtractor<Object>())
does the trick.
Another solution:
public class ExampleInterceptor implements ClientInterceptor {
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
var resp = (SoapMessage) messageContext.getResponse();
Optional.of(resp)
.filter(res -> !hasFault(res))
.orElseThrow(() -> new SoapFaultClientException(resp));
return true;
}
private boolean hasFault(final WebServiceMessage response) {
return Optional.ofNullable(response)
.filter(resp -> resp instanceof FaultAwareWebServiceMessage)
.map(resp -> (FaultAwareWebServiceMessage) resp)
.map(FaultAwareWebServiceMessage::hasFault)
.orElse(false);
}
}
#Configuration
public class ExampleConnectorConfig extends WSConnectorConfig
#Bean
public WSConnector soapConnector(Jaxb2Marshaller marshaller) {
var client = new WSConnector(messageFactory());
client.setInterceptors(new ClientInterceptor[]{new ExampleInterceptor()});
client.setDefaultUri(proxy);
return client;
}
//Example
#Bean
public SaajSoapMessageFactory messageFactory() {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
messageFactory.afterPropertiesSet();
return messageFactory;
}
}
Is there anything that can achieve the equivalent of the below:
<import resource="a.xml">
<prop name="key" value="a"/>
</import>
<import resource="a.xml">
<prop name="key" value="b"/>
</import>
Such that the beans defined in resouce a would see the property key with two different values? The intention would be that this would be used to name the beans in the imports such that resource a.xml would appear:
<bean id="${key}"/>
And hence the application would have two beans named a and b now available with the same definition but as distinct instances. I know about prototype scope; it is not intended for this reason, there will be many objects created with interdepednencies that are not actually prototypes. Currently I am simply copying a.xml, creating b.xml and renaming all the beans using the equivalent of a sed command. I feel there must be a better way.
I suppose that PropertyPlaceholderConfigurers work on a per container basis, so you can't achieve this with xml imports.
Re The application would have two beans named a and b now available with the same definition but as distinct instances
I think you should consider creating additional application contexts(ClassPathXmlApplicationContext for example) manually, using your current application context as the parent application context.
So your many objects created with interdependencies sets will reside in its own container each.
However, in this case you will not be able to reference b-beans from a-container.
update you can postprocess the bean definitions(add new ones) manually by registering a BeanDefinitionRegistryPostProcessor specialized bean, but this solution also does not seem to be easy.
OK, here's my rough attempt to import xml file manually:
disclaimer: I'm very bad java io programmer actually so double check the resource related code :-)
public class CustomXmlImporter implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
private Map<String, String> properties;
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public Map<String, String> getProperties() {
return properties;
}
private void readXml(XmlBeanDefinitionReader reader) {
InputStream inputStream;
try {
inputStream = new ClassPathResource(this.classpathXmlLocation).getInputStream();
} catch (IOException e1) {
throw new AssertionError();
}
try {
Scanner sc = new Scanner(inputStream);
try {
sc.useDelimiter("\\A");
if (!sc.hasNext())
throw new AssertionError();
String entireXml = sc.next();
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${",
"}", null, false);
Properties props = new Properties();
props.putAll(this.properties);
String newXml = helper.replacePlaceholders(entireXml, props);
reader.loadBeanDefinitions(new ByteArrayResource(newXml.getBytes()));
} finally {
sc.close();
}
} finally {
try {
inputStream.close();
} catch (IOException e) {
throw new AssertionError();
}
}
}
private String classpathXmlLocation;
public void setClassPathXmlLocation(String classpathXmlLocation) {
this.classpathXmlLocation = classpathXmlLocation;
}
public String getClassPathXmlLocation() {
return this.classpathXmlLocation;
}
#Override
public void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry) throws BeansException {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
readXml(reader);
}
}
XML configuration:
<bean class="CustomXmlImporter">
<property name="classPathXmlLocation" value="a.xml" />
<property name="properties">
<map>
<entry key="key" value="a" />
</map>
</property>
</bean>
<bean class="CustomXmlImporter">
<property name="classPathXmlLocation" value="a.xml" />
<property name="properties">
<map>
<entry key="key" value="b" />
</map>
</property>
</bean>
this code loads the resources from classpath. I would think twice before doing something like that, anyway, you can use this as a starting point.