I have a List of objects with nested properties and at the bottom of the hierarchy each object has a CommonsMultipartFile property.
A Folder has a list of Requisites and each of those has a list of Pages
These are the bean definitions, each in its own file:
Page {
private CommonsMultipartFile attributes;
// Getter & Setter
}
Requisite {
private List<Page> pages;
// Other properties and Getters & Setters
}
Folder {
private List<Requisite> requisites;
// Getter & Setter
}
Then I add a Folder object to my modelMap inside a Controller method:
#RequestMapping(value = "loadFiles", method = RequestMethod.GET)
public String initFiles(ModelMap model, HttpServletRequest request) {
Folder folder = new Folder();
folder.setRequisites(requisitesModel.getRequisitesFromDB());
model.addAttribute("folder", folder);
return "loadFiles";
}
At this point the model attribute "folder" has a list of Requisite objects with various properties initialized, however pages (List<Page>) is null in all of them. This approach works fine and allows the user to load a bunch of files and the post request works as expected.
Then I added a method to handle a MaxUploadSizeExceededException and in the resolveException method I copied the behavior of the controller described above. This is to redirect the user to the same form when the total filesize exeeds a given threshold.
Here is the definition of the resolveException method:
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception exception) {
ModelMap model = new ModelMap();
Folder folder = new Folder();
folder.setRequisites(requisitesModel.getRequisitesFromDB());
model.addAttribute("error", "The files exceed the maximum filesize");
model.addAttribute("folder", folder);
return new ModelAndView("loadFiles", model);
}
The problem is that in this one the autoGrowNestedPaths does not work because immediately throws Invalid property 'requisites[0].pages[0]' of bean class [mypackage.Folder]: Cannot access indexed value of property referenced in indexed property path 'pages[0]': returned null.
My understanding is that spring by default autogrows nested paths for all Collection types, even without the use of LazyList or AutopopulatingList. Is my understanding wrong? Do I need to add something else?
I had and solved this problem myself, Jorge.
WebDataBinder does default to auto-grow nested paths; and that includes collections.
But the interesting thing, is that this depends on getting the 'generic type' of the Collection from the property getter method. It uses reflection -- calling Method.getGenericReturnType(), which returns a java.lang.reflect.Type.
If it works, you get a java.lang.reflect.ParameterizedType with the element-type of the collection, to grow; if it doesn't, Spring will get a 'null' element-type & won't auto-grow.
See org.springframework.beans.BeanWrapperImpl.growCollectionIfNecessary().
In my case, Hibernate proxy (subclasses) were found not to have the requisite generic type & method-signature info.. even though this info was on the entity classes (when used directly!)
I de-proxied the entity in my form controller "loadEntity" setup, and was right as rain. (De-proxying is useful & necessary in Hibernate apps sometimes, as are other proxy checks, comparisons, and manipulations.)
Code sample:
public static <T> T deproxy (T obj) {
if (obj == null)
return obj;
if (obj instanceof HibernateProxy) {
// Unwrap Proxy;
// -- loading, if necessary.
HibernateProxy proxy = (HibernateProxy) obj;
LazyInitializer li = proxy.getHibernateLazyInitializer();
return (T) li.getImplementation();
}
return obj;
}
public static boolean isProxy (Object obj) {
if (obj instanceof HibernateProxy)
return true;
return false;
}
public static boolean isSame (Object o1, Object o2) {
if (o1 == o2)
return true;
if (o1 == null || o2 == null)
return false;
Object d1 = deproxy(o1);
Object d2 = deproxy(o2);
if (d1 == d2)
return true;
return false;
}
public static Class getClassWithoutInitializingProxy (Object obj) {
if (obj instanceof HibernateProxy) {
HibernateProxy proxy = (HibernateProxy) obj;
LazyInitializer li = proxy.getHibernateLazyInitializer();
return li.getPersistentClass();
}
// Not a Proxy.
return obj.getClass();
}
Hope this helps guide you to your problem.. give me an upvote even!
From the BeanWrapper javadoc
setAutoGrowNestedPaths
void setAutoGrowNestedPaths(boolean
autoGrowNestedPaths)
Set whether this BeanWrapper should attempt to "auto-grow" a nested
path that contains a null value. If "true", a null path location will
be populated with a default object value and traversed instead of
resulting in a NullValueInNestedPathException. Turning this flag on
also enables auto-growth of collection elements when accessing an
out-of-bounds index.
Default is "false" on a plain BeanWrapper.
So no, BeanWrapperImpl does not autogrow nested path for lists by default (assuming you are using spring 3). Like you mentioned in your post, you can fix this by using an Autopopulating or Lazy list.
You can also use initbinder to exlicitely set autogrowNestedPaths property to true.
Related
I have an eventsourced aggregate and use Jackson as eventserializer. Now, when I apply an event A in a command handler, I can see it's event sourcing handler called immediately, with all the expected event fields (event is the same instance as I created in the command handler). One of the fields in the event is the aggregateId.
However, when the (read side) event handler is called, the event object is a different instance, but the field I filled with the aggregateId has a wrong value! Debugging shows it is filled with the event identifier. When I set a breakpoint in the event constructors, I see it called and a wrong field value being set.
When I switch to XStream as event serializer, everything is fine. No additional even instantion is done, and the event created in the command handler is the same as being processing in the eventhandler.
What is going on here?
After an hour of debugging, I found my own mistake ;-). As the Axon docs say, when using Jackson as EventSerializer, you have to stick to the Jackson conventions, which I didn't. All my aggregate id's are subclasses of this AggregateId:
public abstract class AggregateId {
private final UUID id;
public AggregateId() {
this(UUID.randomUUID());
}
public AggregateId(UUID id) {
this.id = id;
}
public String toString() {
return id.toString();
}
public UUID getValue() {
return id;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AggregateId that = (AggregateId) o;
return id.equals(that.id);
}
#Override
public int hashCode() {
return Objects.hash(id);
}
}
Having a private field id with no getter and a getValue that returns that id is the perfect way to fool Jackson (and myself).
After renaming id to value all tests are green.
We have an ASP.NET application. We cannot edit source code of controllers. But we can implement ActionFilter.
One of our controller action methods returns JSON. Is it possible to modify it in ActionFilter? We need to add one more property to a returned object.
Maybe, some other way to achieve it?
Found this interesting and as #Chris mentioned, though conceptually I knew this would work, I never tried this and hence thought of giving it a shot. I'm not sure whether this is an elegant/correct way of doing it, but this worked for me. (I'm trying to add Age property dynamically using ActionResult)
[PropertyInjector("Age", 12)]
public ActionResult Index()
{
return Json(new { Name = "Hello World" }, JsonRequestBehavior.AllowGet);
}
And the filter:
public class PropertyInjector : ActionFilterAttribute
{
string key;
object value;
public PropertyInjector(string key, object value)
{
this.key = key;
this.value = value;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var jsonData = ((JsonResult)filterContext.Result).Data;
JObject data = JObject.FromObject(jsonData);
data.Add(this.key,JToken.FromObject(this.value));
filterContext.Result = new ContentResult { Content = data.ToString(), ContentType = "application/json" };
base.OnActionExecuted(filterContext);
}
}
Update
If it's not dynamic data which is to be injected, then remove filter constructor and hard code key & value directly and then the filter could be registered globally without editing the controller
GlobalFilters.Filters.Add(new PropertyInjector());
As I read explanation here, I found that Spring can automatically bind GET request parameter to a type. Below is the sample code from the link.
#Controller
#RequestMapping("/person")
public class PersonController {
...
#RequestMapping("/create")
public String create(Person p) {
//TODO: add Person to DAO
return "person/show";
}
}
Can someone tell me how spring do this? What bean that contains the logic to convert the parameter onto command type (Person type)?
The trick is done here: org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument()
This is the excerpt of code where it actually binds the class to the values:
String name = ModelFactory.getNameForParameter(parameter);
//Here it determines the type of the parameter and creates an instance
Object attribute = (mavContainer.containsAttribute(name)) ?
mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
//Then it binds the parameters from the servlet to the previously created instance
WebDataBinder binder = binderFactory.createBinder(request, attribute, name);
if (binder.getTarget() != null) {
bindRequestParameters(binder, request);
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors()) {
if (isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
}
I have two objects:-
LabTest
LabTestDetails
Where a LabTest object can have zero or many LabTestDetails objects.
I need to implement the following business rule:-
The user should not be able to edit a LabTest object if it has been assigned to one or more LabTestDetails objects.
Currently i have implemented a helper method named IsAlreadyAssigned on the LabTest object (to check if the LabTest object has been assigned to any LabTestDetails object):-
public partial class LabTest
{
public bool IsAlreadyAssigned(int id)
{
return (LabTestDetailss.Any(r2 => r2.LabTestID == id));
}}
Then i have added the following checks on the Get & Post Edit action methods:-
public ActionResult Edit(int id)
{
LabTest c = repository.GetLabTest (id);
if ((c == null) || (c.IsAlreadyAssigned (id)))
{
return View("Error");
}
return View(c);
}
[HttpPost]
public ActionResult Edit(int id, FormCollection colletion)
{
LabTest c = repository.GetLabTest (id);
if ((c == null) || (c.IsAlreadyAssigned (id))) // *******
{
return View("Error");
}
try
{
if (TryUpdateModel(c))
{
elearningrepository.Save();
return RedirectToAction("Details", new { id = c.LabTestID });
}
}
The above might work fine on most of the cases, but if the LabTest object were just assigned to a labTestDetails object by another user after the if ((c == null) || (c.IsAlreadyAssigned (id))) check on the post action method i mark it as(*) on the above code , then my business logic will be broken.
so is there a way to implement my action methods so that it will always prevent editing a LabTest object if it has been assigned to a LabTestdetail object .
BR
You could use a stored procedure, as suggested in the comments, but you could also create a service method that checks whether or not a LabTest is assigned, like
public bool LabTestIsAssigned(int labTestId)
{
using (var context = new YourContext())
{
return context.LabTestDetails.Any(d => d.LabTestID == id);
}
}
The advantage of using this method, rather than the navigation property, is that it is guaranteed to reflect the current state of the database.
Note that you'll have to do this check just before saving changes as well! Even then, an insert may occur right after evaluating the check and just before saving the changes.
Subject says it all: I'm creating a secure and generic wrapper to access a WCF service transparently.
A little background:
what I have done is that I've created similar business classes definition both on server and client. The server-side contains the actual logic while the client side contains only method definitions. Additionally, the client-side classes derive from a SecureFactory which implements a Call method. The body of every method of each derived class contains only a call to Call method. This Call method is responsible for invoking the server service passing such things as to the type of business class and which of its method to invoke to perform the requested operation.
This approach is being designed in order to simplify security checks by restricting passing of security information to only between SecureFactory and Server service. There are tuns of other benefits which you most of already aware of.
Now here's the issue: I'm stuck at as to how to return objects (especially arrays of objects) from Server to Call method?
The server may return a single business object (DataContract applied) as well as list of such objects. Since it's a generic approach, I have only Object to be used as return type. Following is the Call method
public object Call(params object[] parameters)
{
var mb = (new StackFrame(1).GetMethod());
using (Proxy.ServerClient server = new Security.BO.Proxy.ServerClient())
{
try
{
if (((MethodInfo)mb).ReturnType.IsGenericType)
{
var response = server.InvokeForList(SecurityManager.Current.SID, SecurityManager.Current.Token, mb.DeclaringType.ToString(), mb.Name, parameters);
return response.Result.ToList();
}
else
{
var response = server.Invoke(SecurityManager.Current.SID, SecurityManager.Current.Token, mb.DeclaringType.ToString(), mb.Name, parameters);
return response.Result;
}
}
catch (Exception ex)
{
System.Diagnostics.Debugger.Break();
}
}
return null;
}
server methods:
public CollectionResponse InvokeForList(string SID, string token, string type, string method, object[] parameters)
{
// Validation here
var t = assemblyBO.GetType(type, true);
object BO = Activator.CreateInstance(t);
var mi = t.GetMethod(method);
if (mi == null)
throw new MethodNotImplementedException("Method " + method + " could not be found on type " + t.ToString());
object res = mi.Invoke(BO, parameters);
// Convert list to t[]
object list = res.GetType().GetMethod("ToArray").Invoke(res, new object[0]);
// Error here! cannot convert t[] into object[]
return new CollectionResponse((object[])list);
}
The other method Invoke(...) is similar accept it returns Response object instead of CollectionResponse.
Here's the CollectionResponse class: (Response is similar:just it takes only one object)
[DataContract]
public class CollectionResponse
{
[DataMember]
private Object[] _result;
public Object[] Result
{
get
{
return _result;
}
}
public CollectionResponse(Object[] result)
{
this._result = result;
}
}
Initially I was thinking to have only one Invoke for both lists and singleton – but failed with "Connection was closed unexpectedly." still I'm not able to achieve – how can I convert T[] into object[].
Do you have any suggestion to improve it, or any other way of achieving the same?
Thanks
I can see an immediate problem here. You are using reflection which is far less perfromant than the direct call.
For me, that is enough not to follow this route.