Aspectj: intercept method from external jar - aspectj

I am using a X.jar and adding to my AspectJ project(in eclipse). I have written pointcut and advice for a method myMethod() inside X.jar.
But aspectj is not intercepting this method call.
How can I tell aspectj to intercept method calls on external jars.Or is it not possible?
Thanks

There are two options:
a) compile the aspects into the JAR
b) use load time weaving (I'd go with that one)
Both of these are advanced topics, I'd suggest you read AspectJ in Action (2nd Ed) by Ramnivas Laddad to learn more.
To clarify: there are different types of pointcuts. If your code calls the library's methods, you can of course intercept these calls, as they happen in your code. So call() pointcuts will work, but execute() (and many other) pointcuts won't because they change the executing method, which is not in your code base. So you have to either change the byte code of the library (option a) or change how it is loaded into your application (option b).

Here is a simple example with AspectJ Load-Time Weaving on GitHub https://github.com/medvedev1088/aspectj-ltw-example
It uses Joda Time library to demonstrate how to intercept the DateTime#toString() method invocations.
The aspect:
#Aspect
public class DateTimeToStringAspect {
public static final String TO_STRING_RESULT = "test";
#Pointcut("execution(* org.joda.time.base.AbstractDateTime.toString())")
public void dateTimeToString() {
}
#Around("dateTimeToString()")
public Object toLowerCase(ProceedingJoinPoint joinPoint) throws Throwable {
Object ignoredToStringResult = joinPoint.proceed();
System.out.println("DateTime#toString() has been invoked: " + ignoredToStringResult);
return TO_STRING_RESULT;
}
}
aop.xml
<aspectj>
<aspects>
<!-- Aspects -->
<aspect name="com.example.aspectj.DateTimeToStringAspect"/>
</aspects>
<weaver options="-verbose -showWeaveInfo">
<include within="org.joda.time.base.AbstractDateTime"/>
</weaver>
</aspectj>
test:
public class DateTimeToStringAspectTest {
#Test
public void testDateTimeToString() throws Exception {
assertThat(new DateTime().toString(), is(DateTimeToStringAspect.TO_STRING_RESULT));
}
}
Surefire plugin configuration from pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<argLine>-XX:-UseSplitVerifier</argLine>
<argLine>-javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/${aspectjweaver.version}/aspectjweaver-${aspectjweaver.version}.jar</argLine>
</configuration>
</plugin>

Related

Can I suppress the default Optional<NativeWebRequest> getRequest() that OpenAPI generates into interfaces?

I have an OpenAPI 3.0 file that specifies two REST resources with operations, let's say:
openapi: 3.0.0
[...]
paths:
/a:
post:
[...]
/b
post:
[...]
Then I use the openapi-generator-maven-plugin like:
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.1.2</version>
<configuration>
[...]
<configOptions>
<interfaceOnly>true</interfaceOnly>
[...]
</configOptions>
</configuration>
</plugin>
To generate Java interfaces, giving me:
public interface AApi {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
default ResponseEntity<String> postA([...]) { [...] }
}
public interface BApi {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
default ResponseEntity<String> postB([...]) { [...] }
}
In the end, I would like to write a single class that implements both interfaces:
class TheController implements AApi, BApi { [...] }
However, the getRequest() method gets in the way, because Java is unable to inherit two default implementations with identical names.
Is there a way to suppress generating this method? (Or some other means to enable implementing both interfaces, that I haven't thought of?)
I know this question is already answered, but I also had this issue and found a different solution. You can also add this in the configOptions section:
<configOptions>
<skipDefaultInterface>true</skipDefaultInterface>
</configOptions>
It does have the 'side effect' that you no longer have default implementations.
See also documentation
You can override the getRequest() method in your implementing controller class.
For further reading, refer to https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8.4

How I should configure a POJO class to enable it to be used as a resource in JAX-RS rest service? (Liferay 7.1)

I can use String object in JAX-RS rest service but not able to use POJO object. How I should configure a POJO class to enable it to be used as a resource in JAX-RS rest service?
DTO class
public class RestServiceDTO {
private String groupId;
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId)
this.groupId = groupId;
}
#Override
public String toString() {
return "RestServiceDTO [groupId=" + groupId + "]";
}
}
Rest service:
#Component(
immediate = true,
property = {
JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + "=/greetings",
},
service = Application.class
)
public class RestServiceApplication extends Application {
public Set<Object> getSingletons() {
return Collections.<Object>singleton(this);
}
#POST
#Path("/post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String test(RestServiceDTO dto) {
String groupid = dto.getGroupId();
return "{'groupid':'" + groupid + "'}";
}
}
Error:
2019-02-12 13:33:58.021 ERROR [http-nio-8080-exec-1][JAXRSUtils:83] No
message body reader has been found for class com.dto.RestServiceDTO,
ContentType: application/json
Since version 7.1 Liferay supports the OSGi JAX-RS Whiteboard specification, which means support for JAX-RS 2.1 using CXF, which also mean that there is support for JAXB annotated POJOs.
If you need to return a simple POJO you would normally be OK just by annotating your POJO class with #XmlRootElement.
Make sure you get JAXB runtime support attached to your application by requiring it on your application component configuration putting the property osgi.jaxrs.extension.select=(osgi.jaxrs.name=jaxb-json) on your application component.
This property will instruct the JAX-RS whiteboard to not start your application until the extension is present and ready for your application.
Since Liferay 7.2 the JAXB default implementation has been changed to Jackson. There is no configuration change needed, but now every POJO can be serialized to JSON even if the POJO is not annotated. Just make sure the jaxb runtime support is attached to your application the same as above.
In both 7.1 and 7.2 you can check the status of your JAX-RS applications, and attached extensions, using the gogo shell command jaxrs:check
Liferay uses the Apache CXF implementation of JAX-RS. As #dkb mentioned in the comments you need to provide the annotation that you have in the sample code.
You need to add the dependencies. See the list below and note that some are provided by the platform but some needs to be included in your jar and don't forget the transitive dependencies.
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.7.9</version>
</dependency>
The last thing is. You need to register your Jackson provider within the JAX-RS app. It is done in teh applicaiton class for example like this (there are more ways how to do it).
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(getJacksonProvider());
return Collections.unmodifiableSet(singletons);
}
Add dependency
for MAVEN:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.9.10</version>
</dependency>
for GRADLE:
compileOnly group: 'com.fasterxml.jackson.jaxrs', name: 'jackson-jaxrs-json-provider', version: '2.9.10'
now add this method in your Apllication class
private JacksonJsonProvider getJacksonJsonProvider() {
JacksonJsonProvider jacksonJsonProvider = new JacksonJsonProvider();
ObjectMapper objectMapper = new ObjectMapper();
// Prevent serialization of null and empty string values
objectMapper.setSerializationInclusion(Include.NON_EMPTY);
jacksonJsonProvider.setMapper(objectMapper);
jacksonJsonProvider.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return jacksonJsonProvider;
}
now change the code of getSingletons() method of your Application class to
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(this);
singletons.add(getJacksonJsonProvider());
return Collections.unmodifiableSet(singletons);
}
now I think you have to change your return statement to
return Response.ok(JSONFactoryUtil.looseSerializeDeep("{'groupid':'" + groupid + "'}"), MediaType.APPLICATION_JSON).build();
but I am not sure that you have to change your return statement or yours one will run ok

