I am developing RESTful API for my application. All getters (that use HTTP GET) work fine. I cannot make save method (that uses POST) to work.
I am using HTML form and RESTClient for testing.
Here is my Controller
#Controller
public class EntitiesController {
#RequestMapping(value="/ci/save/", method = RequestMethod.POST)
public ModelAndView saveConfigurationItem(#RequestBody ConfigurationItem body) {
System.out.println("saveConfigurationItem: body=" + body);
return createModelAndView("ci", Collections.emptyList());
}
}
This method is expected to be called when client posts ConfigurationItem.
I am using custom serialization format. It is not XML or JSON. It is VCard or VCalendar format. For my first test I used the following VCard:
BEGIN:VCARD
N:Pooh;Winnie
FN:Winnie the Pooh
TEL:tel:+441234567
END:VCARD
I posted it to URL http://localhost:8080/core.solution-1.0/data/ci/save/.
Here is the response I get:
415
The server refused this request because the request entity is in a format not
supported by the requested resource for the requested method ()
(*) ConfigurationItem is an abstract class. CardEntry extends it. I tried both.
I tried to change the method parameter to String. In this case the method is called but the string is empty. The same happens when following one of recommendations I saw in web I changed the parameter type to MultiValueMap and sent request from simple HTML form.
I saw that marshal() is not called at all.
What's wrong?
Here is what I have. (I put here relevant code only.)
Spring configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">
<import resource="classes/spring-config-prod.xml"/>
<context:component-scan base-package="com.mycompany.solution.service" />
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean id="ciCardView" class="com.mycompany.solution.service.VFormatView">
<constructor-arg>
<bean class="com.mycompany.solution.service.VFormatMarshaller">
<property name="packagesToScan" value="com.mycompany.solution.entity"/>
</bean>
</constructor-arg>
</bean>
</beans>
Marshaller
public class VFormatMarshaller implements Marshaller, Unmarshaller {
#Override
public void marshal(Object obj, Result result)
throws IOException/*, XmlMappingException*/ {
System.out.println("VFormatMarshaller.marshal(" + obj + ")");
marshalStreamResult(obj, (StreamResult)result);
}
#Override
public boolean supports(Class<?> paramClass) {
System.out.println("VFormatMarshaller.supports(" + paramClass + ")");
boolean supports = new HashSet<String>(Arrays.asList(packagesToScan)).contains(paramClass.getPackage().getName());
if (supports) {
return supports;
}
return Collection.class.isAssignableFrom(paramClass);
}
#Override
public Object unmarshal(Source source) throws IOException/*, XmlMappingException*/ {
System.out.println("VFormatMarshaller.unmarshal(" + source + ")");
return unmarshalStreamSource((StreamSource)source);
}
//// .............................
}
View (this is written only to override the content type)
public class VFormatView extends MarshallingView {
public VFormatView() {
super();
setContentType("application/vcard");
System.out.println("VFormatView()");
}
public VFormatView(Marshaller marshaller) {
super(marshaller);
setContentType("application/vcard");
System.out.println("VFormatView(" + marshaller + ")");
}
}
#RequestBody/#ResponseBody are supported by an hierarchy of HttpMessageConverters, that is completely different from ViewResolvers.
In you case you need to configure a MarshallingHttpMessageConverter with appropriate marshaller/unmarshaller and content type (or create your own HttpMessageConverter if you don't need to depend on the existing implementation of marshaller/unmarshaller), and supply a configured instance to AnnotationMethodHandlerAdapter.
The least intrusive way to configure a custom HttpMessageConveter is to create a BeanPostProcessor as follows:
public class Configurer implements BeanPostProcessor {
public void postProcessAfterInitialization(String name, Object bean) {
if (bean instanceof AnnotationMethodHandlerAdapter) {
AnnotationMethodHandlerAdapter a = (AnnotationMethodHandlerAdapter) bean;
HttpMessageConverter[] convs = a.getMessageConverters();
... add new converter ...
a.setMessageConverters(convs);
}
}
...
}
Related
I have some error during the invocation of a bean inside an Aspect, and I also would like to have some suggestions about initialization of beans inside an aspect class.
I am trying to invoke a bean [2] inside my aspects class [1], but in the end I get the following error [4]. It seems that the #Autowired parameter didn't initialize the bean. My configuration file is in [3]. What am I doing wrong?
I want that the bean will be invoked one time during initialization. So, even the map function is called multiple times, the bean is already initialized.
Another solution is, I want (I don't know if it is possible), that the bean will be initialized when the pointcut map or reduce is invoked by the first time.
Are these requests possible to do?
Thanks,
[1] My aspect class
#Aspect
#Configurable
#Component
public class MapReduceAspects {
#Autowired
MedusaDigests digests;
#Before("execution(* map(..))")
public void mymap(JoinPoint joinPoint) {
System.out.println("My Map Execution: " + joinPoint.getArgs() + ":" + joinPoint.getTarget());
Object[] obj = joinPoint.getArgs();
if (obj.length > 0) {
byte[] key = convertToBytes(obj[0]);
byte[] value = convertToBytes(obj[1]);
digests.updateDigest(key, value);
}
}
#Before("execution(* reduce(..))")
public void myreduce(JoinPoint joinPoint) { System.out.println("My Reduce Execution");
Object[] obj = joinPoint.getArgs();
if (obj.length > 0) {
byte[] key = convertToBytes(obj[0]);
byte[] value = convertToBytes(obj[1]);
digests.updateDigest(key, value);
}
}
}
[1] My aspect
#Component
public class MedusaDigests {
private final String MD5 = "MD5";
private MessageDigest mda = null;
public MedusaDigests() {
try {
mda = MessageDigest.getInstance(MD5);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void updateDigest(byte[] key, byte[] value) {
mda.update(key);
mda.update(value);
}
}
[3] My beans-aspects.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--<aop:include name="mapreduceAspect"/>-->
<!--<aop:include name="jobClient"/>-->
<!--</aop:aspectj-autoproxy>-->
<!--<context:load-time-weaver/>-->
<context:component-scan base-package="org.apache.hadoop.mapred"/>
<bean id="mapreduceAspect" class="org.apache.hadoop.mapred.aspects.MapReduceAspects" factory-method="aspectOf" autowire="byType" />
<bean id="medusaDigests" class="org.apache.hadoop.mapred.MedusaDigests"/>
[4] the error that I get
WARNING: job_local138234703_0001
java.lang.Exception: java.lang.NullPointerException at org.apache.hadoop.mapred.LocalJobRunner$Job.runTasks(LocalJobRunner.java:462)
at org.apache.hadoop.mapred.LocalJobRunner$Job.run(LocalJobRunner.java:522)
Caused by: java.lang.NullPointerException
at org.apache.hadoop.mapred.aspects.MapReduceAspects.mymap(MapReduceAspects.java:50)
at org.apache.hadoop.mapred.examples.DummyWordCount$Map.map(DummyWordCount.java:32)
at org.apache.hadoop.mapred.examples.DummyWordCount$Map.map(DummyWordCount.java:1)
at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:146)
at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:787)
at org.apache.hadoop.mapred.MapTask.run(MapTask.java:341)
at org.apache.hadoop.mapred.LocalJobRunner$Job$MapTaskRunnable.run(LocalJobRunner.java:243)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
In the first you don't need declare your beans at the xml configuration and using annotation like this:
<bean id="medusaDigests" class="org.apache.hadoop.mapred.MedusaDigests"/>
#Component
public class MedusaDigests {...}
You must declare bean only in one place, for example you can remove tags:
<bean id="mapreduceAspect" class="org.apache.hadoop.mapred.aspects.MapReduceAspects" factory-method="aspectOf" autowire="byType" />
<bean id="medusaDigests" class="org.apache.hadoop.mapred.MedusaDigests"/>
Secondly, tag
<context:component-scan base-package="org.apache.hadoop.mapred"/>
does not scan Autowired annotation, if also you want scan #Autowired annotation you can choose one of two variants:
declare in xml configuration
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
or
declare in xml configuration
<context:annotation-config/>
I have the next simple application(class Message has only one method which prints incoming message and has no interest for the question):
package messager.spring;
public class User {
private Messenger misiger;
private String name;
public User(String name) {
this.name = name;
}
public void setMessenger(Messenger messinger) {
this.misiger = messinger;
}
public void send(String mess) {
String message = name + " sent message " + "'" + mess + "'";
misiger.send(message);
}
// public String getname() {
// return name;
// }
}
Main class:
package messager.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
user.send("testing3...");
}
}
Spring configuration file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="user" class="messager.spring.User" autowire="byName">
<constructor-arg type="java.lang.String" value="Vova2"/>
</bean>
<bean id="messenger" class="messager.spring.MobileMessenger"/>
</beans>
I used autowiring for class User autowiring Messanger class. According to documentation :
When ByName is used, it then tries to match and wire its properties with the beans defined by the same names in the configuration file. If matches are found, it will inject those beans otherwise, it will throw exceptions.
This configuration works but I dont understand why((( I dont have property with name messenger inside User class((( I changed it on purpose to misiger. And it still works. It seems that bean id directly depends not on property name, but on setter name!!! Is it so?
YES, you are right. As described here :
Spring will lowercase the first letter after “set” in the method name and use the rest of the method name as-is for deducing the property name.
So not the member variable but the setter defines the property name.
That's how JavaBeans work, by looking at naming conventions.
The underlying reference isn't necessarily relevant.
The name of your property is messenger because there's a getter called that.
I have a simple example program here:
package com.test;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class Main {
public static void main(String[] args) throws InterruptedException {
try {
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("TestBeanFile.xml");
Object o = context.getBean("AInstance");
}
catch (Throwable e) {
e.printStackTrace();
}
Thread.sleep(Long.MAX_VALUE);
}
private static class A implements InitializingBean {
private B myB;
public A() {
System.out.println("A constructor");
}
public void setB(B aB) {
myB = aB;
}
#Override
public void afterPropertiesSet() throws Exception {
System.out.println("A aps");
}
}
private static class B implements Runnable, InitializingBean {
private C myC;
private volatile boolean exit = false;
public B() {
System.out.println("B constructor");
}
public void setC(C aC) {
myC = aC;
}
#Override
public void afterPropertiesSet() throws Exception {
System.out.println("B aps");
if (myC == null) throw new IllegalArgumentException("C cannot be null");
new Thread(this).start();
}
public void exit() {
exit = true;
}
#Override
public void run() {
while (!exit) {
System.out.println(myC.getValue());
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private static class C implements InitializingBean {
private String value = "C's value";
public C() {
System.out.println("C constructor");
}
public String getValue() {
return value;
}
#Override
public void afterPropertiesSet() throws Exception {
System.out.println("C aps");
}
}
}
And here is a simple XML to bean load them which intentionally has an incorrect fully qualified name to the A class. This should mean that C B and A dont get constructed.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
xmlns:int-ip="http://www.springframework.org/schema/integration/ip"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream-2.1.xsd
http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip-2.1.xsd">
<bean id="CInstance" class = "com.test.Main.C" />
<bean id="BInstance" class = "com.test.Main.B">
<property name="C" ref="CInstance" />
</bean>
<bean id="AInstance" class = "com.test.Main.A1">
<property name="B" ref="BInstance"/>
</bean>
</beans>
When you run this application using the above XML C and B DO get constructed but A doesnt. It will produce the following output (omitting the stack trace when the exception is thrown, but I assure you A does not get constructed):
C constructor
C aps
B constructor
B aps
C's value
C's value
C's value
....... // one a second
If you modify the XML to look like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-stream="http://www.springframework.org/schema/integration/stream"
xmlns:int-ip="http://www.springframework.org/schema/integration/ip"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream-2.1.xsd
http://www.springframework.org/schema/integration/ip http://www.springframework.org/schema/integration/ip/spring-integration-ip-2.1.xsd">
<bean id="AInstance" class = "com.test.Main.A1">
<property name="B" ref="BInstance"/>
</bean>
<bean id="BInstance" class = "com.test.Main.B">
<property name="C" ref="CInstance" />
</bean>
<bean id="CInstance" class = "com.test.Main.C" />
</beans>
The only output you get is the stack trace, and the B and C objects dont get constructed at all.
This seems like its a bug in spring. I dont understand why the marshaling of the objects, and the constructors / afterpropertiesSet methods which are run depend on the order of their appearance in the XML. What this means for me, is that when I have classes which construct threads in their constructors / after properties set methods, if I'm not careful about how I order the elements in the XML I can end up leaking resources if my only reference to them is, in this example, the A class. Because it fails construction in both cases.
So, is this a bug, or is this some feature that I dont understand? If this is a feature, what reason would they have to make the order of marshaling dependent on the XML file, instead of the object graph of the bean definitions?
Spring first reads the <bean> and other elements in the XML configuration and creates appropriate BeanDefinition objects defining them.
It then has to initialize them. To do this, it has to decide on an order. The order of initialization is undefined, unless a bean depends on another or there is a mix of #Order, implementations of Ordered and a few others (depends on the case).
In your first example, CInstance is initialized first. Then BInstance is initialized. Part of its initialization involves invoking its afterPropertiesSet() method which launches a non-daemon thread. Then Spring attempts to initialize AInstance and fails.
In your second example, Spring attempts to initialize AInstance first. It fails immediately, with no change to start a second thread.
Note that in a declaration like
<bean id="AInstance" class="com.example.Spring.A1">
<property name="B" ref="BInstance" />
</bean>
although AInstance depends on BInstance, the actual instance needs to be initialized before any property setters can be invoked to assign BInstance to B. So Spring begins the initialization of AInstance by instantiating the class. If it fails, the entirety of context refresh fails. If it passes, it will then initialize the BInstance bean and use it to set the B property.
I'm new to spring.
I've a rulefactory, which would return an instance from a static method
based on the type value
Now i'll get the type from the main methods, argument.
Now i would like to pass the argument type to the factory method getInstance
type argument.
how to do that.
/* Factory class, getInstance will return a subtype of RuleEvaluation, for simplicity, i've not
provided the Implementation class for SingleRuleEvaluation and MassRuleEvaluation. Basically both the classes implements RuleEvaluation */
public class RuleEvalFactory {
public static RuleEvaluation getInstance(String type) {
if (type != null && type.equals("Single")) {
return new SingleRuleEvaluation();
} else if (type != null && type.equals("mass")) {
return new MassRuleEvaluation();
}
return null;
}
}
/* My Main class , i need to get an instance of the RuleEvaluation here based on the type(dyamic)
dont know how to do it.
*/
public class MyApp {
public static void main(String args[]) {
ApplicationContext context =
new ClassPathXmlApplicationContext("Spring-All-Module.xml");
String type = args[0];
/* i want to pass the above type String to the factory method and get the instance how to do that */
RuleEvaluation re = (HarmonyService) context.getBean("rulefactory") ;
}
}
/* my Spring xml configuration file */
Spring xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="instanceMethodFactory" class="test.factory.RuleEvalFactory"> </bean>
<!-- i dont know how to pass the dynamic type from the Myapp main
method into this constructory argument -->
<bean id="rulefactory" factory-bean="instanceMethodFactory" factory-method="getInstance">
<constructor-arg index="0"> </constructor-arg>
</bean>
</beans>
Please give the code in Spring xml and Myapp main method how to inject the type into the factory method's getInstance.
Regards,
Raghu
You need to specify in the bean the constructor argument,
<bean id="myBean" class="A" scope="prototype">
<constructor-arg value="0"/> <!-- dummy value -->
</bean>
And then pass the value to bean factory,
getBean("myBean", argument);
I'm new to Spring, and I'm curious as to how to approach validating form input against a data source. I'm using Spring 3.0.3, by the way. So lets say I have the following form:
public class MyForm {
private String format;
public String getFormat() {
return format;
}
public void setFormat( value:String ) {
format = value;
}
}
Now lets say the format property is a string representation of a file format: "jpg", "png", "gif", "bmp", etc. Naturally I thought to write a validator for the form. It looks something like this:
public class MyFormvalidator implements Validator
{
#Override
public boolean supports( Class<?> clazz ) {
return MyForm.class.equals( clazz );
}
#Override
public void validate( Object obj, Errors errors ) {
MyForm myForm = (MyForm) obj;
ValidationUtils.rejectIfEmptyOrWhitespace( errors, "format", "error.required" );
if( !myForm.getFormat().equals( "jpg" ) &&
!myForm.getFormat().equals( "png" ) &&
.... etc .... ) {
errors.rejectValue( "format", "error.invalid" );
}
}
}
So thats all fine and dandy, but lets say I want to add new supported formats without having to adjust my validator, recompile, and deploy it. Rather than get into database 'n what not, lets keep it simple go with a little XML file that could be modified. It would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<fileformats>
<item>jpg</item>
<item>png</item>
.... etc ....
</fileformats>
And finally, the real point of all this...what should my approach be to making this data available during the validation of the form input?
The easiest way to do this is to make the Validator a Spring bean, and put the list of formats in your Spring configuration:
public class MyFormvalidator implements Validator
{
private List<String> fileFormats;
public MyFormValidator(List<String> fileFormats) {
this.fileFormats = fileFormats;
}
... //the rest of your validator, including checks for fileFormats.contains(myForm.getFormat())
Now, in your Spring configuration, you have
<bean name="myFormValidator" class="something.something.MyFormValidator">
<constructor-arg>
<list>
<value>jpg</value>
<value>png</value>
... etc ...
</list>
</constructor-arg>
</bean>
Do you consider changes to the application context source changes? You can create a list in your bean definition and inject it right into the validator.
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd">
<bean id="myValidator"
class="MyValidator>
<property name="acceptedExtensions"/>
<util:set>
<value>jpg</value>
<value>png</value>
</util:set>
</property>
</bean>
</beans>
public class MyFormvalidator
implements Validator {
private Set<String> acceptedExtensions;
public void setAcceptExtensions(Set<String> extensions) {//etc}
#Override
public void validate( Object obj, Errors errors ) {
MyForm myForm = (MyForm) obj;
ValidationUtils.rejectIfEmptyOrWhitespace(
errors, "format", "error.required" );
if( !acceptedExtensions.contains(myForm.getFormat){
errors.rejectValue( "format", "error.invalid" );
}
}