How to best publish files to multiple sftp targets - java

I have the following sftp bean Id configured in my spring application-context file. It defines two sftp targets which my application is to send different types of files to:
<bean id="targetA"
class="com.sftp.Sender">
<constructor-arg value="${A.host}"/>
<constructor-arg value="${A.username}"/>
<constructor-arg value="${A.remoteDir}"/>
<constructor-arg value="${A.notificationDir}"/>
</bean>
<bean id="targetB" class="com.sftp.Sender">
<constructor-arg value="${B.host}"/>
<constructor-arg value="${B.username}"/>
<constructor-arg value="${B.remoteDir}"/>
</bean>
My main class looks something like this:
public class Main {
Occ occ= ctx.getBean("occ", Occ.class);
public static void main(String[] args) {
if (args != null && args.length >= 1) {
String target = args[0];
occ.rePublish(target);
} else {
String target = "Both";
orc.processReports(target);
}
}
My Occ class something like this:
public class Occ {
public void rePublish(String target) {
processReports(target);
}
public void processReports(String target) {
// determines who the target is and prepare the appropriate
// file to send to that target
if (target.equal("Both")) {
// prepare both target A and B files
}
}
}
Generally, when the program is executed, it should prepare both sets of files for both target A and target B. In certain scenarios, one transfer may fail and I want to be able to prepare and distribute files for just that target. I can only think of passing in the resend target as arguments in main method. But is this the best way to do this? What are your thoughts and how would you do it? Can I restructure the above code in other better ways?

Related

how to skip blank lines while writing to csv file with Spring Batch FlatFileItemWriter

I am trying to write the java data objects to .csv file using the Spring Batch FlatFifileItemWriter exactly as mentioned in the example at below location. http://www.mkyong.com/spring-batch/spring-batch-example-xml-file-to-csv-file/
The writer configuration is like below.
<bean id="cvsFileItemWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
<!-- write to this csv file -->
<property name="resource" value="file:cvs/report.csv" />
<property name="shouldDeleteIfExists" value="true" />
<property name="lineAggregator">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="delimiter" value="," />
<property name="fieldExtractor">
<bean
class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="refId, name, age, csvDob, income" />
</bean>
</property>
</bean>
</property>
</bean>
Since the FlatFileItemWriter has following write() method definition in the API, even though the LineAggregator returns empty String,it still writes to file.
StringBuilder lines = new StringBuilder();
int lineCount = 0;
for (T item : items) {
lines.append(lineAggregator.aggregate(item) + lineSeparator);
lineCount++;
}
try {
state.write(lines.toString());
}
is there any way to configure it to skip if the line is blank while writing to csv file in java using spring batch.
You'll want to add an ItemProcessor<YourObject> to your step. In it, you can add a simple YourObject process(YourObject item) method that checks if the object will result in a blank line. If not, return the object. If so, return null.
This will prevent the items you wish to discard from getting to the writer and will increase the FILTER_COUNT when you look at the BATCH_STEP_EXECUTION table for that Step.
More on filtering objects can be found in the Spring Batch docs.
Edit: The tutorial you referenced in your answer includes an example of a filtering processor (FilterReportProcessor) as well.
if memberclass is empty here's another way to writer using composite writer.
Create another writer as below.
public class YourMemberFileItemWriter extends FlatFileItemWriter<YourMainClass>{
#Override
public String doWrite(List<? extends YourMainClass> items) {
StringBuilder lines = new StringBuilder();
for (YourMainClass item : items) {
if ( YourMainClass.getYourMemberClass() != null) {
lines.append(this.lineAggregator.aggregate(item)).append(this.lineSeparator);
}
}
return lines.toString();
}
}
Define your composite writer.
CompositeItemWriter compositeItemWriter = new CompositeItemWriter<>();
compositeItemWriter.setDelegates(Arrays.asList( mainClassCsv()
,memberClassCsv());
return compositeItemWriter;
Add member class methods as below to composite writer:
public FlatFileItemWriter memberClassCsv() {
// TODO Auto-generated method stub
YourMemberFileItemWriter fileWriter = new YourMemberFileItemWriter();
fileWriter.setName("yourMemberClass");
fileWriter.setResource(new FileSystemResource("memberfile.csv"));
fileWriter.setLineAggregator(getMemberClassDelimitedLineAggregator());
return fileWriter;
}
public DelimitedLineAggregator<YourMainClass> getMemberClassDelimitedLineAggregator() {
BeanWrapperFieldExtractor<YourMainClass> beanWrapperFieldExtractor = new BeanWrapperFieldExtractor<YourMainClass>();
beanWrapperFieldExtractor.setNames(new String[] {
"memberClassField.fieldname1"
,"memberClassField.fieldname2"
});
DelimitedLineAggregator<YourMainClass> delimitedLineAggregator = new DelimitedLineAggregator<YourMainClass>();
delimitedLineAggregator.setDelimiter(",");
delimitedLineAggregator.setFieldExtractor(beanWrapperFieldExtractor);
return delimitedLineAggregator;
}

wrap spring bean with another bean

I have some service bean which is accessible by identifier someSpecificService which I need to modify.
Beans are defined in different xml files and are collected together in runtime. So one big xml file is created where all these xmls are imported:
context.xml
....
<import path="spring1.xml" />
<import path="spring2.xml" />
...
So there is following configuration:
<-- definitions from spring1.xml -->
<alias name="defaultSomeSpecificService" alias="someSpecificService" />
<bean id="defaultSomeSpecificService" class="..."/>
....
<!-- definitions from spring2.xml -->
<alias name="myOwnSomeSpecificService" alias="someSpecificService" />
<bean id="myOwnSomeSpecificService" class="..." /> <!-- how to inject previously defined someSpecificService into this new bean? -->
I would like to override someSpecificService from spring1.xml in spring2.xml, however I do need to inject previously defined bean defaultSomeSpecificService and all I know is its alias name someSpecificService which I need to redefine to new bean myOwnSomeSpecificService.
Is it possible to implement?
One solution would be to avoid trying to override the definition, by creating a proxy for the service implementation to intercept all calls towards it.
1) For the sake of the example, suppose the service would be something like:
public interface Service {
public String run();
}
public class ExistingServiceImpl implements Service {
#Override
public String run() {
throw new IllegalStateException("Muahahahaha!");
}
}
2) Implement an interceptor instead of myOwnSomeSpecificService:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class SomeSpecificServiceInterceptor implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
String status;
try {
// allow the original invocation to actually execute
status = String.valueOf(invocation.proceed());
} catch (IllegalStateException e) {
System.out.println("Existing service threw the following exception [" + e.getMessage() + "]");
status = "FAIL";
}
return status;
}
}
3) In spring2.xml define the proxy creator and the interceptor:
<bean id="serviceInterceptor" class="com.nsn.SomeSpecificServiceInterceptor" />
<bean id="proxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="someSpecificService"/>
<property name="interceptorNames">
<list>
<value>serviceInterceptor</value>
</list>
</property>
</bean>
4) Running a small example such as:
public class Main {
public static void main(String[] args) {
Service service = new ClassPathXmlApplicationContext("context.xml").getBean("someSpecificService", Service.class);
System.out.println("Service execution status [" + service.run() + "]");
}
}
... instead of the IllegalStateException stacktrace you'd normally expect, it will print:
Existing service threw the following exception [Muahahahaha!]
Service execution status [FAIL]
Please note that in this example the service instance is not injected in the interceptor as you asked because I had no user for it. However should you really need it, you can easily inject it via constructor/property/etc because the interceptor is a spring bean itself.