Pretty-Faces redirect loop

hi i'm using pretty faces 3.3.3 in tomcat 7
and this config
<rewrite match="/browse" trailingSlash="append" toCase="lowercase" />
<url-mapping id="browsecategory">
<pattern value="/browse/" />
<view-id value="/browser.xhtml" />
</url-mapping>
i want requests without trailing slash after "browse" to be redirected to browse/ (with trailing slash). The background: if the trailing slash is missing, my relative outputLinks are not handeled as subdirectorys, but as files in the current directory.
when i request now
localhost:8081/App/browse
my browser gets into a redirect loop
EDIT:
is it possible that browse is a reserved keyword? when i replace it with squirrel everything works as expected:
<rewrite match="/squirrel" trailingSlash="append" toCase="lowercase" />
<url-mapping id="browsecategory">
<pattern value="/squirrel/" />
<view-id value="/browser.xhtml" />
</url-mapping>
Because of the amount of confusion that has occurred using the <rewrite/> tag in PrettyFaces, we've migrated to a new core architecture for PrettyFaces (//Rewrite 2.0.0.Final) that provides much greater control over application configuration. (Available here http://ocpsoft.org/prettyfaces/)
I would recommend trying PrettyFaces 4 if your environment permits. You can leave your URL-mappings in the pretty-config.xml file if you wish, but you can now define more custom Rewrite rules, more safely, in a Rewrite ConfigurationProvider:
<!-- for JSF 2.x -->
<dependency>
<groupId>org.ocpsoft.rewrite</groupId>
<artifactId>rewrite-servlet</artifactId>
<version>2.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.ocpsoft.rewrite</groupId>
<artifactId>rewrite-config-prettyfaces</artifactId>
<version>2.0.0.Final</version>
</dependency>
Leave your pretty-config.xml as it is:
<url-mapping id="browsecategory">
<pattern value="/browse/" />
<view-id value="/browser.xhtml" />
</url-mapping>
Now also create a ConfigurationProvider to handle your trailing slashes:
public class RewriteConfig extends HttpConfigurationProvider
{
#Override
public int priority()
{
return 10;
}
#Override
public Configuration getConfiguration(final ServletContext context)
{
return ConfigurationBuilder.begin()
.addRule()
.when(Direction.isInbound().and(Path.matches("/{p}")))
.perform(Redirect.to(context.getContextRoot() + "/{p}/"))
.where("p").matches("^.*[^/]$");
}
}
Don't forget to register/activate the ConfigurationProvider.
Additionally, you can do your URL-mappings in this configuration file as well, thus removing the need for pretty-config.xml or the PrettyFaces 4 con:
public class RewriteConfig extends HttpConfigurationProvider
{
#Override
public int priority()
{
return 10;
}
#Override
public Configuration getConfiguration(final ServletContext context)
{
return ConfigurationBuilder.begin()
.addRule(Join.path("/browse/").to("/browser.xhtml"))
.addRule()
.when(Direction.isInbound().and(Path.matches("/{p}")))
.perform(Redirect.to(context.getContextRoot() + "/{p}/"))
.where("p").matches("^.*[^/]$");
}
}
I didn't test the regular expression in the matches() clause, but it should be something like that. I hope this helps!
The problem is that your trailingSlash rewrite rule also matches things like /browse. Could you try to adjust it like this:
<rewrite match="^/browse$" trailingSlash="append" toCase="lowercase" />
I think this should work, because the rule will now only match exactly /browse and not /browse/.

