NoAspectBoundException with aspectj and tapestry 5 - java

I have a web project built with Tapestry 5.2.1. I have a simple logging aspect that I was using for tracing on this application. Everything was working fine until I started refactoring parts of the application and attempted to deploy it.
When I deploy the application, no matter what page I attempt to go to I get the following exception:
Caused by: java.lang.RuntimeException: Exception assembling root component of page Index: Exception while initializing TraceAspect: org.aspectj.lang.NoAspectBoundException: TraceAspect
at org.apache.tapestry5.internal.pageload.ComponentAssemblerImpl.performAssembleRootComponent(ComponentAssemblerImpl.java:124)
at org.apache.tapestry5.internal.pageload.ComponentAssemblerImpl.access$000(ComponentAssemblerImpl.java:38)
at org.apache.tapestry5.internal.pageload.ComponentAssemblerImpl$1.invoke(ComponentAssemblerImpl.java:82)
at org.apache.tapestry5.internal.pageload.ComponentAssemblerImpl$1.invoke(ComponentAssemblerImpl.java:79)
at org.apache.tapestry5.ioc.internal.OperationTrackerImpl.invoke(OperationTrackerImpl.java:65)
... 73 more
Caused by: org.aspectj.lang.NoAspectBoundException: Exception while initializing TraceAspect: org.aspectj.lang.NoAspectBoundException: TraceAspect
at TraceAspect.aspectOf(TraceAspect.aj:1)
at com.wex.rrt.wrightweb.reportrequest.webapp.pages.Index.initializer(Index.java:3)
at com.wex.rrt.wrightweb.reportrequest.webapp.pages.Index.<init>(Index.java)
at $Instantiator_12d4da06f67.newInstance($Instantiator_12d4da06f67.java)
at org.apache.tapestry5.internal.structure.InternalComponentResourcesImpl.<init>(InternalComponentResourcesImpl.java:146)
at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.<init>(ComponentPageElementImpl.java:593)
at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.<init>(ComponentPageElementImpl.java:609)
at org.apache.tapestry5.internal.pageload.ComponentAssemblerImpl.performAssembleRootComponent(ComponentAssemblerImpl.java:93)
... 77 more
Caused by: org.aspectj.lang.NoAspectBoundException: TraceAspect
at TraceAspect.aspectOf(TraceAspect.aj:1)
at AbstractLoggingAspect.<init>(AbstractLoggingAspect.aj:7)
at TraceAspect.<init>(TraceAspect.aj:12)
at TraceAspect.ajc$postClinit(TraceAspect.aj:1)
at TraceAspect.<clinit>(TraceAspect.aj:1)
... 84 more
My aspect has remained unchanged and is this:
#Aspect
public class TraceAspect {
Logger logger = Logger.getLogger("trace");
public TraceAspect() {
logger.setLevel(Level.ALL);
}
/**
* Will log every execution of
* <ul>
* <li>doEverything</li>
* <li>doSomething</li>
* </ul>
* excluding any test classes.
*/
#Pointcut("(execution(public void *(..)) || execution(*.new(..))) && !within(*Test) !within(com.aspects.*)")
protected void logging() {
}
#Around("logging()")
public void doThing(final ProceedingJoinPoint thisJoinPoint) throws Throwable {
final String joinPointName = thisJoinPoint.getThis().getClass().getSimpleName() + "." + thisJoinPoint.getSignature().getName() + "()";
logger.info("Entering [" + joinPointName + "]");
thisJoinPoint.proceed();
logger.info("Leaving [" + joinPointName + "]");
}
}
During compilation everything works fine. I'm using the maven plugin to compile the aspects:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.3</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<complianceLevel>1.6</complianceLevel>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
I've been working at this off and on for most of the day and haven't gotten anywhere. I'm not exactly understanding the NoAspectBoundException. It would seem that the compiler is not weaving the aspect completely? I'm new to AspectJ but I'm wondering if this is something to do with Tapestry5. Although I know that Tap5 uses AOP though.
As I said, this was all working as is just fine until I moved some things into a separate tapestry custom library that is now a dependency for my web app.

You are advising the aspect's constructor due to the unrestricted execution(*.new(..)) part of the pointcut. When the aspect is being instantiated, the advice doesn't have an instance of the aspect, hence the error. You should be able to fix this by adding !within([package of].TraceAspect).