Property is not found from properties file using #Value

I use properties file in spring framework
root-context.xml
<context:property-placeholder location="classpath:config.properties" />
<util:properties id="config" location="classpath:config.properties" />
java code
#Value("#{config[ebookUseYN]}")
String EBOOKUSEYN;
when Using url call(#RequestMapping(value="/recommendbooks" , method=RequestMethod.GET, produces="application/json;charset=UTF-8")).. this work!
but, i use method call,
public void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
IndexManageController indexManage = new IndexManageController();
CommonSearchDTO commonSearchDTO = new CommonSearchDTO();
try {
if("Y".equals(EBOOKUSEYN)){
indexManage.deleteLuceneDocEbook();
indexManage.initialBatchEbook(null, commonSearchDTO);
}
indexManage.deleteLuceneDoc(); <= this point
indexManage.deleteLuceneDocFacet();
indexManage.initialBatch(null, commonSearchDTO);
}catch (Exception e) {
e.printStackTrace();
}
}
when 'this point ' method call, changing controller, and don't read properties file field..
#Value("#{config[IndexBasePath]}")
String IndexBasePath;
#RequestMapping(value="/deleteLuceneDoc" , method=RequestMethod.GET, produces="application/json;charset=UTF-8")
public #ResponseBody ResultCodeMessageDTO deleteLuceneDoc()
throws Exception
{
long startTime = System.currentTimeMillis();
ResultCodeMessageDTO result = new ResultCodeMessageDTO();
System.out.println(IndexBasePath);
}
It doesn't read IndexBasePath
In your code you are creating a new instance of the IndexManageController, Spring doesn't know this instance and as such it will never be processed.
public void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
IndexManageController indexManage = new IndexManageController();
Instead of creating a new instance inject the dependency for the IndexManageController so that it uses the pre-configured instance constructed and managed by Spring. (And remove the line which constructs a new instance of that class).
public class MyJob {
#Autowired
private IndexManageController indexManage;
}
Your configuration is also loading the properties twice
<context:property-placeholder location="classpath:config.properties" />
<util:properties id="config" location="classpath:config.properties" />
Both load the config.properties file. Simply wire the config to the property-placeholder element.
<context:property-placeholder properties-ref="config"/>
<util:properties id="config" location="classpath:config.properties" />
Saves you loading twice and saves you another bean.

