Struts2+ Spring : Request Paremeters dont get reset in Action objects? - java

I am developing Spring 3 + Struts2 application and I configure my Actions in Spring as follows:
<bean id="patientSearchAPIClass" class="com.axiohelix.nozoki.web.action.api.PatientSearch">
<property name="searchService" ref="searchService"/>
</bean>
But in my Action class I keep fields to store Request parameters ,
public class PatientSearch extends ActionSupport {
public String getPharumoId() {
return pharumoId;
}
public void setPharumoId(String pharumoId) {
this.pharumoId = pharumoId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
private String pharumoId;
..
public String execute(){
searchResults=searchService.searchPatients(pharumoId, name,
birthday,
pharmacyId,
clinic,
doctorName,
drugName,
supplyDate,
offset,
pageSize);
return Action.SUCCESS;
}
This Action returns a JSON output and I access it using URL like:
http://localhost/app/searchAPI.action?name=UserName
then next time if I access using URL :
http://localhost/app/searchAPI.action
The field 'name' is till set to previous "UserName" value.
1.How to reset these values per request ?
2.I thought Action classes are instantiated per request,is it not ?

Problem was with the way Action classes are getting created by Spring.By default Spring creates singleton instances and for Struts2 ,Action classes also work as Model, due to this framework create a new instance of Action and place it in to values stack.
While creating action class using Spring make sure to define scope as prototype like
<bean id="patientSearchAPIClass"
class="com.axiohelix.nozoki.web.action.api.PatientSearch" scope=prototype>
So that new instance of Action should get created by Spring for each request.

Related

How to create a bean and autowire at run time dynamically based on user input param

How to create Student class object at run time dynamically based on the parameters received in the URL and inject in to WebapplicationContext so the IoC container can auto wire it automaticallly to Access class?
I need to create a bean at run time based on user parameters.
#RestController
public class FunRestController {
#GetMapping("/{id}/{name}")
public String welcomeToBoot(#PathVariable int id, #PathVariable String name) {
// How to create Student class object at run time dynamically based
// on the param received in the URL and can auto wire it dynamically to ***Access*** class below ?
return "Welcome " + name;
}
}
I need to Autowire a run time bean created
public class Access {
#Autowired
Student s;
void print() {
System.out.println(s.name);
}
}
POJO:
public class Student {
public int id;
public String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
}
I would suggest not to #Autowired the Student object but instead, pass it as a parameter to a function. Something as follows:
public class Access {
void print(Student s) {
System.out.println(s.name);
}
}
Now you just need to call print() method with the given Student. If you need Access to be a Spring-managed Bean so that you can inject it into your Controller, you would need to annotate it with #Component.
Instead of creating bean, you can create a thread local variable and initialise it as the first thing. Then it'll be available throughout the request / response scope

The onFlushDirty Hibernate Interceptor method is never called

Question: Why MyInterceptor#onFlushDirty is never called?
I extend AbstractEntityManagerFactoryBean in xml configs like
<bean id="myEntityManagerFactory" parent="abstractEntityManagerFactoryBean" abstract="true">
<property name="entityInterceptor">
<bean class="xxxx.MyInterceptor"/>
</property>
</bean>
<bean id="abstractEntityManagerFactoryBean" class="xxxx.MyEntityManagerFactoryBean"/>
MyEntityManagerFactoryBean
public class MyEntityManagerFactoryBean extends AbstractEntityManagerFactoryBean implements LoadTimeWeaverAware {
private Interceptor entityInterceptor;
public Interceptor getEntityInterceptor() {
return entityInterceptor;
}
public void setEntityInterceptor(Interceptor interceptor) {
entityInterceptor = interceptor;
}
}
MyInterceptor:
public class MyInterceptor extends EmptyInterceptor {
public MyInterceptor() {
System.out.println("init"); // Works well
}
// PROBLEM - is never called
#Override
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
if (entity instanceof File) {
.....
}
return false;
}
}
UPDATE: [explanation why custom dirty policy looks like not my way]
I want update modified timestamp each time I change something in Folder entity EXCEPT folderPosition. In the same time folderPosition should be persistent and not transient (means cause entity to be dirty).
Due I use Spring Transactional and Hibernate Templates, there is some nuances:
1) I can't update modified timestamp at the end of each setter like:
public void setXXX(XXX xxx) {
//PROBLEM: Hibernate templates collect object via setters,
//means simple get query will cause multiple 'modified' timestamp updates
this.xxx = xxx;
this.modified = new Date();
}
2) I can't call setModified manually, because it has about 25 fields, and setXXX for each field is scattered across whole app. And I have no power to make refactoring.
#Entity
public class Folder {
/**
* GOAL: Changing of each of these fields except 'folderPosition' should cause
* 'modified' timestamp update
*/
private long id;
private String name;
private Date created;
private Date modified;
private Integer folderLocation;
#PreUpdate
public void preUpdate() {
//PROBLEM : change modified even if only location field has been changed!
//PROBLEM: need to know which fields have been updated!
modified = new Date();
}
....
}
You need to extend the findDirty method not onFlushDirty. Check this tutorial for a detail explanation with a reference to a GitHub working example.