How can I use a JUnit RunListener in Eclipse?

I wrote a simple RunListener for JUnit which works quite well with Maven. I could register it for the maven-failsafe-plugin via
<properties>
<property>
<name>listener</name>
<value>com.asml.lcp.middleware.common.jboss.test.tps.TestDocumentationListener</value>
</property>
</properties>
and see the correct output from the listener.
Now I want to register the same RunListener in Eclipse to see the same output there, when I run the tests.
Is this possible? For testing purposes and to be consistent it would be nice to have the same output.
I have a set of tests that need a database to be executed. I want to create the database at the beginning of their execution and remove it at the end.
From maven I've also added a RunListener to the maven-surefire-plugin and it works fine. And I've also added a system property variable named ismaven. When I execute the test from maven this variable is initialized but when I execute the tests from the Eclipse, this variable is null (I access to the variable with System.getProperty).
<configuration>
<properties>
<property>
<name>listener</name>
<value>com.mycompany.MyRunListener</value>
</property>
</properties>
<systemPropertyVariables>
<ismaven>true</ismaven>
</systemPropertyVariables>
</configuration>
All my database tests inherit from a class that has a #BeforeClass and an #AfterClass methods. These methods check if the test is being executed by Maven or by the Eclipse checking the value of the ismaven property. If the test is being executed by maven, the ismaven property has a value and they do anything. But is the test is being executed by the Eclipse, the ismaven variable is null and they starts (#BeforeClass) or stops (#AfterClass) the database:
#BeforeClass
public static void checkIfStartDatabase() {
String ismaven = System.getProperty("ismaven");
// If it is not maven, start the database
if (ismaven == null) {
startDatabase();
}
}
#AfterClass
public static void checkIfStopDatabase() {
String ismaven = System.getProperty("ismaven");
// If it is not maven, stop the database
if (ismaven == null) {
stopDatabase();
}
}
This solution doesn't solve 100% your problem but if you implement it you can execute (and debug) all the tests of one JUnit class using the Eclipse and you can also execute all the tests of your project using Maven with the guarantee that you will execute once a piece of code before or after the execution of all your tests.
Yes, it is possible. Basically you have to implement your own Runner and inside the run method, you can add a custom run listener. I figured this out based on this post, point 2.
Here is my listener
public class TestLogger extends RunListener
{
public void testFinished(Description description)
{
System.out.println("Successful " + description.getMethodName());
}
}
and here is my Runner
public class TestRunner extends BlockJUnit4ClassRunner
{
public TestRunner(Class<?> klass) throws InitializationError
{
super(klass);
}
#Override
public void run(RunNotifier notifier)
{
notifier.addListener(new TestLogger()); // THIS IS THE IMPORTANT LINE
super.run(notifier);
}
}
and here is my actual junit test
#RunWith(TestRunner.class) // THIS LINE IS ALSO IMPORTANT
public class MyTest1
{
#Test
public void Test1() throws Exception
{
if (Math.random() < .5) throw new Exception("ouch");
assertFalse(Math.random() < .5);
}
}
You can run MyTest1 or the Test1 method using the context menu in Eclipse and it will invoke the testLogger as you would expect.
I did some more reseatrch on this. As far as I can see now, there is no way to add a run listener too the JUnit runs in Eclipse.

