I am facing an issue with spring and it goes as follows:
In SessionAttributes I have an object person with an attribute addresses which is a list. Whenever person is updated via controler, previous entries still remain. So for example if I had in person addresses: old address 1, old address 2, old address 3 and I update person via form to have only one new address, list of addresses becomes: new address 1, old address 2, old address 3 while intended behavior is to have "new address 1" only. I couldn't seem to find a workaround this problem. I am using Spring 3.0.X.
Please find below all related code that shows the issue on hand.
Person.java:
package com.convert.dashboard.web.test;
import java.util.List;
public class Person {
private String name;
private Integer age;
private List<String> addresses;
public Person(List<String> addresses) {
this.addresses = addresses;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public List<String> getAddresses() {
return addresses;
}
public void setAddresses(List<String> addresses) {
this.addresses = addresses;
}
}
TestController.java
package com.convert.dashboard.web.test;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
#Controller
#RequestMapping("/test")
#SessionAttributes("person")
public class TestController {
#RequestMapping(value = "/")
public ModelAndView xyz() {
ModelAndView mav = new ModelAndView();
List<String> abc = new ArrayList<String>();
abc.add("old address1");
abc.add("old address2");
abc.add("old address3");
Person person = new Person(abc);
mav.addObject("person", person);
mav.setViewName("cForm");
return mav;
}
#RequestMapping("/save")
public #ResponseBody
String process(#ModelAttribute("person") Person person) {
return "<body>" + " Name:" + person.getName() + " Age: " + person.getAge() + " Addresses: " + person.getAddresses();
}
}
cForm.jsp:
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>populate form</title>
</head>
<body>
<form:form modelAttribute="person" action="/dashboard/test/save">
<form:hidden path="name" value="X" />
<form:hidden path="age" value="20" />
<form:hidden path="addresses[0]" value="New address" />
<input type="Submit" value="Submit" />
</form:form>
</body>
</html>
There are a couple of design issues I would like to address.
You are using a single object for both form binding and domain data. This tends to cause problems exactly like the one you've encountered here. The problem is not that the form fails to "clear" out the addresses of the session object; the problem is that the session object leaks its data structure to the form, which causes binding problems.
The form has knowledge about the contents of the Person object. Specifically, the form expects there to be three addresses in the person.getAddresses() list. Like (1) above, the problem is a leak of the domain structure into the view layer.
I recommend that you create two different "person" classes: one to represent the domain data (the session object), and one to exactly mirror the structure of the form (the form binding object). Your form will contain fields that map directly to properties in a PersonForm class, and in your TestController you can take the data from the PersonForm and update the session Person appropriately. Then the form inputs don't need to be designed to handle different states of the Person.addresses list.
This approach does require a bit more code, but not terribly much, and the savings in form complexity and form/domain decoupling is well worth it.
So the solution goes as follows:
By appending an AJAX call on remove on the client side and adding the code below to the controller.
#RequestMapping("/remeveAddress")
public #ResponseBody String removeElement(#ModelAttribute("person") Person person, #RequestParam Integer addressId, Model model){
person.getAddresses().remove(addressId);
model.addAttribute("person", person);
return "{}";
}
I was facing the same issue and solved it by introducing an init binder to the controller.
The init binder resets the list of addresses for the session attribute.
Depending on requirements it could do some more sophisticated job.
#InitBinder
public void initBinder(HttpSession session) {
Person person = (Person)session.getAttribute("person");
person.getAddresses().clear();
}
Hope that could help someone else :)
Recently I've faced with the same problem, and I've found a very simple solution.
So you need just to add a hidden field for your list.
Spring conversion service will set empty list from the blank string value of this hidden field :)
You can see the solution below:
<input type='hidden' name='addresses' value='' />
And after that put your other code:
<form:input path="addresses[0]" />
<form:input path="addresses[n]" />
Notes:
If you need to convert String to List of YourClass'es, and you haven't own convertors or editors for that.
So make sure that at least ObjectToObjectConverter can do it, YourClass must have constructor with a String arg or the static method valueOf(String).
Yet another possible solution is to use a handler interceptor as follows.
servlet.xml:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/test/**" />
<bean class="com.convert.dashboard.web.test.PersonInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
PersonInterceptor.java:
public class PersonInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
Person person = (Person)request.getSession().getAttribute("person");
if (person != null)
person.getAddresses().clear();
return super.preHandle(request, response, handler);
}
}
Related
I have a site built using Sitecore 7.5 and Webforms for Marketers 2.5. I am trying to create a custom email message processor pipeline command that will change the TO field for the email before it goes out. But the proper email address needs to come from a Session variable.
Here is my class:
public class CustomEmailMessageProcessor
{
public void Process(ProcessMessageArgs args)
{
//Change the TO address for the email based on the selection in the Subject field
var subjectField = args.Fields.GetEntryByName("Subject");
if (subjectField == null)
{
return;
}
//The value of the selected item will be the ID of a Subject Option
var selectedSubjectOptionItem = Sitecore.Context.Database.GetItem(new ID(subjectField.Value));
if (selectedSubjectOptionItem == null)
{
return;
}
var selectedSubjectOption = selectedSubjectOptionItem.GlassCast<Contact_Us_Subject_Option>();
//Based on the currently selected Region in the Session variable, get the proper
//child of selectedSubjectOption to populate the TO field
???
if (args.To.Length != 0)
{
args.To.Append(",");
}
args.To.Append(proper email address goes here);
}
}
And here is my associated config:
<processMessage>
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="ExpandLinks" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="ExpandTokens" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddHostToItemLink" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddHostToMediaItem" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="AddAttachments" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="BuildToFromRecipient" />
<!-- Custom setting -->
<processor type="myProject.CustomEmailMessageProcessor, myProject" method="Process" />
<processor type="Sitecore.Form.Core.Pipelines.ProcessMessage.ProcessMessage, Sitecore.Forms.Core" method="SendEmail" />
</processMessage>
The user will select a Subject from a dropdown list on the form. The value of each Subject will be the Guid of a corresponding item. That subject item will have children. Each child will have an email address and will correspond to a Region item in the system. I will then find the child item that matches the Region ID that is stored in Session. Then I will have the correct email address to send the email to.
However I have no idea how to access the Session variable from within the pipeline (or if it is even possible). It doesn't have to be Session. I am happy to pass in the currently selected Region in some other fashion. I just need some way to pass information in that can be accessed from the pipeline code.
Thanks,
Corey
When using the Sitecore WFFM webservice, the save action is run on the CMS, shell site. The save action don't know anything about the user session.
A different more standard solution is create a (custom) Field hidden, and set there the value you can use to find the needed email address.
Create a custom save action and do there your e-mail sending.
Note: If you do not have a good identifier to find the email address watch out with putting an email address or to simple identifier in a (hidden) form value, it can be abused.
Example of a hidden field, based on a <input type="text" .....> for Webforms
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sitecore.Form.Web.UI.Controls;
namespace StockpickSitecore.Controls.WFFM.CustomFields
{
public class HiddenEmailfield : SingleLineText
{
private static readonly string baseCssClassName = "scfSingleLineTextBorder";
private string CssClassName { get; set; }
public int MaxLength
{
get
{
return this.textbox.MaxLength;
}
set
{
this.textbox.MaxLength = value;
}
}
public int MinLength { get; set; }
public new string CssClass
{
get
{
return base.CssClass;
}
set
{
base.CssClass = value;
}
}
public HiddenEmailfield()
{
this.Text = "info#yourdomein.com";
}
protected override void OnInit(EventArgs e)
{
this.Attributes.Add("style", "display: none;");
this.textbox.CssClass = "scfSingleLineTextBox";
this.help.CssClass = "scfSingleLineTextUsefulInfo";
this.generalPanel.CssClass = "scfSingleLineGeneralPanel";
this.title.CssClass = "scfSingleLineTextLabel";
this.textbox.TextMode = TextBoxMode.SingleLine;
this.Controls.AddAt(0, (Control) this.generalPanel);
this.Controls.AddAt(0, (Control) this.title);
this.generalPanel.Controls.AddAt(0, (Control) this.help);
this.generalPanel.Controls.AddAt(0, (Control) this.textbox);
}
}
}
Change the constructor HiddenEmailfield and set the this.Text with the Session value.
Creat a item in Sitecore below /sitecore/system/Modules/Web Forms for Marketers/Settings/Field Types/Custom/
based on Template: /sitecore/templates/Web Forms for Marketers/Field Type
Fill in the Assembly : StockpickSitecore for Example
Fill in the Class: StockpickSitecore.Controls.WFFM.CustomFields.HiddenEmailfield
Now you can use HiddenEmailfield in you webform als field type
I am using the Jersey 2.1 API to return lists of JAXB annotationed objects.
I have a class Person
#XmlRootElement(name = "person")
public class Person { ...
In the Jersey API, when I return a List of Person and have the output set to xml, it creates a wrapper called <People> around my list:
<People>
<Person>
.. fields
</Person>
</People>
when I set the output to JSON format it does not add this extra People wrapper and I would like it to. I am using EclipseLink Moxy as the JSON provider. Is there a way to get the JSON output to look the same as the XML?
I came across a field for the Jersey 1.X API called FEATURE_XMLROOTELEMENT_PROCESSING that is supposed to enable this, but I don't know how to set this in 2.x. And the fact that it is doing it for XML output seems to indicate that it is already set. I just need to get the JSON to be the same!
Any help would be appreciated, thanks!
You could do the following:
Java Model
You could introduce a new class called People into your object model.
People
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="People")
#XmlAccessorType(XmlAccessType.FIELD)
public class People {
#XmlElementRef
private List<Person> person;
}
Person
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Person")
public class Person {
}
RESTful Service
Instead of:
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Person> read() {
You would do:
#GET
#Produces(MediaType.APPLICATION_JSON)
public People read() {
By default MOXy won't included the root element. When working with Jersey you can leverage the MoxyJsonConfig object (see: http://blog.bdoughan.com/2013/06/moxy-is-new-default-json-binding.html).
import javax.ws.rs.ext.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
#Provider
public class MOXyJsonContextResolver implements ContextResolver<MoxyJsonConfig> {
private final MoxyJsonConfig config;
public MOXyJsonContextResolver() {
config = new MoxyJsonConfig()
.setIncludeRoot(true);
}
#Override
public MoxyJsonConfig getContext(Class<?> objectType) {
return config;
}
}
You can also leverage MOXy's MOXyJsonProvider class to do the same configuration:
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
Is there a way to implement databinding similar to what we have in Flex?
I've noticed that in lesson 9 there is support for dataBinding using a simple template engine but once I update my model, those changes don't propagate to my view.
[Update]
This is my mediator
package mediators
{
import randori.behaviors.AbstractMediator;
import randori.behaviors.SimpleList;
import randori.jquery.Event;
import randori.jquery.JQuery;
/**
* Created by IntelliJ IDEA.
* User: jfernandes
* Date: 23-04-2013
* Time: 14:54
*/
public class IndexMediator extends AbstractMediator
{
[View]
public var names:SimpleList;
[View]
public var change:JQuery;
private var Mike : People = new People("Mike");
private var Roland : People = new People("Roland");
override protected function onRegister():void {
names.data = [Mike,Roland];
change.click(function(event:Event):void
{
this.Mike.name = "Mike Lambriola";
});
}
}
}
People class
package
{
/**
* Created by IntelliJ IDEA.
* User: jfernandes
* Date: 30-04-2013
* Time: 12:52
*/
public class People
{
public function People(name:String)
{
this.name = name;
}
public var name:String="";
}
}
Index body
<body class="simpleApp">
<ul id="names" class="simpleList">
<li id="template">{name}</li>
</ul>
<input id="change" value="Change values" type="button"/>
</body>
By clicking on the button I don't see Mike's name being updated to Mike Lambriola.
In the current release version we haven't included our observable pattern code (v.2.6). You will see this included in the coming releases and we expect full support by v.3.0. The reason for the delay is compatibility. We needed to implement something that can be wrapped to work equally well with SlickGrid's data model versus Kendo's, for example.
That said, understand that in most JavaScript components expose their own model for databinding. So, if you are working within a single set of components, for example Kendo, you can already use their observable objects and DataSources and all works.
So, the trick (and the hard part for randori) isn't creating an implementation. Its having one that is viable to use from AS but then shareable with other component sets should you be using multiple libraries.
Once this is released, the List, template and other randori behaviors will be updated to use it.
For now, there is very little penalty in reassigning the data to a behavior once it changed.
Mike
Trying to become a grails convert I have begun converting an existing application to Grails and Groovy. It works very well but I get stuck on the conversion of select tags.
I have a domain class:
package todo
class Person {
String ssn
String firstname
String familyname
String role
String emailname
String emailserver
...
When creating a new "todo" task an owner may be assigned from those persons in the system who are developers and I get this working (a fairly direct translation from PHP):
<select id="owner" name="owner">
<option>Noboby ...</option>
<g:each in="${Person.list()}">
<g:if test="${it?.role=='developer'}">
<option value="${it?.id}">${it?.firstname} ${it?.familyname}</option>
</g:if>
</g:each>
</select>
But every attempt to make it more "Grails-ish" fails. How can it be moulded into Grails v2.2.1 code? I spent hours reading, trying, failing.
If you woulkd like to make it more Grails style, you should perform all your logic within controllers \ services not in the view.
Assuming you have a view createTodo in the folder person and the PersonController, then modify your createTodo action like this:
class PersonController {
def createTodo() {
def developers = Person.findAllWhere(role: 'developer')
[developers: developers, ... /* your other values */]
}
}
So you don't need to handle with database operations in your view.
Next step is to use the g:select tag like this:
<g:select name="owner" from="${developers}" optionValue="${{'${it.firstName} ${it.familyName}'}}" noSelection="['null':'Nobody ...']" optionKey="id" value="${personInstance?.id}" />
Try this code:
<g:select optionKey="id" from="${Person.findAllByRole('developer')}" optionValue="${{it.fullName}}" value="${yourDomainInstance?.person?.id}" noSelection="['null':'Nobody']"></g:select>
And in your class:
class Person {
....
String getFullName(){
it?.firstname+' '+ it?.familyname
}
static transients = ['fullName']
....
}
See g:select tag for more details
Finally, I got it working as I want to and it works (almost) according to the #"Mr. Cat" solution. One little detail, though, 'it' does not exist in the class so the getFullName method had to become:
String getFullName(){
this?.firstname+' '+ this?.familyname
}
Up and working, thank you for all help.
I'm hoping to have a flexible way of marshalling objects. A verbose version for single objects and a less-verbose version for multiple object versions.
For example, consider my department model:
GET /locations/1:
<location id='1'>
<link rel="self" href="http://example.com/locations/1"/>
<link rel="parent" href="http://example.com/serviceareas/1"/>
<name>location 01</name>
<departments>
<department id='1'>
<link rel="self" href="http://example.com/departments/1"/>
<name>department 01</name>
</department>
<department id='2'>
<link rel="self" href="http://example.com/departments/2"/>
<name>department 02</name>
</department>
<department id='3'>
<link rel="self" href="http://example.com/departments/3"/>
<name>department 03</name>
</department>
</departments>
</location>
GET /department/1:
<department id='1'>
<link rel="self" href="http://example.com/departments/1"/>
<link rel="parent" href="http://example.com/locations/1"/>
<name>department 01</name>
<abbr>dept 01</abbr>
....
<specialty>critical care</specialty>
</department>
Is there a way to do this? Would I need to have separate entity objects? One that references the table for CRUD operations and another for lists?
Note: I'm the EclipseLink JAXB (MOXy) lead, and a member of the JAXB 2 (JSR-222) expert group.
Your question is tagged EclipseLink, if you are using EclipseLink JAXB (MOXy) you can take advantage of the external binding document to apply a second mapping to the Department class.
ContextResolver
In a JAX-RS environment you can leverage MOXy's external binding document through a ContextResolver:
import java.io.*;
import java.util.*;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
#Provider
#Produces({"application/xml", "application/json"})
public class DepartmentContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext jc;
public DepartmentContextResolver() {
try {
Map<String, Object> props = new HashMap<String, Object>(1);
props.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "example/bindings.xml");
jc = JAXBContext.newInstance(new Class[] {Department.class} , props);
} catch(JAXBException e) {
throw new RuntimeException(e);
}
}
public JAXBContext getContext(Class<?> clazz) {
if(Department.class == clazz) {
return jc;
}
return null;
}
}
For More Information
http://blog.bdoughan.com/2011/04/moxys-xml-metadata-in-jax-rs-service.html
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-35.html
External Binding Document
By default MOXy's external binding document is used to augment the annotated model, but if you set the xml-mapping-metadata-complete flag it will completely override the annotations allowing you to apply a completely different mapping:
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="example"
xml-mapping-metadata-complete="true">
...
</xml-bindings>
For More Information
http://blog.bdoughan.com/2011/09/mapping-objects-to-multiple-xml-schemas.html
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
UPDATE
This update is to address a number of questions you asked in one of your comments:
1 . Should/can each ContentResolver have its own binding file?
Yes each ContextResolver should have its own binding file. The main reason for introducing a new ContextResolver is to represent a secondary mapping.
2 . Can I have more than one for each ContentResolver (this would give me a number of renderings of the same class, creating a 'view' of sorts), perhaps specifying its location in the constructor?
For a single ContextResolver you can express the metadata across multiple binding files, but they will be combined into a single set of mappings. This means that a single ContentResolver cannot have multiple views of a single class. A separate ContextResolver is used to represent a secondary mapping.
3 . Where should the binding files reside?
I recommend loading the metadata file from the class path.
http://blog.bdoughan.com/2011/04/moxys-xml-metadata-in-jax-rs-service.html
4 . I can see how the ContentResolver could be easily specified in a Resource's GET method, but how would this be done if the object is embedded in another (JPA) object? In the embedded object's getter/setter?
Your JAX-RS implementation should pick up your ContextResolver because it is annotated with #Provider. The ContextResolver used for a class will depend on how you implement the getContext method:
public JAXBContext getContext(Class<?> clazz) {
if(Customer.class == clazz) {
return jc;
}
return null;
}
Here comes another idea. Could be a bad idea but somewhat easy.
class Department {
#XmlElement(required = true)
public Link getSelf() {
return self;
}
#XmlElement(required = false) // default
public Link getParent() {
if (verbose) {
return parent;
}
return null;
}
#XmlElement(required = false) // default
public String getSpecialty() {
if (verbose) {
return specialty;
}
return null;
}
#XmlTransient
private boolean verbose;
}