Get current resource name using MultiResourceItemReader Spring batch - spring-batch

I am using MultiResourceItemReader in Spring Batch for reading multiple XML files and I want to get current resource.Here is my configuration:
public class MultiFileResourcePartitioner extends MultiResourceItemReader<MyObject> {
#Override
public void update(final ExecutionContext pExecutionContext) throws ItemStreamException {
super.update(pExecutionContext);
if (getCurrentResource() != null && getCurrentResource().getFilename() != null) {
System.out.println("update:" + getCurrentResource().getFilename());
}
}
}
And my reader:
<bean id="myMultiSourceReader"
class="mypackage.MultiFileResourcePartitioner">
<property name="resources" value="file:${input.directory}/*.xml" />
</bean>
The code above read XML files correctly but the method getCurrentResources() return null.
By debugging, the batch enter to update method
Please help!

There is a specific interface for this problem called ResourceAware: it's purpouse is to inject current resource into objects read from a MultiResourceItemReader.
Check this thread for further information.

I tried it with a simple Listener for logging the current resource from a injected {#link MultiResourceItemReader}. Saves the value to the StepExecutionContext.
To get it working with a step scoped MultiResourceItemReader i access the proxy directly, see http://forum.springsource.org/showthread.php?120775-Accessing-the-currently-processing-filename, https://gist.github.com/1582202 and https://jira.springsource.org/browse/BATCH-1831.
public class GetCurrentResourceChunkListener implements ChunkListener, StepExecutionListener {
private StepExecution stepExecution;
private Object proxy;
private final List<String> fileNames = new ArrayList<>();
public void setProxy(Object mrir) {
this.proxy = mrir;
}
#Override
public void beforeStep(StepExecution stepExecution) {
this.stepExecution = stepExecution;
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
return stepExecution.getExitStatus();
}
#Override
public void beforeChunk(ChunkContext cc) {
if (proxy instanceof Advised) {
try {
Advised advised = (Advised) proxy;
Object obj = advised.getTargetSource().getTarget();
MultiResourceItemReader mrirTarget = (MultiResourceItemReader) obj;
if (mrirTarget != null
&& mrirTarget.getCurrentResource() != null
&& !fileNames.contains(mrirTarget.getCurrentResource().getFilename())) {
String fileName = mrirTarget.getCurrentResource().getFilename();
fileNames.add(fileName);
String index = String.valueOf(fileNames.indexOf(fileName));
stepExecution.getExecutionContext().put("current.resource" + index, fileName);
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
#Override
public void afterChunk(ChunkContext cc) {
}
#Override
public void afterChunkError(ChunkContext cc) {
}
}
see https://github.com/langmi/spring-batch-examples-playground for a working example - look for "GetCurrentResource..."

public class CpsFileItemProcessor implements ItemProcessor<T, T> {
#Autowired
MultiResourceItemReader multiResourceItemReader;
private String fileName;
#Override
public FileDetailsEntityTemp process(T item) {
if(multiResourceItemReader.getCurrentResource()!=null){
fileName = multiResourceItemReader.getCurrentResource().getFilename();
}
item.setFileName(fileName);
return item;
}
}

Related

Accessing the resource read by FlatFileReader in SkipPolicy SpringBatch [duplicate]

I have a job with Spring Batch which I read some files with BeanIO, and I would handle invalid files, so I created a SkipPolicy class.
public class FileVerificationSkipper implements SkipPolicy {
private static final FluentLogger LOGGER = LoggerService.init(FileVerificationSkipper.class);
#Override
public boolean shouldSkip(Throwable exception, int skipCount) throws SkipLimitExceededException {
if (exception instanceof FileNotFoundException) {
return false;
}
if (exception instanceof BeanReaderException && skipCount <= 10) {
LOGGER.all().logKey("Error on read file: ").value(exception).asError();
return true;
}
else {
return false;
}
}
}
On my reader step I access the name like this: #Value("#{jobParameters['input.file.name']}") String inputFile
I would like to log the filename, how could I do that?
Debugging how Spring Batch inject the parameters I found the solution.
I just need to add #StepScope in the class and create the variable where I want to inject the parameter:
#Component
#StepScope
#RequiredArgsConstructor
public class FileVerificationSkipper implements SkipPolicy {
#Value("#{jobParameters['input.file.name']}")
private String inputFile;
...
}

Access job filename parameter in SkipPolicy of Spring Batch

I have a job with Spring Batch which I read some files with BeanIO, and I would handle invalid files, so I created a SkipPolicy class.
public class FileVerificationSkipper implements SkipPolicy {
private static final FluentLogger LOGGER = LoggerService.init(FileVerificationSkipper.class);
#Override
public boolean shouldSkip(Throwable exception, int skipCount) throws SkipLimitExceededException {
if (exception instanceof FileNotFoundException) {
return false;
}
if (exception instanceof BeanReaderException && skipCount <= 10) {
LOGGER.all().logKey("Error on read file: ").value(exception).asError();
return true;
}
else {
return false;
}
}
}
On my reader step I access the name like this: #Value("#{jobParameters['input.file.name']}") String inputFile
I would like to log the filename, how could I do that?
Debugging how Spring Batch inject the parameters I found the solution.
I just need to add #StepScope in the class and create the variable where I want to inject the parameter:
#Component
#StepScope
#RequiredArgsConstructor
public class FileVerificationSkipper implements SkipPolicy {
#Value("#{jobParameters['input.file.name']}")
private String inputFile;
...
}

How do I access Job parameters in a Spring Batch Listener?

I'm working with ItemListenerSupport to do some error handling for ItemReadListener, ItemProcessListener, and ItemWriteListener. I want to access the job parameters in this instance. How do I fetch those? I tried #BeforeStep to inject the StepExecution and Jobexecution but neither worked.
To get the handle of Job Parameters you can implement StepExecutionListener to your listener Class to make use of Overridden methods beforeStep and afterStep
#Override
public void beforeStep(StepExecution stepExecution) {
String name = (String) stepExecution.getJobExecution().getExecutionContext()
.get("name");
}
#Override
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getStatus() == BatchStatus.COMPLETED) {
return ExitStatus.COMPLETED;
}
return ExitStatus.FAILED;
}
You can declare your listener as a step scoped bean and inject job parameters in it, something like:
#Bean
#StepScope
public ItemReadListener itemReadListener(final #Value("#{jobParameters['name']}") String name) {
return new ItemListenerSupport() {
#Override
public void afterRead(Object item) {
System.out.println("in listener, job param name=" + name);
super.afterRead(item);
}
};
}

Circular view path exception with Spring framework And AngularJs

I am relatively new to the Spring boot frame work. I had a basic web application built in Angular with Spring boot connected and to a Mongodb. The application allowed users to add todo lists and register for the the website. When the application started it returned the todolists stored in mongodb to the view. The user could register, and there details were stored in a Mongo repository.
When I added and implemented spring security I got the error message
Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
What I want to happen is, when the webapp loads, I want the index.html to be injected with todo.html. Then if a user logs in they will be directed to another page or some Ui feature to become available. At the moment I am stuck in this Circular view pathloop.
I have looked through the different answers but I still am confused as to what exactly is causing the issue. I believe it is in the WebSecurityConfig class
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
UserDetailsService userDS;
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers("/api/todos/*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDS);
}
#Override
protected UserDetailsService userDetailsService() {
return userDS;
}
}
AuthUserDetailsService
#Repository
public class AuthUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository users;
private org.springframework.security.core.userdetails.User userdetails;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// TODO Auto-generated method stub
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
todoapp.models.User user = getUserDetail(username);
userdetails = new User (user.getUsername(),
user.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(user.getRole())
);
return userdetails;
}
public List<GrantedAuthority> getAuthorities(Integer role) {
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
if (role.intValue() == 1) {
authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
} else if (role.intValue() == 2) {
authList.add(new SimpleGrantedAuthority("ROLE_USER"));
}
return authList;
}
private todoapp.models.User getUserDetail(String username){
todoapp.models.User user = users.findByUsername(username);
return user;
}
}
TodoController
#RestController
#RequestMapping("/api/todos")
public class TodoController {
#Autowired
TodoRepository todoRepository;
#RequestMapping(method=RequestMethod.GET)
public List<Todo> getAllTodos() {
return todoRepository.findAll();
}
#RequestMapping(method=RequestMethod.POST)
public Todo createTodo(#Valid #RequestBody Todo todo) {
return todoRepository.save(todo);
}
#RequestMapping(value="{id}", method=RequestMethod.GET)
public ResponseEntity<Todo> getTodoById(#PathVariable("id") String id) {
Todo todo = todoRepository.findOne(id);
if(todo == null) {
return new ResponseEntity<Todo>(HttpStatus.NOT_FOUND);
} else {
return new ResponseEntity<Todo>(todo, HttpStatus.OK);
}
}
#RequestMapping(value="{id}", method=RequestMethod.PUT)
public ResponseEntity<Todo> updateTodo(#Valid #RequestBody Todo todo, #PathVariable("id") String id) {
Todo todoData = todoRepository.findOne(id);
if(todoData == null) {
return new ResponseEntity<Todo>(HttpStatus.NOT_FOUND);
}
todoData.setTitle(todo.getTitle());
todoData.setCompleted(todo.getCompleted());
Todo updatedTodo = todoRepository.save(todoData);
return new ResponseEntity<Todo>(updatedTodo, HttpStatus.OK);
}
#RequestMapping(value="{id}", method=RequestMethod.DELETE)
public void deleteTodo(#PathVariable("id") String id) {
todoRepository.delete(id);
}
}
RecourceController
#Configuration
public class ResourceController extends WebMvcConfigurerAdapter{
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/api/todos").setViewName("home");
registry.addViewController("/register").setViewName("register");
registry.addViewController("/login").setViewName("login");
}
}
Any help will be greatly appreciated.
This is the Project Layout.
You forgot to add .html to your view names:
registry.addViewController("/").setViewName("app/views/index.html");
registry.addViewController("/api/todos").setViewName("app/views/home.html");
registry.addViewController("/register").setViewName("app/views/register.html");
registry.addViewController("/login").setViewName("app/views/login.html");
Spring Boot registers a ResourceHttpRequestHandler which is capable of resolving static resources under static folder.
Because you set login as view name ResourceHttpRequestHandler tries to load static/login which apparently does not exist.
Change it to app/views/login.html so that static/login becomes static/app/views/login.html.