get list of services implementations with OSGi declarative services

I have a very simple example of declarative services. I'm following this tutorial http://www.eclipsezone.com/eclipse/forums/t97690.html?start=0. Every thing is working as expected. However, I cannot figure out how I can make the "SampleImporter" (which is the bundle that is expected to use other bundles' services) aware of the list of "SampleExporter" (bundle providing a service). In other words, I want the "SamlpeImporter" to see the ID of the bundle(s) that it is eventually using. This information is very useful for my application.
here is the XML file for SampleExporter:
<?xml version="1.0"?>
<component name="samplerunnable">
<implementation class="org.example.ds.SampleRunnable"/>
<property name="ID" value="expoter" />
<service>
<provide interface="java.lang.Runnable"/>
</service>
while for the SampleImporter:
<?xml version="1.0"?>
<component name="commandprovider1">
<implementation class="org.example.ds.SampleCommandProvider1"/>
<service>
<provide interface="org.eclipse.osgi.framework.console.CommandProvider"/>
</service>
<reference name="RUNNABLE"
interface="java.lang.Runnable"
bind="setRunnable"
unbind="unsetRunnable"
cardinality="0..1"
policy="dynamic"/>
</component>
In the Importer side, I have the following function:
public class SampleCommandProvider1 implements CommandProvider {
private Runnable runnable;
public synchronized void setRunnable(Runnable r) {
runnable = r;
}
public synchronized void unsetRunnable(Runnable r) {
runnable = null;
}
public synchronized void _run(CommandInterpreter ci) {
if(runnable != null) {
runnable.run();
} else {
ci.println("Error, no Runnable available");
}
}
public String getHelp() {
return "\trun - execute a Runnable service";
}
}
This works fine but then if I want to get the value of the property, using
public synchronized void setRunnable(Runnable r, Map properties)
or
public synchronized void setRunnable(Runnable r, ServiceReference reference)
the method run of the exporter is never called which means that the bind function (setRunnable is not called).Hwever, using the console command "services" I see that the exporter bundle is used by the imporeter one. Also, using ss and ls I can see that the component eporter is "satisfied".
What is wrong with my implementetion?
Thanks in advance
Cheers
Marie
The following bind signature is not supported by any version of DS:
public void setRunnable(Runnable r, ServiceReference ref)
Instead you will have to take only the ServiceReference and use either the ComponentContext or BundleContext to access the service instance object.
Alternatively if you want a more POJO-style way of accessing service properties, the following bind signature is allowed in DS 1.1 (but not in DS 1.0):
public void setRunnable(Runnable r, Map properties)
To access DS 1.1 features, you need to add the correct namespace to your XML as follows:
<component xmlns='http://www.osgi.org/xmlns/scr/v1.1.0' name='...'>
By the way, I wrote this original article a very long time ago! These days I would use bnd annotations to avoid having to write the XML document by hand.