I'm studying how interceptors works in java. I'm using Netbeans IDE and just created a new project called Interceptors.
I created an annotation called "logged"
#Inherited
#InterceptorBinding
#Retention(RUNTIME)
#Target({METHOD, TYPE})
public #interface Logged { }
Then I created a class "LoggedInterceptor"
#Interceptor
public class LoggedInterceptor implements Serializable {
public LoggedInterceptor() {}
#AroundInvoke
public Object logMethodEntry(InvocationContext invocationContext) throws Exception
{
System.out.println("Entering method: "
+ invocationContext.getMethod().getName() + " in class "
+ invocationContext.getMethod().getDeclaringClass().getName());
return invocationContext.proceed();
}
}
Then I just created a class that make use of Logged annotation
public class SuperService
{
#Logged
public String deliverService(String uid)
{
return uid;
}
public static void main(String[] args)
{
SuperService ss = new SuperService();
System.out.println(ss.deliverService("sisi"));
}
}
Nothing happened. Later I added under src/main/resources/META-INF/ an xml file called beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1"
bean-discovery-mode="all">
<interceptors>
<class>ascompany.interceptors.LoggedInterceptor</class>
</interceptors>
</beans>
But logMethodEntry method doesn't get called when I call deliverService method. Am I missing some other configuration file? Or just something else?
I already tried to add #Priority annotation to LoggedInterceptor but nothing changed...
EDIT:
I added logget annotation to LoggedInterceptor as #Luciano van der Veekens said but nothing changed
You forgot to annotate the LoggedInterceptor class with #Logged. This actually binds the annotation to the interceptor.
#Logged
#Interceptor
public class LoggedInterceptor implements Serializable {
}
They do the same in one of the Java EE 6 tutorials.
Also it looks like SuperService is just a regular class. An interceptor is a Java EE concept and only works for EJBs deployed on an application server.
Related
I am in the process of refactoring an old module, by adding CDI.
I end with
public interface ApiFactory {
...
}
public class ApiFactorySp
implements ApiFactory {
#Inject
UrlProducer urlProducer; // <-- Does not get injected
...
}
and
public interface UrlProducer {
public String getUrl();
}
#Alternative
public class UrlProducerTest
implements UrlProducer {
#Override
public String getUrl() {
return "https://myTestEnv.mydomain/myWebApp";
}
}
For testing, I create a beans.xml file in META-INF:
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
<alternatives>
<class>myOrg.myProject.myPackage.UrlProducerTest</class>
</alternatives>
</beans>
To test it, I am doing like shown in this blog
public class WeldContext {
public static final WeldContext INSTANCE = new WeldContext();
private final Weld weld;
private final WeldContainer container;
private WeldContext() {
this.weld = new Weld();
this.container = weld.initialize();
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
weld.shutdown();
}
});
}
public <T> T getBean(Class<T> type) {
return container.instance().select(type).get();
}
}
and
public class WeldJUnit4Runner extends BlockJUnit4ClassRunner {
public WeldJUnit4Runner(Class<Object> clazz) throws InitializationError {
super(clazz);
}
#Override
protected Object createTest() {
final Class<?> test = getTestClass().getJavaClass();
return WeldContext.INSTANCE.getBean(test);
}
}
Now, when I try to test the logic, I do
#RunWith(WeldJUnit4Runner.class)
public class MyTest {
#Inject
UrlProducer urlProducer;
#Inject
ApiFactory apiFactory;
#Test
public void test() {
apiFactory.doSomethingThatRequiresUrlProducer();
}
}
When I run this, both of the test attributes are inject, but I get NPE because the urlProducer attribute inside of the apiFactory instance has not been assigned a value.
Why is Weld not recognizing the #Inject attribute inside ApiFactory?
JDK 7, Weld 2.2.10, Junit 4.12
UPDATE: After posting the question, started trying with a simpler, brand new project (with just two interfaces and three classes). Using Weld "standalone" did not solve the issue, using CDI-Unit did solve it.
Then I modified my original project to use CDI-Unit, but it did not improve anything. After that I change the injection of UrlProducerTest in ApiFactory from field to constructor (i.e., defining the #Inject ApiFactory(UrlProducer urlProducer) constructor) solved it. I still have not tried this solution with "standalone" Weld (that is for tomorrow), but nonetheless I am still interested in know why field injection is not working.
If UrlProducerTest is an alternative and you want to inject this bean this class should be added to beans.xml into <alternatives> tag.
EDIT:
I believe if some bean can't be injected you get exception with 'unsatisfied/ambiguous dependencies' message. Null could be injected if you used CDI producer method that returned null but this is not your scenario.
So if there are no errors in console I have two assumptions:
Injection doesn't work at all and you get NPE because apiFactory is null
You use urlProducer before injection. For example, from constructor or initialization block (apiFactory.doSomethingThatRequiresUrlProducer() is not provided). So move this logic to some method and annotate it by #PostConstruct
Because ApiFactorySp isn't a CDI bean. You need to annotate the class with #Named to identify the class as a CDI bean for CDI to perform dependency injection.
I am learning Spring framework (more generally Java EE).
I like the feature of passing the configuration using xml files. I started by the this example and it worked fine.
The only problem is that once I add my custom xml configuration with beans to set the attribute value inside the controller it doesn't work anymore, in the server log file it says Caused by: java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'com.example.controller.FirstController#0' bean method (...) then it lists all the methods in the controller exactly like if I defined multiple methods with identical RequestMapping (which is not the case).
I wanted to set a single attribute, but it seems that because of that the entire autoconfiguration doesn't work anymore.
Before
Main class
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Controller class
#RestController
#RequestMapping("first")
public class FirstController {
protected final Logger log = LoggerFactory.getLogger(getClass());
#RequestMapping("test")
public String test() {
log.info("Test");
return "OK";
}
}
After
Main class
#Configuration
#ComponentScan
#EnableAutoConfiguration
#ImportResource("classpath:config.xml")
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
Controller class
#RestController
#RequestMapping("first")
public class FirstController {
protected final Logger log = LoggerFactory.getLogger(getClass());
private String testingbean;
public void setTestingbean(String testingbean) {
this.testingbean = testingbean;
}
#RequestMapping("test")
public String test() {
log.info("Test");
return "OK";
}
#RequestMapping("beantest")
public String testBeans() {
return testingbean;
}
}
Config.xml 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.xsd">
<!-- test bean -->
<bean class="com.example.controller.FirstController">
<property name="testingbean" value="works"/>
</bean>
</beans>
In the Before version after accessing /first/test it returned OK, now I get blank page and Ambiguous mapping found error in the log file.
Could someone explain to me how to mix Spring Boot autoconfiguration with custom defined beans?
I would also recommend to use property files to externalize configuration if possible.
EDIT: Spring boot provides fine documentation on that topic.
The problem can be the combination of XML configuration and default component scan - the same bean can be defined twice. If this is the case consider "manual" exclude via http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html#excludeFilters--
My Aspect class will be ,
#Configuration
#EnableAspectJAutoProxy
#Component
#Aspect
public class AspectClass {
#Before("execution(* com.pointel.aop.test1.AopTest.beforeAspect())")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before running the beforeAspect() in the AopTest.java class!");
System.out.println("Hijacked Method name : " + joinPoint.getSignature().getName());
System.out.println("************************");
}
}
My other java Class
public class AopTest {
public void beforeAspect() {
System.out.println("This is beforeAspect() !");
}
}
My Main Class is
public class MainMethod {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("ApplicationContext/applicationContext.xml");
AopTest test = (AopTest)context.getBean("bean1");
test.beforeAspect();
}
}
My applicationContext.xml is ,
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<bean id="bean1" class="com.pointel.aop.test1.AopTest" />
</beans>
In this the #Before("execution(* com.pointel.aop.test1.AopTest.beforeAspect())") in the AspectClass will not be executed before the beforeAspect() in the AopTest , when running Main method.
Good answers are definitely appreciated.
First of all if you're going to use an annotation based configuration, use AnnotationConfigApplicationContext instead of FileSystemXmlApplicationContext. And get rid of the applicationContext.xml file and simply add a #Bean method in your configuration class. Something like this:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = "your.aspect.package")
public class AspectConfig {
#Bean
public AopTest aopTest() {
return new AopTest();
}
}
In your main
public class MainMethod {
public static void main(String[] args) {
AnnotationConfigApplicationContextcontext = new AnnotationConfigApplicationContext(AspectConfig.class);
// don't forget to refresh
context.refresh();
AopTest test = (AopTest)context.getBean("aopTest");
test.beforeAspect();
}
}
In AspectClass you should have #Component, #Aspect, and your method should have the advice or pointcut annotation like #Before. It needs to be a #Component, so that Spring knows to scan it.
Here some code need to add in xml to use annotations-
1.for #component annotation.
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
2.after that use component scan to get all annotated bean class which use #component annotation,and use aop autoproxy-
<context:annotation-config/>
<context:component-scan base-package="mypackage"></context:component-scan>
<aop:aspectj-autoproxy>
</aop:aspectj-autoproxy>
for examples visit-www.technicaltoday.com/p/spring.html
You are missing the point cut definition in your aspect class.
For example;
#Pointcut("execution(* *.advice(..))")
public void logBefore(){}
#Before("logBefore()")
public void beforeAdvicing(){
System.out.println("Listen Up!!!!");
}
You first have to defin the point to weave your aspect to. You do this by using Point cuts.It is the point cut name you give within your #Before annotation. Have a look at my blog post for more information # http://dinukaroshan.blogspot.com/2010/06/aop-with-spring.html
I don't see your AspectClass in the beans configuration. You should also declare it as a Bean.
I'm using Java EE 6 & Jboss AS7.1 and try to use interceptor binding (Example from jboss site).
I have an InterceptorBinding annotation:
#InterceptorBinding
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface GeoRestrictedEquipment {
}
The interceptor:
#GeoRestrictedEquipment
#Interceptor
public class GeoRestrictedEquipmentInterceptor {
#EJB EquipmentDao equipmenttDao;
#EJB SecurityService securityService;
#AroundInvoke
public Object checker(InvocationContext ctx) throws Exception {
Integer id = (Integer) ctx.getParameters()[0];
Equipment equipment = equipmenttDao.findById(id);
GeoChecker.check(equipment.getSite(), securityService.getUser());
return ctx.proceed();
}
}
And a bean:
#Stateless
#LocalBean
#SecurityDomain(Realm.NAME)
#RolesAllowed({ Roles.REGISTERED })
public class PumpService implements PumpServiceLocal {
#Override
#GeoRestrictedEquipment
public PumpInfos getPumpInfos(Integer pumpId) {
/* ... */
}
}
But the interceptor is not called... What do I miss from the example ?
The interceptor is called when I write this:
#Override
#Interceptors({GeoRestrictedEquipmentInterceptor.class})
public PumpInfos getPumpInfos(Integer pumpId) {
/* ... */
}
Thanks for your help.
According to the documentation there is another way rather than using beans.xml:
You do not need to specify the interceptor in the beans.xml file when
you use the #Priority annotation.
#Logged
#Interceptor
#Priority(Interceptor.Priority.APPLICATION)
public class LoggedInterceptor implements Serializable { ... }
And it works.
Did you enable your interceptor as described in the referenced example?
By default, a bean archive has no enabled interceptors bound via
interceptor bindings. An interceptor must be explicitly enabled by
listing its class under the element of the beans.xml
file of the bean archive.
You can use any priority value = Priority.Application is 2000 by default.
For example =
#Interceptor
#Loggable
#Priority(100)
public class FileLogger {}
Priority type:
PLATFORM_BEFORE [0-999] - interceptors start at the first. They are started by the platform.
LIBRARY_BEFORE [1000-1999] - by the libraries.
APPLICATION [2000-2999]- by the application
LIBRARY_AFTER,PLATFORM_AFTER [3000-4000]
You manage primary loading for interceptors.
I am trying to get Aspect working with Spring 3 and annotations.
#Aspect
public class AttributeAspect {
#Pointcut("#annotation(com.mak.selective.annotation.Attribute)")
public void process(){
System.out.println("Inside Process ....");
}
#Around("process()")
public void processAttribute(){
System.out.println("Inside Actual Aspect ..");
}
}
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:p="http://www.springframework.org/schema/p"
xmlns:util="http://www.springframework.org/schema/util"
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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<aop:aspectj-autoproxy proxy-target-class="false" />
<context:component-scan base-package="com.mak.selective.annotation.*" />
<bean name="attribute" class="com.mak.selective.annotation.AttributeAspect"/>
</beans>
MY Test to test the Aspect:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/springcontext/*.xml")
public class AttributeTest {
#Attribute(tableName = "firstTable", columnName = "New Column")
private void getAttribute() {
System.out.println("Inside Attribute call...");
}
#Test
public void testAttributeAspect() {
getAttribute();
}
}
With this code i can only see "Inside Attribute call..." but nothing from Aspect.
Please guide.
Got this working by making a new Object (Component) and injected to the Junit test class.
Good to see that you got it working from XML, but you could have also done it from annotations.
The issue is that the #Aspect annotation is not a Spring stereotype, so the scanner is not registering the aspect as a Spring Bean. Just add either #Service or #Component above or below #Aspect and it will be registered.
Also, either directly name the bean (e.g., #Service("myNamedService")) or have it implement an interface (e.g., public class AttributeAspect implements IAspect {), as per standard Spring design.
You need to use real AspectJ if you want to intercept invocations of methods within the same bean form where it is invoked. (What you have done, should work if the method testAttributeAspect() is located in an other bean.)
How to do real AspectJ?
Using the AspectJ compiler and weaver enables use of the full AspectJ language, and is discussed in Section 7.8, “Using AspectJ with Spring applications”.
#See Spring Reference
A few things:
Firstly, when you do around advice you need to write the advice method like this:
#Around(...)
public void aroundAdviceMethod(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("before...");
pjp.proceed();
}
finally {
System.out.println("After...");
}
}
But also (and this at least applies when you're using proxies, not entirely sure in your case), the method you're putting advice on needs to be public (yours isn't), spring managed (via #Component or otherwise) and called external from the class so the proxy can take effect (also not the case in your example). So you need something like this:
#Component
public class SomeClass {
#Attribute
public void someMethodCall() {
System.out.println("In method call");
}
}
public class SomeUnitTest {
#Autowired SomeClass someClass;
#Test
public void testAspect() {
someClass.someMethodCall();
}
}