Spring MVC - bind enum array - spring-mvc

If I post in the format weather=sunny, Spring MVC happily converts this to an Weather enum instance using the enum with name=sunny.
However if I post weather=sunny&weather=windy, then Spring is not able to convert it into an instance of Weather[]. The error I get is:
Failed to convert property value of type 'java.lang.String[]' to required type 'com.blah.Weather[]' for property 'weather'
How can I achieve this?

You can use Converters to perform the custom conversion. For your example, you would need to do something like:
public class WeatherConverter implements Converter<String[], Weather[]> {
#Override
public Weather[] convert(String[] source) {
if(source == null || source.length == 0) {
return new Weather[0];
}
Weather[] weathers = new Weather[source.length];
int i = 0;
for(String name : source) {
weathers[i++] = Weather.valueOf(name);
}
return weathers;
}
}
You can use Converters anywhere you might want type-conversions. Now, what you need to do is register it:
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="package.path.WeatherConverter"/>
</list>
</property>
</bean>
And it is done.
You can see more details in the Spring Reference.
You could also look into PropertyEditors, with #InitBinder, and, probably, #ControllerAdvice if you want. However, Converters are much easier to use (IMO).

Related

Spring MVC #RequestParam - empty List vs null

By default Spring MVC assumes #RequestParam to be required. Consider this method (in Kotlin):
fun myMethod(#RequestParam list: List<String>) { ... }
When passing empty list from javaScript, we would call something like:
$.post("myMethod", {list: []}, ...)
In this case however, as the list is empty, there is no way to serialize empty list, so the parameter essentially disappears and so the condition on required parameter is not satisfied. One is forced to use the required: false on the #RequestParam annotation. That is not nice, because we will never receive the empty list, but null.
Is there a way to force Spring MVC always assume empty lists in such case instead of being null?
To get Spring to give you an empty list instead of null, you set the default value to be an empty string:
#RequestParam(required = false, defaultValue = "")
This can be managed in the serialization with ObjectMapper. If you are using jackson in your spring MVC, you can do either the following.
1) Configure your object mapper:
objectMapper.configure(SerializationConfig.Feature.WRITE_EMPTY_JSON_ARRAYS, false);
2) Or if you are using beans via xml config:
<bean name="objectMapper" class="org.springframework.http.converter.json.JacksonObjectMapperFactoryBean" autowire="no">
<property name="featuresToDisable">
<list>
<value type="org.codehaus.jackson.map.SerializationConfig.Feature">WRITE_EMPTY_JSON_ARRAYS</value>
</list>
</property>
</bean>
You can try a WebDataBinder in your controller.
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(List.class, "list", new CustomCollectionEditor( List.class, true));
}
Tried this?
fun myMethod(#RequestParam list: List<String> = listOf()) { ... }

Update Document Template Dynamically in Alfresco from property page

I have one document template as like attached screen shot. now my requirement is i want to dynamically update some values of that document when i will upload it in alfresco site. i will either create that Document through node template or upload. I want to provide those values from document property page.
can anyone help me how can i achieve this.
I tried it using java script, but i am getting some problem. so i am thinking for using Alfresco CMIS API. But i am quite new in alfresco cmis. so can any one tell the better way which i can use for this task.
Thanks in advance.
I think you should trigger an custom action by a Rule
When someone update a node (for example by editing properties), your action will be triggered and you will be able to :
get the content of your node
get the properties of your node
use the apache poi library provided by alfresco (my guess is that you use word documents) to find your parameters, and the replace them by the value you find in the property nodes.
Here is an excellent tutorial to learn how to create a custom action.
Note : You may have to keep the original templated document if you want to change a second time your properties (by using versionning for example).
Edit (see discussion below) :
I assume you know how to use rules in alfresco : http://docs.alfresco.com/5.0/tasks/library-folder-rules-define.html
Declare a new action to assign to a rule. This action must be triggered when the excel is dropped or updated
Create an action implementing your need :
public class MyAction extends ActionExecuterAbstractBase {
...
#Override
protected void executeImpl(final Action arg0, final NodeRef arg1) {
// your code here
}
....
}
This action will :
Take the nodeRef parameter (which is the excel file) and load the file
...
Inputstream inputstream = getFileFolderService().getReader(arg1).getContentInputStream();
...
NPOIFSFileSystem fs = null;
HSSFWorkbook workbook = null;
try {
fs = new NPOIFSFileSystem(inputstream);
workbook = new HSSFWorkbook(fs.getRoot(), true);
//get your data with POI
For each row of your excel :
Make a copy of your template :
find it :
getSearchService().query(searchParameters)
copy it :
getFileFolderService().copy(sourceNodeRef, targetParentRef, newName)
Do transformations of your new word content (Find occurrences and replace with poi library).
Update the content of your new file :
ContentWriter writerDoc = getContentService().getWriter(document.getNodeRef(), ContentModel.PROP_CONTENT,
true);
writerDoc.putContent(file/inputStream);
In your context file, declare you action :
<bean id="my-action" class="x.y.z.MyAction" parent="action-executer">
<property name="searchService">
<ref bean="searchService" />
</property>
<property name="nodeService">
<ref bean="nodeService" />
</property>
<property name="contentService">
<ref bean="contentService" />
</property>
<property name="fileFolderService">
<ref bean="FileFolderService" />
</property>
....
</bean>
In Share, assign to a folder a rule with the action you have created.

HTML escape with Spring MVC and Jackson Mapper

I am going to escape HTML in Spring MVC with Jackson Mapper to avoid XSS attack. I search for escaping with Jackson alone and how to config Jackson in Spring. I tried to export json with text like "<" ">", I expected it to escape them to < and >. For example, I added some text enclosed with "bold tag" <b>, I expected to see plain bold tag text in the front end html but it ended up so that the text is shown in bold style in the front end html page.
Below is my approach. I don't know why it didn't work out.
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
this.getJsonFactory().setCharacterEscapes(new CustomCharacterEscapes());
}
}
public class CustomCharacterEscapes extends CharacterEscapes {
private final int[] asciiEscapes;
public CustomCharacterEscapes() {
int[] esc = CharacterEscapes.standardAsciiEscapesForJSON();
esc['<'] = CharacterEscapes.ESCAPE_STANDARD;
esc['>'] = CharacterEscapes.ESCAPE_STANDARD;
esc['&'] = CharacterEscapes.ESCAPE_STANDARD;
esc['\''] = CharacterEscapes.ESCAPE_STANDARD;
asciiEscapes = esc;
}
#Override
public int[] getEscapeCodesForAscii() {
return asciiEscapes;
}
#Override
public SerializableString getEscapeSequence(int ch) {
return null;
}
}
Here is the Spring Bean configuration:
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<array>
<bean id="jsonConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper">
<bean class="x.y.z.CustomObjectMapper" />
</property>
</bean>
</array>
</property>
</bean>
I have never tried to write my own HttpMessageConverter, but I did find this posting that seems pretty relevant to what you want to do. In looking at their solution vs. what you posted here, I can say the biggest differences I noticed was that you did not seem to implement/override the following:
protected boolean supports(Class<?> clazz), which indicates which class type you are supporting (I would recon in your case this would be Object or Serializable if you want it to be generic enough to handle every possibility, or some class specific to your domain objects)
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage), looks like it's used for the request-side
protected void writeInternal(Object t, HttpOutputMessage outputMessage), which looks like it's used for the response-side
Another approach might be to simple create a custom Jackson serializer in conjunction with #ResponseBody. Or, better yet, if you have a value that is user-driven, and your storing it in a database, escape the values prior to insertion. That way you don't need to do anything at all, and the value(s) in question would be "safe" from end-to-end. If you wanted to get crazy-fancy, you could write a custom java.beans.PropertyEditor that escapes Strings for HTML and plug that into the mix using the InitBinder.
Finally, I would like to recommend that, instead of trying to replace the characters on your own, you use something like Apache Commons-Lang's StringEscapeUtils to escape the values.