I think we are having a conflict of AOP. AspectJ has done something to the code, and then Tapestry comes along and does something else ... including ignoring existing Class constructors on the component class and adding its own. I'm not sure how to get the two working together.

I know it is too late to answer this question. But as I understand it's better to don't use aspectj with the tapestry. If you want to use it you must not define the #Aspect class as a tapestry component. Just move the class to another path (anywhere except the pages package).

Related

Generate VDM for SFSF using Java in SAP Cloud SDK: Generated URI is wrong

I'm trying to build an app that reads info from SFSF. For this, I'm using the Virtual Data model generator tool (the maven plugin) with SFSF OData metadata to be able to access the system. I'm following these steps:
Get a project via archetype (with powershell):
mvn archetype:generate "-DarchetypeGroupId=com.sap.cloud.sdk.archetypes" "-DarchetypeArtifactId=scp-cf-tomee" "-DarchetypeVersion=RELEASE"
Add the following to the application\pom.xml
In dependencies:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
In plugins:
<plugin>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>odata-generator-maven-plugin</artifactId>
<version>3.13.0</version>
<executions>
<execution>
<id>generate-consumption</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputDirectory>${project.basedir}/edmx</inputDirectory>
<outputDirectory>${project.build.directory}/vdm</outputDirectory>
<defaultBasePath>/odata/v2</defaultBasePath>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.basedir}/vdm</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
Get the OData metadata file from https://apisalesdemo2.successfactors.eu/odata/v2/JobRequisition/$metadata and place it in ./application/edmx
Create a destination service (my-destination) and add a destination there pointing to my SFSF instance with basic auth (with user#companyId, the connection is 200:OK)
Add the destination service in the manifest.yml
Create a java class to call the destination and get the data:
package com.sap.sdk;
import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.odatav2.connectivity.ODataException;
import com.sap.cloud.sdk.s4hana.connectivity.DefaultErpHttpDestination;
import com.sap.cloud.sdk.s4hana.connectivity.ErpHttpDestination;
import com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.rcmjobrequisition.JobRequisition;
import com.sap.cloud.sdk.s4hana.datamodel.odata.services.DefaultRCMJobRequisitionService;
#WebServlet("/req")
public class JobReqServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(JobReqServlet.class);
private final ErpHttpDestination destination = DestinationAccessor.getDestination("sfsf-sdk-dest").asHttp()
.decorate(DefaultErpHttpDestination::new);
#Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
throws ServletException, IOException {
try {
final List<JobRequisition> jobReqs = new DefaultRCMJobRequisitionService()
.getAllJobRequisition()
.execute(destination);
response.setContentType("application/json");
response.getWriter().write(new Gson().toJson(jobReqs));
} catch (final ODataException e) {
logger.error(e.getMessage(), e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write(e.getMessage());
}
}
}
With all this (I think I'm not missing anything), I do:
mvn clean install
and:
cf push
Everything works well, the hello world servlet works, but when I try to access /req, I get a:
Unable to execute metadata request.
However, I can see that the app is hitting SFSF because if I play with the base path of the service (in the pom.xml) I get 404's coming from SFSF.
Checking everything, I see this when the VDM generator is running:
1. This is the base path I'm giving in the pom:
<defaultBasePath>/odata/v2</defaultBasePath>
I can see the generator picking that path correctly:
[main] INFO com.sap.cloud.sdk.datamodel.odata.generator.DataModelGenerator - Default base path: /odata/v2/
But this is what the generator processes:
[main] INFO com.sap.cloud.sdk.datamodel.odata.generator.ODataToVdmGenerator - Title: RCMJobRequisition
[main] INFO com.sap.cloud.sdk.datamodel.odata.generator.ODataToVdmGenerator - Raw URL: /odata/v2/SFODataSet
[main] INFO com.sap.cloud.sdk.datamodel.odata.generator.ODataToVdmGenerator - Java Package Name: rcmjobrequisition
[main] INFO com.sap.cloud.sdk.datamodel.odata.generator.ODataToVdmGenerator - Java Class Name: RCMJobRequisition
Clearly, that SFODataSet in the URL is not correct. When the app runs, it's tring to get the metadata from .../odata/v2/SFODataSet/$metadata, and that's why it's not finding it.
That SFODataSet is coming from the SFSF metadata:
<Schema Namespace="SFODataSet" xmlns="http://schemas.microsoft.com/ado/2008/09/edm" xmlns:sf="http://www.successfactors.com/edm/sf" xmlns:sap="http://www.sap.com/Protocols/SAPData">
<EntityContainer Name="EntityContainer" m:IsDefaultEntityContainer="true">
<EntitySet Name="JobOfferTemplate_Standard_Offer_Details" EntityType="SFOData.JobOfferTemplate_Standard_Offer_Details" sap:label="JobOfferTemplate_Standard_Offer_Details" sap:creatable="false" sap:updatable="false" sap:upsertable="false" sap:deletable="false">
<Documentation>
<Summary>Job Requisition Template</Summary>
<LongDescription>These entities represent the job requisition template as defined in provisioning.</LongDescription>
<sap:tagcollection>
<sap:tag>Recruiting (RCM)</sap:tag>
<sap:tag>RCM - Job Requisition</sap:tag>
</sap:tagcollection>
</Documentation>
</EntitySet>
<EntitySet Name="JobRequisitionLocale" EntityType="SFOData.JobRequisitionLocale" sap:label="JobRequisitionLocale" sap:creatable="false" sap:updatable="false" sap:upsertable="false" sap:deletable="false">
<Documentation>
...
I can't find the way for this to work. Can you help me find the issue here?
I'm using:
Apache Maven 3.6.2
SAP Cloud SDK 3.13.0
Edit:
SFSF metadata files are available in https://api.sap.com/
The one I'm using for this app is for SFSF - Job Requisition, available here:
https://api.sap.com/api/RCMJobRequisition/overview
From there, you can download the EDMX specification. These are "mock" API's, not connected to a real SFSF instance, but the problem is the same.
To do this I'm following two blogs mainly:
https://blogs.sap.com/2018/04/30/deep-dive-10-with-sap-s4hana-cloud-sdk-generating-java-vdm-for-s4hana-custom-odata-service/
https://blogs.sap.com/2019/05/06/create-an-application-with-sap-cloud-sdk-to-integrate-with-sap-successfactors/
Also, I removed tha last part as I will open a separate question:
SFSF OData call: Failed to convert response into ODataFeed: An 'EdmSimpleTypeException' occurred
Thanks,
kepair
I will start of with a partial answer and edit in more information later if needed.
Regarding the URL:
The behaviour you observe is intentional. The full URL of a request will be assembled as follows: Destination URL + service path + service name + entity + '?' + query parameters. So in your case that might be:
https://my.host.domain/odata/v2/JobRequisitions/MyEntity
Destination: https://my.host.domain
Service Path: /odata/v2
Service name: JobRequisitions
Entity: MyEntity
The generator assembles the default base path from service path + service name. The service name will actually be pulled from the namespace of the EDMX. That is why the URL of your service is being generated the way it is.
The reason for this is simple: One might want to generate a VDM for multiple services at the same time. All of these services are exposed under the same endpoint except for the service name itself. In order to generate all the VDMs with one configuration we can specify the "service path" in the generator and the generator pulls the service name from the EDXM itself.
So that means that your approach of overwriting the generated base path should work:
final List<JobRequisition> jobReqs = new DefaultRCMJobRequisitionService()
.withServicePath("odata/v2/JobRequisition")
.getAllJobRequisition()
.execute(destination);
The error message at the very end of your question looks a bit like a problem with parsing to me. But in order to tackle that one further we would need the full stack trace and the HTTP log output. Also, we can only reproduce the problem if we have access to the metadata. The link you provided requires authorization through username/password.
Since your question above is already quite comprehensive I would recommend that you separate these two problems and create a new question, if this really turns out to be an independent problem. This will also make both questions more relevant for others.

How to pass parameters spring batch Intellij maven

I'm reading a spring batch book and making the examples that show because I will start to work with java and spring batch the next month(now I use c#), one example uses ParameterValidator to validate receiving one parameter name so the maven test will work only if the parameter it was past,
public class ParameterValidator implements JobParametersValidator {
#Override
public void validate(JobParameters parameters) throws JobParametersInvalidException {
String fileName = parameters.getString("fileName");
if(!StringUtils.hasText(fileName)) {
throw new JobParametersInvalidException("fileName missing");
}
else if(!StringUtils.endsWithIgnoreCase(fileName, "csv")) {
throw new JobParametersInvalidException("fileName parameter does " +
"not use the csv file extension");
}
}
I tried passing the data using arguments tag in pom.xml:
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<arguments> fileName=foo.csv</arguments>
</configuration>
</plugin>
</plugins>
, editing the test:
and try other answers I found on internet but always raise the error of missing parameter.
Is it possible to pass arguments using this IDE and maven?
Thanks!
You can just pass in command line argument through args[] passed to main function and call CommandLineJobRunner.main(args) as if you are executing from cmd

Quarkus native build class in both build and run time initialization

I have a 3rd party class which is added to build time initialization by Quarkus, but it requires run time initialization due to static thread usage. When adding it to run time initialization native build then complains about it being in both.
Example project which re-produces this: https://github.com/hshorter/quarkus-avro-decode-example
With "--initialize-at-run-time=org.apache.avro.specific.SpecificDatumReader":
Error: Classes that should be initialized at run time got initialized
during image building: org.apache.avro.specific.SpecificDatumReader
the class was requested to be initialized at build time (from the
command line). To see why
org.apache.avro.specific.SpecificDatumReader got initialized use
-H:+TraceClassInitialization
Without "--initialize-at-run-time=org.apache.avro.specific.SpecificDatumReader":
Error: Detected a started Thread in the image heap. Threads running in
the image generator are no longer running at image run time. To see
how this object got instantiated use -H:+TraceClassInitialization. The
object was probably created by a class initializer and is reachable
from a static field. You can request class initialization at image run
time by using the option --initialize-at-build-time=. Or
you can write your own initialization methods and call them explicitly
from your main entry point. Detailed message: Trace: object
org.apache.avro.specific.SpecificDatumReader
Any help much appreciated.
We just fought through this, in the generated code there's a static initializer like this:
private static BinaryMessageEncoder ENCODER = new BinaryMessageEncoder(MODEL$, SCHEMA$);
private static BinaryMessageDecoder DECODER = new BinaryMessageDecoder(MODEL$, SCHEMA$);
We modified the Velocity templates in the Avro code generation to:
Add the #io.quarkus.runtime.annotations.RegisterForReflection annotation
initialize the statics in the constructor using lazy initialization.
Remove those classes from the runtime class init.
The downside is that you have to maintain your custom codegen templates. It's relatively easy though, here's the maven config to automate your codegen:
<plugin>
<groupId>org.apache.avro</groupId>
<artifactId>avro-maven-plugin</artifactId>
<version>${avro.version}</version>
<executions>
<execution>
<id>schemas</id>
<phase>generate-sources</phase>
<goals>
<goal>schema</goal>
</goals>
<configuration>
<templateDirectory>${project.basedir}/src/main/resources/avro/templates/</templateDirectory>
</configuration>
</execution>
</executions>
</plugin>
you can find the base templates at https://github.com/apache/avro/tree/master/lang/java/compiler/src/main/velocity/org/apache/avro/compiler/specific/templates/java/classic
Sorry, I'm late to the party but the issue you have here is that while you asked SpecificDatumReader to be initialized at runtime, another class initialized at build time requires SpecificDatumReader to be initialized.
So basically, you have some detective work to do to determine why this class was initialized and probably mark the class initializing that one as runtime initialized too.
Note that sometimes it can be a bit hairy.

Maven 2 reports 'java.util.List' cannot be instantiated error

A user has reported that a maven plugin I maintain no longer works with maven 2 (it runs just fine under maven 3). It fails to parse the pom with the error
Cause: Class 'java.util.List' cannot be instantiated
A quick Google suggests that this is a bug in the maven model prior to 3.0 that has affected other plugins.
http://code.google.com/p/maven-samaxes-plugin/issues/detail?id=18
I recently added support for setting values via the command line by specifying expressions for List parameters, and it appears that this has triggered the bug.
/**
* #parameter expression="${targetClasses}"
*/
protected List<String> targetClasses;
I can work around the problem by declaring the field as a concrete list type, but everyone knows that a fairy dies each time you do this.
Are there any nicer soloutions to this problem apart from withdrawing support for maven 2 or using concrete types?
This is quite old now and you've answered your own question, I guess... Still, for completeness, here goes:
I'm using mvn 2.0.9 and had to migrate from List<String>:
/**
* #parameter expression="${arguments}"
*/
protected List<String> arguments; //with or without generics, still complained!
to String[]:
/**
* #parameter expression="${arguments}"
*/
protected String[] arguments;
The pom configuration is then as follows:
<configuration>
...
<arguments>
<argument>arg1</argument>
<argument>arg2</argument>
</arguments>
</configuration>

Maven Codehaus findbugs plugin "onlyAnalyze" option not working as expected

Update for the impatient: it's simple, use package.- for sub-package scanning instead of package.*, as-per martoe's answer below!
I cannot seem to get onlyAnalyze working for my multi-module project: regardless of what package (or pattern) I set, maven-findbugs-plugin doesn't evaluate sub-packages as I'd expect from passing it packagename.*.
To prove either myself or the plugin at fault (though I always assume it's the former!), I setup a small Maven project with the following structure:
pom.xml
src/
main/java/acme/App.java
main/java/acme/moo/App.java
main/java/no_detect/App.java
which is very simple!
The POM has the following findbugs configuration:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>2.4.0</version>
<executions>
<execution>
<phase>verify</phase>
<goals><goal>findbugs</goal><goal>check</goal></goals>
</execution>
</executions>
<configuration>
<debug>true</debug>
<effort>Max</effort>
<threshold>Low</threshold>
<onlyAnalyze>acme.*</onlyAnalyze>
</configuration>
</plugin>
</plugins>
</build>
and every App.java has the following code with two obvious violations:
package acme;
import java.io.Serializable;
public class App implements Serializable
{
private static final class NotSer {
private String meh = "meh";
}
private static final NotSer ns = new NotSer();// Violation: not serializable field
public static void main( String[] args )
{
ns.meh = "hehehe";// Vilation: unused
System.out.println( "Hello World!" );
}
}
Note that no_detect.App has the same content as above, but my expectation is that it wouldn't be evaluated by findbugs because I have the "onlyAnalyze" option set to acme.* which I assume would evaluate acme.App and acme.moo.App and nothing else.
I now execute a mvn clean install to clean, build, test, run findbugs, package, install, which produces the following findbugs report (snipped for brevity) and results in a build failure which is expected because acme.App and acme.moo.App:
<BugInstance category='BAD_PRACTICE' type='SE_NO_SERIALVERSIONID' instanceOccurrenceMax='0'>
<ShortMessage>Class is Serializable, but doesn't define serialVersionUID</ShortMessage>
<LongMessage>acme.App is Serializable; consider declaring a serialVersionUID</LongMessage>
<Details>
<p> This field is never read.&nbsp; Consider removing it from the class.</p>
</Details>
<BugPattern category='BAD_PRACTICE' abbrev='SnVI' type='SE_NO_SERIALVERSIONID'><ShortDescription>Class is Serializable, but doesn't define serialVersionUID</ShortDescription><Details>
<BugCode abbrev='UrF'><Description>Unread field</Description></BugCode><BugCode abbrev='SnVI'><Description>Serializable class with no Version ID</Description></BugCode>
To summarise: only acme.App is analysed, acme.moo.App isn't (bad) and neither is no_detect.App (good).
I tried with two wildcards in the onlyAnalyze option but that produces a successful build but with a findbugs error (Dangling meta character '*' etc).
I tried with onlyAnalyze set to acme.*,acme.moo.* which analyzes all the expected classes (acme.App and acme.moo.App) which means it "works" but not as I expect; i.e. I have to explicitly declare all parent-packages for the classes I want to analyze: that could get large and difficult to maintain on a multi-module project!
Do I have to define every package I want analyzed, or can I declare a wildcard/regex pattern that will do what I want?
I'd rather not use the inclusion/exclusion XML because that requires far more setup and reasoning that I don't currently have time for...
To cite the Findbugs manual: "Replace .* with .- to also analyze all subpackages"

Categories