I'm currently working on an OSGi project.
Without many experiences in AOP combined with OSGi, I would like to know how to best do AOP in an OSGi environment?
We have implemented the AOP scenario to create a console that intercept the call to a bundle in order to store the elapsed time for each task started by this bundle.
Today, this aspect has been deployed on a jboss container using the LoadTimeWeaver provided by aspectj (adding an agent to the jboss start script in order to instrument the jars in the container -javaagent:%APP_HOME%\application\lib\aspectjweaver-1.6.11.jar).
I've read some articles about this problem, but did not find a solution that suits well for me. There is, for example, an Equinox Incubator project for AspectJ. But since I'm using Apache Felix and Bnd(tools) I want to avoid using something from Equinox. One requirement for the weaving process will be that it should be at load-time as well (a bundle for aspectj that instrument the method inside another bundle).
Someone can share experiences with such a use case using AOP aspectj with OSGI Felix ?
Here is a working example of minimal
felix aspectj setup
The basic pattern is:
register weaving hook on activation
public class Activator implements BundleActivator {
public void start(BundleContext context) throws Exception {
AspectWeaver weaver = new AspectWeaver();
servList.add(context.registerService(WeavingHook.class, weaver, null));
}
}
inject weaving definition context:
public class AspectContext extends DefaultWeavingContext {
#Override
public List<Definition> getDefinitions(final ClassLoader loader, final WeavingAdaptor adaptor) {
if (definitionList == null) {
definitionList = AspectSupport.definitionList(loader, rootConfig);
}
return definitionList;
}
}
provide weaving hook implementation
public class AspectWeaver implements WeavingHook {
#Override
public void weave(WovenClass woven) {
String name = woven.getClassName();
BundleWiring wiring = woven.getBundleWiring();
ClassLoaderWeavingAdaptor adaptor = ensureAdaptor(wiring);
final byte[] source = woven.getBytes();
final byte[] target;
// aspectj is single-threaded
synchronized (adaptor) {
target = adaptor.weaveClass(name, source);
}
woven.setBytes(target);
}
Related
I am trying to create a lightweight web service around a legacy java library to expose it as a web service using Spring Boot. I am new to Spring, while I have a lot of java experiance writing libraries all my web service experiance is in ASP.NET.
I can instantiate an instance of my library object but I can't figure out how to then have that object be injected into my controllers via #Autowired when the application is spun up.
This is my main application:
#SpringBootApplication
public class ResolverWebServiceApplication {
private static ArgumentParser newArgumentParser() {
ArgumentParser parser = ArgumentParsers.newFor("Resolver").build();
// configuring the parser
return parser;
}
public static void main(String[] args) throws ArgumentParserException {
ArgumentParser parser = newArgumentParser();
Namespace ns = parser.parseArgs(args);
ResolverOptions options = new ResolverOptions.Builder(ns)
.build();
ResolverContext context = new ResolverContext(options);
// ^^^ I need to get this injected into my controllers ^^^
SpringApplication.run(ResolverWebServiceApplication.class, args);
}
}
And then a simple controller which needs the class injected:
#RestController
public class VersionController {
#Autowired
private ResolverContext context; // And here the instance needs to be injected.
#GetMapping(path = "/version", produces = MediaType.APPLICATION_JSON_VALUE)
public long version() {
return context.getResolver().getVersionAsLong();
}
}
I could make the context a singleton which the controllers just refer to but I want to be able to test my controllers by mocking the context. There is also obviously a lot of validation and error handeling that needs to be added.
I can't have it be a Bean since I only want to instantiate one for my entire application.
The closest question I have found is this one: Registering an instance as 'singleton' bean at application startup. But I can't put the options in the configuration files. The application might be spun up in a container or on a users machine and requires the ability to accept arguments to initialize the library class. It would be a real usability degradation if someone had to manually edit the application config for these options.
You need to tell spring to consider the required classes from your lib when initializing the application context i.e Configure and let spring know how to create a bean and then let spring handle dependency injection for you.
First of all, add required jar that you have in your build file, say pom.xml for maven, in your current project. Idea is to have it on your classpath when you build the project.
As you said it is legacy lib and I am assuming it is not a spring bean, then
In your configuration class, return it as a bean, using #Bean annotaion.
#Configuration
public class YourConfigurationClass {
#Bean
SomeBean returnSomeBeanFromLegacyLib() {
return new SomeClassFromLib();
}
Once you return this bean from your config, it should be available to Spring Context for dependency injection whereever you #Autowire the required dependency.
|--Integration tests
|--Spring boot rest application
I have two modules,
Spring boot application is where I have the end points,
it runs on its own embedded tomcat, I want to be able to run it as a part of Integration test's maven build and run integration tests on it.
My question is, is there a way to run spring boot application from a different module via maven?
On Spring boot's website I can only see an example of running a spring-boot application through its own pom by using spring-boot-maven-plugin, but not by running the application as a part of different module by specifiying a jar file with in the execution.
Yes, there are several ways to do what you ask, for example:
use the #SpringBootTest annotation on your test classes (since Spring Boot 1.4);
programmatically start the Spring Boot application from within your test.
The first is my favorite one and the simpler one as well but it only works in the context of unit tests, of course. Here's an example.
Let's assume that you have a class named Application annotated with #SpringBootApplication in your REST module. You can test the endpoints by just defining a test like this inside your Integration test module:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class, properties = {"my.overriden.property=true"} )
public class RestEndpointTest
{
// ...
}
By doing so, the entire context of the application will start. You can then further configure your test depending on your needs, also overriding some properties (see my.overridden.property).
Alternatively, you can define your own configuration inside the test module, referencing any required class from the other module, for example:
#Configuration
#ComponentScan(basePackageClasses = {BaseClass.class})
#EnableJpaRepositories
#EntityScan
#EnableAutoConfiguration
public class SupportConfiguration
{
#Bean
public ARequiredBean bean()
{
return new ARequiredBean();
}
// etc...
}
and the using it just like you would do with any other context:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SupportConfiguration.class)
public class CustomTest
{
// ...
}
The other method would be to programmatically start an instance of your REST application, with something like this:
public static void main(String[] args) throws IOException
{
try (ConfigurableApplicationContext context = SpringApplication.run(Application.class, args))
{
log.info("Server Started. Press <Enter> to shutdown...");
context.registerShutdownHook();
BufferedReader inReader = new BufferedReader(new InputStreamReader(System.in));
inReader.readLine();
log.info("Closing application context...");
context.stop();
}
log.info("Context closed, shutting down. Bye.");
System.exit(0);
}
I am trying to implement AspectJ logging to module to analyze it better. My application is already using log4J framework.
If I run AspectJ class in a separate application it works fine but it doesn't work when I integrate it with application. Here what I have done
applicationContext.xml
<aop:aspectj-autoproxy/>
<bean id="logAspectj" class="com.test.aspect.LoggingAspect"/>
LoggingAspect class
#Component
#Aspect
public class LoggingAspect {
private final Log log = LogFactory.getLog(this.getClass());
#Around("execution(public void com.db.TestMarshaller.saveDoc(..))")
public Object logTimeMethod(ProceedingJoinPoint joinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object retVal = joinPoint.proceed();
stopWatch.stop();
StringBuffer logMessage = new StringBuffer();
logMessage.append(joinPoint.getTarget().getClass().getName());
logMessage.append(".");
log.info(logMessage.toString());
return retVal;
}
}
Note: Here TestMarshaller is not exposed via Spring.
Is there some specific AspectJ setting for Log4j?
since your com.db.TestMarshaller is not "spring managed" (there is no Spring bean of that type in your context), the used spring namespace will NOT create a proxy which calls your Aspect.
In this case you'll either have to compile your sources with the AspectJ Compiler (Compile Time Weaving) or use the aspectj runtime to modify your bytecode when your class is loaded (Load Time Weaving).
It really depends on your use case which way you go - if your Aspect is part of your business logic and can not change during runtime, use the aspectj compiler, since it will only be done once. I case you need a more dynamic application of your aspect, use load time weaving instead.
Hope this helps,
Jochen
I have a web application with spring 3.0. I need to run a class with main method from a cron that uses beans defined in appcontext xml(using component scan annocations). I have my main class in same src directory.
How can I inject beans from web context into main method. I tried to do it using
ApplicationContext context = new ClassPathXmlApplicationContext("appservlet.xml");
I tried to use AutoWired and it returns a null bean. So I used Application ctx and this is creating a new context (as expected) when I run main method. But is it possible that I can use existing beans from container.
#Autowired
static DAO dao;
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("xman- servlet.xml");
TableClient client = context.getBean(TableClient.class);
client.start(context);
}
You can not inject a Spring bean into any object that was not created by spring. Another way to say that is: Spring can only inject into objects that it manages.
Since you are creating the context, you will need to call getBean for your DAO object.
Check out Spring Batch it may be useful to you.
You may use a spring context for your main application, and reuse the same beans as the webapp. You could even reuse some Spring XML configuration files, provided they don't define beans which only make sense in a webapp context (request-scope, web controllers, etc.).
But you'll get different instances, since you'll have two JVMs running. If you really want to reuse the same bean instances, then your main class should remotely call some method of a bean in your webapp, using a web service, or HttpInvoker.
Try with this Main:
public class Main {
public static void main(String[] args) {
Main p = new Main();
p.start(args);
}
#Autowired
private MyBean myBean;
private void start(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:/META-INF/spring/applicationContext*.xml");
System.out.println("The method of my Bean: " + myBean.getStr());
}
}
And this Bean:
#Service
public class MyBean {
public String getStr() {
return "mybean!";
}
}
In order to address this issue, I have created https://jira.springsource.org/browse/SPR-9044. If you like the proposed approach, please vote for it.
Spring boot provides an official solution for this. Download a skeleton from
https://start.spring.io/
and make sure packaging in the pom.xml is set to jar. As long as you don't include any web dependency the application will remain a console app.
Trying to design simple aspect,that will print word "logg" to console,when any of public methods executed.
aspect:
#Aspect
public class LoggingAspect {
#Pointcut("execution(public * *(..))")
public void publicServices() {
};
#Before("publicServices()")
public void logg() {
System.out.println("logg");
}
}
xml config:
<context:component-scan base-package="aspectlogging" />
<aop:aspectj-autoproxy/>
<bean id="loggingAspectHolder" class="aspectlogging.LoggingAspect"/>
simple bean:
package aspectlogging;
#Component
public class TestableBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
test:
public class TestLogging {
public static void main(String[] args) {
TestableBean tb = new TestableBean();
tb.setName("yes");
tb.getName();
}
}
I expect,that result of running of TestLogging will be "logg" word in console,and no output returned.
Do I understand AOP correctly in this case?
With #Around advice, you need to have a ProceedingJoinPoint pjp argument to the advising method and to call pjp.proceed() at the point in the advisor when you want the wrapped method to be called. It's easier to use #Before advice really, when what you've done will otherwise work just fine.
[EDIT]: Also, you must let Spring construct your beans for you instead of directly calling new. This is because the bean object is actually a proxy for your real object (which sits inside it). Because your target object doesn't implement an interface, you will need to have the cglib library on your classpath in addition to the Spring libraries. (Alternatively, you can go with using AspectJ fully, but that requires using a different compiler configuration.)
To create your beans, you first need to create a Spring context and then query that for the bean instance. This means you change from:
TestableBean tb = new TestableBean();
To (assuming you're using Spring 3, and that your XML config is in "config.xml" somewhere on your classpath):
ApplicationContext context = new ClassPathXmlApplicationContext("config.xml");
TestableBean tb = context.getBean(TestableBean.class);
The rest of your code remains the same (after adjusting for import statements and possibly additional dependencies).
Not quite sure on this one, but maybe you need to use a spring managed TestableBean to have spring AOP pick up the method call.
edit: of course, you can't use #Around the way that you provided - but this subject has been addressed by another answer, so it's omitted here.
edit2: If you need help on how to get a spring managed bean, please feel free to ask. but since you already got your aspect bean set up, I believe you can handle this :)
edit3: Hehe. Ok.. maybe not :)
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
will load your application context.
Load beans from there by calling:
TestableBean testableBean = (TestableBean )ctx.getBean("testableBean ");
Define the TestableBean just like you did with your Aspect bean.
edit4: Now I'm pretty sure that the fault is the non-spring managed bean.
Use the simplest thing that can work. Spring AOP is simpler than using full AspectJ as there is no requirement to introduce the AspectJ compiler / weaver into your development and build processes. If you only need to advise the execution of operations on Spring beans, then Spring AOP is the right choice. If you need to advise domain objects, or any other object not managed by the Spring container, then you will need to use AspectJ.
Taken from: http://static.springsource.org/spring/docs/2.0.x/reference/aop.html