Spring MVC is dropping a #PathVariable

If I hit the controller multiple times and hammer it, occasionally my modelCode parameter comes through as null. However the URL has the modelCode in it.
Using Spring Framework 3.0.5.RELEASE
#RequestMapping(value="ws/getallvariants/{channelCode}/{modelCode}/{regionId}/{year}")
public ModelAndView getAllVariants(#PathVariable("channelCode") String channelCode,
#PathVariable("modelCode") String modelCode,#PathVariable("regionId") String regionId,#PathVariable("year") String year){
if (modelCode == null)
{
int i = 0; // this should never hit, but does.
}
Yes, RegEx was the most reliable for me as well. Did this to grab an email address as a parameter:
#RequestMapping(value = "rest/userreadserv/search/{email:.+}", method = RequestMethod.GET)
public ResponseEntity getUserAccountByEmail(#PathVariable String email) {...}
Take a look at Spring MVC #PathVariable getting truncated . The regex approach worked for me:
#RequestMapping({ "/servers/{serverName:.+}" })
Updating the spring framework again to the latest version appears to have worked. Apparently when we updated our framework something didn't work correctly.
EDIT:
So this wasn't our issue at all... We had implemented a String Trimmer Editor incorrectly and it was keeping state. So when we put the call under load it would cross results.
//in your xml dispatcher add this property to your default annotation mapper bean as follow
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true"></property>
</bean>

When I use a dynamic class for my WebService arguments, the public properties are not included

I have a dynamic Class that is a Value Object that is used to pass arguments to a WebService. It has two public properties:
package
{
[Bindable]
public dynamic class WebServiceCriteria
{
public var property1:String;
public var property2:String;
}
}
I set these two properties in one part of my application:
var myCriteria:WebServiceCriteria = new WebServiceCriteria();
myCriteria.property1 = "x";
myCriteria.property2 = "y";
Then I added other - dynamic - properties at another point in my application:
myCriteria.property3 = "z";
However, when I pass the instance to the WebService as the arguments, the original two public properties are not sent (as I can see in Fiddler), even though they have values. But, I can see them as properties of my Class instance in the debugger just prior to the send().
operation.arguments = {args: myCriteria};
operation.send(); // only property3 is sent
Why are those two properties not sent?
Here is an example of the SOAP request sent to the WebService:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<intf:webservice_controller xmlns:intf="http://childDir.parentDir">
<args xsi:type="apachesoap:Map" xmlns:apachesoap="http://xml.apache.org/xml-soap">
<item>
<key xsi:type="xsd:string">property1</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">property2</key>
<value xsi:type="xsd:string"></value>
</item>
<item>
<key xsi:type="xsd:string">property3</key>
<value xsi:type="xsd:string">z</value>
</item>
</args>
</intf:webservice_controller>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This behavior is documented in Flex 3.0 manuals. See Dynamic Classes for more information. A direct quote:
[...]Methods created in this way, however, do not have access to any private properties or methods of the [example] class. Moreover, even references to public properties or methods of the [example] class must be qualified with either the this keyword or the class name.
Try to add this to your constructor :
package
{
[Bindable]
public dynamic class WebServiceCriteria
{
public var property1:String;
public var property2:String;
function WebServiceCriteria()
{
prototype.property1 = null;
prototype.property2 = null;
}
}
}
... As it seems like only the Objects properties are enumerable
I don't believe you will be able to send an object to a webservice. If you want to send an object you will need to use remote object. To use a webservice you would need to make your object into some kind of xml or soap request. like
var myCriteria:WebServiceCriteria = new WebServiceCriteria();
myCriteria.property1 = "x";
myCriteria.property2 = "y";
operation.send(<request>
<property1>{myCriteria.property1}</property1>
<property2>{myCriteria.property2}</property2>
</request>);
http://livedocs.adobe.com/flex/3/html/help.html?content=data_intro_2.html

Resources