Refreshing Grails applicationContext

In my configuration's spring/resources.xml file, I define a bean like this :
<bean id="myService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
<property name="serviceUrl" value="http://${remote.host}:8080/MyAgent/remoting/MyService"/>
<property name="serviceInterface" value="services.MyService"/>
</bean>
In my Config.groovy file I have :
remote.host = "someipaddress"
Now I'd like to change this placeholder's value at runtime. In a regular spring app, I do this through a PropertyPlaceHolderConfigurer, then I refresh the context and it works.
In Grails, how can I refresh the context ?
Regards,
Philippe
Ok I give up the refreshing approach.
As a workaround, I created a grails service that looks like this :
class myService {
def myRemoteService
static transactional = false
private MyRemoteService getService(String remoteServiceURL) {
HessianProxyFactory factory = new HessianProxyFactory();
try {
return (MyRemoteService) factory.create(MyRemoteService.class, url);
}
catch (MalformedURLException e) {
e.printStackTrace()
}
return null
}
def someRemoteMethod(String remoteServiceURL) {
getService(remoteServiceURL).myRemoteMethod()
}
}
This allows me to invoke the remote service on any distant machine dinamically.
I'm still interested in a cleaner solution as this makes me rewrite a wrapper method for each remote method :-S
Why not just update the value directly:
def blabla
...
void someServiceMethod() {
blabla.someProperty = 'new value'
}
or
def blabla
...
def someControllerAction = {
blabla.someProperty = 'new value'
}
grailsApplication expose a refresh() method, i'm not sure if it will reload spring context, you could try.
I did a quick search in grails mailing list, and looks like grails do not support app-context reload.
You could try implement InitializingBean and get the values direct from app config.
import org.springframework.beans.factory.InitializingBean
class ExampleService implements InitializingBean {
def grailsApplication
def setting
void afterPropertiesSet() {
this.setting = grailsApplication.config.setting
}
}
Maybe you can listen for changes in the config or get the property every time you need to use it, i do not know, i can not create a app to run some tests right now.
Not tested, but try:
import grails.spring.BeanBuilder
def bb = new BeanBuilder(
application.parentContext,
new GroovyClassLoader(application.classLoader))
def beans = bb.beans {
myService(org.springframework.remoting.caucho.HessianProxyFactoryBean) {
...
}
}
beans.registerBeans(application.mainContext)
This is pretty much what plugins do when they need to swap in new bean instances. You could also raise a JIRA issue for a nicer way of doing this.

Spring JDBC DAO

Im learning Spring (2 and 3) and i got this method in a ClientDao
public Client getClient(int id) {
List<Client> clients= getSimpleJdbcTemplate().query(
CLIENT_GET,
new RowMapper<Client>() {
public Client mapRow(ResultSet rs, int rowNum) throws SQLException {
Client client = new ClientImpl(); // !! this (1)
client.setAccounts(new HashSet<Account>()); // !! this (2)
client.setId(rs.getInt(1));
client.setName(rs.getString(2));
return client;
}
},id
);
return clients.get(0);
}
and the following Spring wiring:
<bean id="account" class="client.AccountRON" scope="prototype">
<property name="currency" value = "RON" />
<property name="ammount" value="0" />
</bean>
<bean id="client" class="client.ClientImpl" scope="prototype">
<property name="name" value="--client--" />
<property name="accounts">
<set>
</set>
</property>
</bean>
The things is that i dont like the commented lines of java code (1) and (2).
I'm going to start with (2) which i think is the easy one: is there a way i can wire the bean in the .xml file to tell spring to instantiate a set implementation for the 'accounts' set in ClientImpl? so i can get rid of (2)
Now moving on to (1): what happens if the implementation changes ? do i really need to write another DAO for a different implementation? or do i have to construct a BeanFactory ? or is there another more beautiful solution ?
Thanks!
I'm a bit confused here - why have you defined a ClientImpl bean in your XML, but not using it in your Java?
Your already have most of the solution, you just need to fetch a new ClientImpl from Spring each iterations through the loop:
private #Autowired BeanFactory beanFactory;
public Client getClient(int id) {
List<Client> clients= getSimpleJdbcTemplate().query(
CLIENT_GET,
new RowMapper<Client>() {
public Client mapRow(ResultSet rs, int rowNum) throws SQLException {
Client client = beanFactory.getBean(Client.class);
client.setId(rs.getInt(1));
client.setName(rs.getString(2));
return client;
}
},id
);
return clients.get(0);
}
With this approach, the actual construction and initialization of ClientImpl is done by Spring, not your code.

Categories

Resources