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/>
Related
I have the following bean:
package com.test;
#Component
public class Sample{
String modified = null;
#Value("${url}")
private String url;
public Sample(){
System.out.println(url );
if(baseUrl.equals(""){
throw new RuntimeException("missing");
}
else{
modified = "test"+url;
}
}
}
I have added:
<context:annotation-config />
<context:property-placeholder location="classpath:test.properties"/> & <context:component-scan base-package="com.test"/>
and trying to access above "modified" field as below
<bean id="url" class="java.lang.String">
<constructor-arg value="#{sample.modified}" />
</bean>
in my application context. But I keep getting the following error:
Field or property 'sample' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'
Not sure why i get this error?
When Spring creates the object it uses the default constructor. It can't set the property until after it constructs it. Instead of what you have, try this to see if the value is being set.
#PostConstruct
public void init(){
System.out.println(url );
if(baseUrl.equals(""){
throw new RuntimeException("missing");
}
}
JustinKSU's answer is right. You have another option: inject value via constructor using #Autowired:
#Component
public class Sample {
#Autowired
public Sample(#Value("${url}") String url) {
System.out.println(url);
if(url.equals("") {
throw new RuntimeException("missing");
}
}
}
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 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);
}
}
...
}
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" );
}
}