delegate HTTP request to Jersey

I have a nano HTTP based web server, that is supposed to delegate its calls to a jersey 2.22.2. On the webserver class constructor I declare an ApplicationHandler as a instance variable:
ApplicationHandler newHandler;
Then in the constructor I initilize it and register a Sample resource class:
Object[] instances = new Object[1];
instances[0] = new SampleResource();
ResourceConfig app = new ResourceConfig();
app.registerInstances(instances);
newHandler = new ApplicationHandler(app);
On the method that processes Http requests I create a ContainerRequest and execute the apply method on the application handler :
SecurityContext secContext = new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return "user";
}
};
}
#Override
public boolean isUserInRole(String s) {
return true;
}
#Override
public boolean isSecure() {
return true;
}
#Override
public String getAuthenticationScheme() {
return null;
}
};
PropertiesDelegate propertiesDelegate = new PropertiesDelegate() {
Map<String, Object> props = new HashMap<>();
#Override
public Object getProperty(String s) {
return props.get(s);
}
#Override
public Collection<String> getPropertyNames() {
return props.keySet();
}
#Override
public void setProperty(String s, Object o) {
props.put(s, o);
}
#Override
public void removeProperty(String s) {
props.remove(s);
}
};
ContainerRequest request = new ContainerRequest(new URI("http://localhost:2000"), new URI("/test"), session.getMethod().toString(), secContext, propertiesDelegate);
Future<ContainerResponse> responseFuture = newHandler.apply(request);
ContainerResponse response = responseFuture.get();
Object entity = response.getEntity();
Below is the code for the SampleResource class :
public class SampleResource {
#GET
#Path("test")
public Response testMethod() {
return Response.status(Response.Status.OK).build();
}
}
The main reason for doing this is that I want to call a custom API that injects objects into the annotated resource classes.
Stepping through the code, all I get is a NotFoundException.
If you want to inject custom Objects to the resources class you can do that in two waus
using #Context -- By adding your custom object to application context
usign #Inject -- By binding the application to resource config
to use #Context , you need to extend the java.security.Principal object and declare your object fields, and you can instantiate and assign values by using security context.
to user #InJect , you need to register org.glassfish.hk2.utilities.binding.AbstractBinder like below
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("org.foo.rest;org.bar.rest");
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(ObjectThatneedtoInject.class).to(yourClass.class);
}
});
}
}