How to implement Jackson custom serialization outside a domain bean?

I have a Spring managed bean...
#Component("Foobean")
#Scope("prototype")
public class foobean {
private String bar1;
private String bar2;
public String getBar1() {
return bar1;
}
public void setBar1(String bar1) {
this.bar1 = bar1;
}
public String getBar2() {
return bar2;
}
public void setBar2(String bar2) {
this.bar2 = bar2;
}
}
...and because I am using Dojo Dgrid to display an ArrayList of this bean, I am returning it into the controller as a JSON string:
#Controller
#RequestMapping("/bo")
public class FooController {
#Autowired
private FooService fooService
#RequestMapping("action=getListOfFoos*")
#ResponseBody
public String clickDisplayFoos(
Map<String, Object> model) {
List<Foobean> foobeans = fooService.getFoobeans();
ObjectMapper objMapper = new ObjectMapper();
String FooJson = null;
try {
FooJson = objMapper.writeValueAsString(foobeans);
} catch (JsonGenerationException e) {
etc.
}
However, my grid needs an additional column which will contain a valid action for each Foo; that action is not really dependent on any data in individual Foos -- they'll all have the same valid action -- repeated on each line of the resulting DGrid -- but that value is actually dependent upon security roles on the session...which can't be sent to the front end in a Json. So, my solution is twofold:
First I need to add a "virtual" Json property to the bean... which I can do in the bean with #JsonProperty on a method...
#JsonProperty("validActions")
public String writeValidActions {
return "placeHolderForSerializerToChange";
}
...but it just generates a placeholder. To really generate a valid value,
I need to reference the security role of the session,
which I am very reluctant to code in the above method. (A service call in
the domain bean itself? Seems very wrong.) I
think I should create a custom serializer and put the logic -- and the reference
to the Session.Security role in there. Are my instincts right, not to
inject session info into a domain bean method? And if so, what would such a
custom serializer look like?
Yes, I wouldn't put Session Info in to the domain or access session directly in my domain.
Unless there is a specific reason, you could simply add the logic in your action class.
public String clickDisplayFoos(){
List<Foo> foos = service.getFoos();
for(iterate through foos){
foo.setValidAction(session.hasSecurityRole())
}
String json = objMapper.writeValueAsString(foobeans);
return json;
}
I don't like the idea of setting new values as part of the serialization process. I feel custom serializers are meant to transform the representation of a particular property rather than add new values to a property.

Spring MVC #ModelAttribute #SessionAttributes - Why does a model attribute need a #ModelAttribute annotated method?

This is how it all looks now:
#SessionAttributes("shoppingCart")
public class ItemController {
#ModelAttribute
public ShoppingCart createShoppingCart() {
return new ShoppingCart();
}
#RequestMapping(value=RequestMappings.ADD_TO_CART + RequestMappings.PARAM_ITEM_ID, method=RequestMethod.GET)
public String addToCart(#PathVariable("itemId") Item item, #ModelAttribute ShoppingCart shoppingCart) {
if(item != null) {
shoppingCartService.addItem(shoppingCart, item);
}
return ViewNamesHolder.SHOPPING_CART;
}
}
When the addToCart method is called first time, the shoppingCart object will be initialized by the createShoppingCart method. After the addToCart method runs, the initialized object will be added to the session and it will be used from the session for the later use. That means the createShoppingCart methode is called just once (as long as it does not removed from the session).
Why does Spring eliminate the need for the ModelAttribute annotated initializer method, by simply creating this object whenever is needed? Then it would all look simpler like this:
#SessionAttributes("shoppingCart")
public class ItemController {
#RequestMapping(value=RequestMappings.ADD_TO_CART + RequestMappings.PARAM_ITEM_ID, method=RequestMethod.GET)
public String addToCart(#PathVariable("itemId") Item item, #ModelAttribute ShoppingCart shoppingCart) {
if(item != null) {
shoppingCartService.addItem(shoppingCart, item);
}
return ViewNamesHolder.SHOPPING_CART;
}
}
Whenever the shoppingCart object will not be found in the session, it would be initialized by its default constructor..
What do you think the reason is for that decision?
I can't speak directly for the Spring team, but your suggestion would limit the desired ModelAttribute to a newly created instance on each request (prior to being stored in the session,) but what if you wanted to start with a fully populated object, say, fetched from a datastore? Your method offers no way to do that. This, however, works well:
#ModelAttribute
public ShoppingCart createShoppingCart() {
...
return someShoppingCartRepo.find(...);
}
This, of course, is just one possible scenario where the usefulness of a separate method should be evident.
EDIT AFTER COMMENTS
You could easily create your own HandlerMethodArgumentResolver that would give you a new instance of your object if none existed, but it might be overkill considering how easy it is to use your createShoppingCart() method. If you are using xml configs, it would be something like this:
<mvc:annotation-driven ...>
<mvc:argument-resolvers>
<bean class="yourpackage.YourCustomArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
You could extend any number of existing HandlerMethodArgumentResolver base classes, or you could implement the interface directly yourself, but most likely you would use something like this:
public class YourCustomArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
// Implement/override methods for creating your model object when encountered as a method argument
}
To identify your argument, you could create a custom annotation:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface YourAutoCreateModelAttribute {
String value() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
Then annotate your method like this to kick off your custom resolver:
#RequestMapping(...)
public String doStuff(#YourAutoCreateModelAttribute ShoppingCart shoppingCart, ...) {
// Now your shoppingCart will be created auto-magically if it does not exist (as long as you wrote your resolver correctly, of course.
}

How can I validate a field as required depending on another field's value in SEAM?

I'm trying to create a simple custom validator for my project, and I can't seem to find a way of getting seam to validate things conditionally.
Here's what I've got:
A helper/backing bean (that is NOT an entity)
#RequiredIfSelected
public class AdSiteHelper {
private Date start;
private Date end;
private boolean selected;
/* getters and setters implied */
}
What I need is for "start" and "end" to be required if and only if selected is true.
I tried creating a custom validator at the TYPE target, but seam doesn't seem to want to pick it up and validate it. (Maybe because it's not an entity?)
here's the general idea of my custom annotation for starters:
#ValidatorClass(RequiredIfSelectedValidator.class)
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface RequiredIfSelected {
String message();
}
public class RequiredIfSelectedValidator implements Validator<RequiredIfSelected>, Serializable {
public boolean isValid(Object value) {
AdSiteHelper ash = (AdSiteHelper) value;
return !ash.isSelected() || (ash.getStart() && ash.getEnd());
}
public void initialize(RequiredIfSelected parameters) { }
}
I had a similar problem covered by this post. If your Bean holding these values is always the same then you could just load the current instance of it into your Validator with
//Assuming you have the #Name annotation populated on your Bean and a Scope of CONVERSATION or higher
AdSiteHelper helper = (AdSiteHelper)Component.getInstance("adSiteHelper");
Also as you're using Seam your validators don't need to be so complex. You don't need an interface and it can be as simple as
#Name("requiredIfSelectedValidator")
#Validator
public class RequiredIfSelectedValidator implements javax.faces.validator.Validator {
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
//do stuff
}
}

Categories