Create custom pathbrowser predicate in AEM 6.2 - java

I'm trying to implement custom OSGI service predicate for pathbrowser. If somebody have any idea what is wrong with this code :) There is exception below. Maybe it is something with the #Component or dependency
<path jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/foundation/form/pathbrowser"
fieldDescription="List item link"
fieldLabel="List Item link"
name="./path"
predicate="predicate"
rootPath="/content">
</path>
Predicate implementation:
import org.apache.commons.collections.Predicate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.Resource;
import com.day.cq.commons.predicate.AbstractResourcePredicate;
import com.day.cq.wcm.api.Page;
#Component(label = "Content-page Predicate", description = "This predicate is used to restricted to allow selection of pages that have template content-page")
#Service(value = Predicate.class)
#Properties({
#Property(label = "Predicate Name", name = "predicate.name", value = "predicate", propertyPrivate = true) })
public class ContentPagePredicate extends AbstractResourcePredicate {
private static final String CQ_TEMPLATE_CONTENT = "/conf/xxx-lab/settings/wcm/templates/content-page";
#Override
public boolean evaluate(Resource resource) {
if (null != resource) {
if (!resource.getResourceType().equals("cq:Page")) {
return false;
}
Page page = resource.adaptTo(Page.class);
return page.getTemplate().getName().equals(CQ_TEMPLATE_CONTENT);
}
return false;
}
}
Maven build output:
[ERROR] Failed to execute goal org.apache.felix:maven-scr-plugin:1.20.0:scr (generate-scr-scrdescriptor) on project SomethingDemo.core: Execution generate-scr-scrdescriptor of goal org.apache.felix:maven-scr-plugin:1.20.0:scr failed: An API incompatibility was encountered while executing org.apache.felix:maven-scr-plugin:1.20.0:scr: java.lang.VerifyError: Constructor must call super() or this() before return
[ERROR] Exception Details:
[ERROR] Location:
[ERROR] com/day/cq/commons/predicate/AbstractNodePredicate.<init>()V #1: return
[ERROR] Reason:
[ERROR] Error exists in the bytecode
[ERROR] Bytecode:
[ERROR] 0x0000000: 2ab1

The error you see can happen when you extend a class from the AEM API that's annotated with SCR annotations (used to generate OSGi bundle descriptors) and, at the same time, obfuscated in the Uber Jar you're using.
You can find an unobfuscated Uber Jar for the AEM version you're using in Adobe's public Maven repository.
If you represent a customer or a partner, you should also be able to download one from the help site https://daycare.day.com/home/products/uberjar.html
If your project is using a repository that already has the unobfuscated Jar, it should be as simple as changing the dependency.
For example, in a project using the AEM 6.2 Uber Jar with obfuscated classes
<dependency>
<groupId>com.adobe.aem</groupId>
<artifactId>uber-jar</artifactId>
<version>6.2.0</version>
<scope>provided</scope>
<classifier>obfuscated-apis</classifier>
</dependency>
Just change the classifier to get an unobfuscated version:
<dependency>
<groupId>com.adobe.aem</groupId>
<artifactId>uber-jar</artifactId>
<version>6.2.0</version>
<scope>provided</scope>
<classifier>apis</classifier>
</dependency>
Check out this Github issue for a wider discussion on a very similar problem.
You may also find this Adobe Help Forum thread interesting,
although it pertains to a beta version.

Just try to implement org.apache.commons.collections.Predicate.
Also: resource.getResourceType().equals("cq:Page") will never be true, as cq:Page is the resource's jcr:pimaryType. page.getTemplate() does not work on publish:
public booean evaluate(Resource resource) {
if (null == resource) return false;
final ValueMap map = resource.getValueMap();
return "cq:Page".equals(map.get("jcr:primaryType", "")
&& CQ_TEMPLATE_CONTENT.equals(map.get("cq:template", "")
}

Related

Load project class within maven mojo

I am trying to load a projects class during the execution of a maven mojo.
Unfortunately this operation fails, since the class loader is missing a referenced class.
Looking around I found already the approaches Maven mojo plugin to load class from hosting project and Maven plugin can't load class
I combined the two approaches, ending up with the following code:
private ClassLoader getClassLoader(final MavenProject project) {
try {
final List<URL> classPathUrls = new ArrayList<>();
// adding the projects dependency jars
final Set<Artifact> artifacts = getProject().getDependencyArtifacts();
for (final Artifact artifact : artifacts) {
classPathUrls.add(artifact.getFile().toURI().toURL());
}
// adding the projects classes itself
final List<String> classpathElements = project.getCompileClasspathElements();
classpathElements.add(project.getBuild().getOutputDirectory());
classpathElements.add(project.getBuild().getTestOutputDirectory());
for (final String classpathElement : classpathElements) {
classPathUrls.add(new File(classpathElement).toURI().toURL());
}
// creating a class loader
final URL urls[] = new URL[classPathUrls.size()];
for (int i = 0; i < classPathUrls.size(); ++i) {
urls[i] = classPathUrls.get(i);
}
return new URLClassLoader(urls, this.getClass().getClassLoader());
} catch (final Exception e) {
getLog().debug("Couldn't get the classloader.");
return this.getClass().getClassLoader();
}
}
The class which fails to load is an implementation of the interface "org.bson.codecs.Codec", which is contained in "org.mongodb:bson", which is implicit imported via the dependency:
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.1.1</version>
<scope>provided</scope>
</dependency>
This dependency has a dependency to another jar (scope: provided), containing the mentioned interface, which can be seen in the maven dependency tree:
$> mvn dependency:tree
[INFO] net.my.project:my-project:jar:1.0-SNAPSHOT
[INFO] +- org.mongodb:mongodb-driver-sync:jar:4.1.1:provided
[INFO] | +- org.mongodb:bson:jar:4.1.1:provided
[INFO] | \- org.mongodb:mongodb-driver-core:jar:4.1.1:provided
When I look to the class path elements of the created class loader, I see that mongodb-driver-sync.jar is included, but since it declares the "org.mongodb:bson" dependency with scope provided it the interface is still not in class path.
So, the final question is: How can I load a class which references a class from an "indirect" dependency?
I've noticed, that
project.getArtifacts()
was empty, even though the javadoc says it is supposed to contain all dependencies (lazily populated).
So, with additional research I found Custom Maven Plugin development - getArtifacts is empty though dependencies are included which suggests to adjust the #Mojo annotation:
#Mojo(name = "mojoName", requiresDependencyResolution = ResolutionScope.COMPILE)
after adjusting the annotation, it is even enough to use the "project.getCompileClasspathElements();", it is not necessary anymore to iterate through the artifacts at all.

Programatically getting an effective POM using Maven Resolver Provider

What do I want to do?
Given a POM file on the local filesystem.
I want to programmatically obtain the effective POM of that POM file. Specifically I want to do the following:
Resolve the POMs dependencies
Ensure that all parent POMs are processed
Obtain the list of dependencies of the fully resolved POM
And so on...
I don't need to obtain transitive dependencies.
What works?
I'm using Maven Resolver Provider which sort of works. However
I have to use a package private class org.apache.maven.repository.internal.DefaultModelResolver
Here a GitHub link to a sample Maven project that you can run: https://github.com/sahilm/maven-resolver-test
The example program does the following:
Downloads the latest spring boot POM from Maven Central.
Prints out it's direct dependencies (with parent deps included)
You can run the the program with:
mvn exec:java -Dexec.mainClass="com.sahilm.maven_resolver_test.Test"
What I need help with?
I need help with understanding why I have to use a package private class to get stuff to work.
Is there another way to get the information I need?
You can create (in your project) a public class under the package: org.apache.maven.repository.internal that extends the package-accessibility class. Just use a class name that is not possible to be used in the furutre by the vendor.
package org.apache.maven.repository.internal;
public class VisibleDefaultModelResolver extends DefaultModelResolver{
public VisibleDefaultModelResolver(RepositorySystemSession session, RequestTrace trace, String context, ArtifactResolver resolver, VersionRangeResolver versionRangeResolver, RemoteRepositoryManager remoteRepositoryManager, List<RemoteRepository> repositories) {
super(session, trace, context, resolver, versionRangeResolver, remoteRepositoryManager, repositories);
}
}
Then your code becomes:
ModelResolver modelResolver = new VisibleDefaultModelResolver(session, requestTrace, "context", artifactResolver, versionRangeResolver, remoteRepositoryManager, repos);
Maybe you can use ProjectModelResolver. Here's a code snippet,
DefaultRepositorySystem repositorySystem =
new DefaultRepositorySystem();
repositorySystem.initService(locator);
ModelResolver modelResolver =
new ProjectModelResolver(session, requestTrace,
repositorySystem, remoteRepositoryManager, repos,
ProjectBuildingRequest.RepositoryMerging.POM_DOMINANT,
null);
I've included a working code here.

Failed to process business interfaces for EJB class

While starting maven with test parameters, I get the above mentioned exception. While creating the integration test deployment, I get the following:
org.jboss.as.server.deployment.DeploymentUnitProcessingException: WFLYEJB0466: Failed to process business interfaces for EJB class class ..contract.ContractMockService
The concerning class looks like this:
package ..integration.bestand.contract;
import java.time.LocalDate;
import java.util.ArrayList;
import javax.ejb.Local;
import javax.ejb.Stateless;
import org.apache.deltaspike.core.api.exclude.Exclude;
import org.apache.deltaspike.core.api.projectstage.ProjectStage;
...
#Exclude(ifProjectStage = {
ProjectStage.Production.class,
ProjectStage.Staging.class,
..Integration.class,
..Qs.class,
..PatchQs.class
})
#Stateless
#Local(IContractIntService.class)
public class ContractMockService implements IContractIntService {
...
return ContractBuilder.build();
}
}
The interface IContractIntService looks like:
package ..integration.bestand.contract;
import javax.ejb.Local;
...
#Local
public interface IContractIntService {
public enum State {
SUCCESS,
UNKNOWN_ERROR,
NOT_FOUND;
// TODO: Stati für Fehler hier definieren
}
//Interface comment
Result<State, ContractDTO> retrieveContract(String contractIdentifier);
}
Note: The interface is in another project which is included via maven.
The Test looks like this:
package ..api.contractregistration.service;
import static org.hamcrest.CoreMatchers.any;
import static org.hamcrest.MatcherAssert.assertThat;
import java.util.logging.Logger;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TestWatcher;
import org.junit.runner.RunWith;
import ..core.test.IntegrationTest;
#RunWith(Arquillian.class)
#Category(IntegrationTest.class)
public class ContractRegistrationIntegrationTest {
protected final Logger log = Logger.getLogger(ContractRegistrationIntegrationTest.class.getCanonicalName());
#Rule
public TestWatcher watcher = new TestWatcher() {
#Override
protected void starting(org.junit.runner.Description description) {
log.info(String.format("---> Starting test: %s", description));
}
#Override
protected void failed(Throwable e, org.junit.runner.Description description) {
log.info(String.format("<--- Test failed: %s", description));
}
#Override
protected void succeeded(org.junit.runner.Description description) {
log.info(String.format("<--- Test succeeded: %s", description));
}
};
#Deployment
public static WebArchive createDeployment() {
WebArchive result = ShrinkWrap.create(WebArchive.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsResource("META-INF/persistence.xml", "META-INF/persistence.xml")
.addPackages(true, "..ejb.portal")
.addPackages(true, "..core")
.deletePackages(true, "..core.config.deltaspike")
.addPackages(true, "..integration")
.addPackages(true, "..api")
.addPackages(true, "org.apache.deltaspike.core")
.addPackages(true, "..ejb.util");
System.out.println("########## TEST DEPLOYMENT########" + result.toString(true));
return result;
}
#Test
public void test() {
String tempPw = "bla"; // result.getDto();
assertThat(tempPw, any(String.class));
}
}
The remarkable thing about this test is, that I'm not even using anything of the MockService inside a test.
The maven configuration looks like this:
Goals: clean test -Parq-wildfly-managed
JRE VM Arguments: -Djboss.home="myLocalWildflyDirectory"
JAVA_HOME is set to jdk8.
Last thing is my pom, specifically the part of the container "arq-wildfly-managed":
...
<profile>
<!-- An optional Arquillian testing profile that executes tests in your WildFly instance, e.g. for build server -->
<!-- This profile will start a new WildFly instance, and execute the test, shutting it down when done -->
<!-- Run with: mvn clean test -Parq-wildfly-managed -->
<id>arq-wildfly-managed</id>
<dependencies>
<dependency>
<groupId>org.wildfly.arquillian</groupId>
<artifactId>wildfly-arquillian-container-managed</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.ivi.torino</groupId>
<artifactId>torino-integration-bestand-mock-ejb</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.ivi.torino</groupId>
<artifactId>torino-integration-docservice-mock-ejb</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.ivi.torino</groupId>
<artifactId>torino-integration-bestand-api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
</profile>
...
A normal maven build with clean verify package install (just no test included) builds successfully.
Note: For this post, I renamed the packages to exclude company specializations.
Similar errors suggest correcting the ShrinkWrap deployment, but I included virtually every package there is and even tried to explicitly include the interface-class. But still, the same error remains.
What could cause this?
Try this in the Test (ShrinkWrap):
.addAsResource(new StringAsset("org.apache.deltaspike.ProjectStage=IntegrationTest"), "META-INF/apache-deltaspike.properties")
And change your Exclude to this:
#Exclude(exceptIfProjectStage = ProjectStage.IntegrationTest.class)
If you need to exclude additional Stages, add them to this very exclude statement
A bit late, but a better solution to the problem was the following:
Inside the ShrinkWrap deployment, a use of the shrinkwrap maven resolver is needed. So, instead of
.addPackages(true, "org.apache.deltaspike.core")
inside the creation of result, use the maven resolver. Should look something like this:
ShrinkWrap
.create(WebArchive.class, "test.war")
.addAsLibraries(
resolver.artifact("org.apache.deltaspike.core")
.resolveAsFiles());
The artifact is the maven artifactId. this will return another .war. multiple .wars (created from the resolver or the way you see in the original question) can be merged. This merged .war then has to be returned from the deployment method.
Reason behind this:
External includes (in this case deltaspike) are missing resources once you import them via ShrinkWrap.create.*.addAsPackages.., so this should only be used for internal project packages. To use the maven resolver, you can include the following in the .pom-file:
<dependency>
<groupId>org.jboss.shrinkwrap.resolver</groupId>
<artifactId>shrinkwrap-resolver-impl-maven</artifactId>
<scope>test</scope>
</dependency>
credits to dzone.com for the maven resolver code snippets. I'm currently working on another project, so I can't show the original code, but it was very similar to this.
Maybe this solution will help someone in the future.

Truth.assertAbout and JavaSourceSubjectFactory.javaSource()

I'm writing an annotation processor and want to write some unit tests for it by using google-compile-testing and truth:
So I want to write a very simple unit test.
import static com.google.common.truth.Truth.assertAbout;
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
#Test
public void componentOnConcreteClass() {
JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.NotAClass",
"package test;",
"",
"import my.annotation.MyAnnotation;",
"",
"#MyAnnotation",
"interface NotAComponent {}");
assertAbout(javaSource()).that(componentFile)
.processedWith(new MyProcessor())
.failsToCompile()
.withErrorContaining("interface");
}
So basically I have copy an pasted a simple test from google's dagger2 repo and replaced the relevant data with my annotation processor.
I'm using maven, and I'm using the same dependencies as dagger2:
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>
<version>0.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
But I can't compile the code. I guess there is an generics param problem, but can't figure out what the problem is.
Compilation failure:
[ERROR] ProcessorTest.java:[46,5] method assertAbout in class com.google.common.truth.Truth cannot be applied to given types;
[ERROR] required: com.google.common.truth.SubjectFactory<S,T>
[ERROR] found: com.google.testing.compile.JavaSourceSubjectFactory
[ERROR] reason: no instance(s) of type variable(s) S,T exist so that argument type com.google.testing.compile.JavaSourceSubjectFactory conforms to formal parameter type com.google.common.truth.SubjectFactory<S,T>
Any hint what I'm doing wrong? I can't find any difference to google dagger2 tests (which by the way compiles on my machine)
The artifact com.google.testing.compile:compile-testing:0.5 depends on org.truth0:truth:0.15 which is the old location of Truth. Try using version 0.6 of compile-testing instead.

Maven clean install: cannot find symbol

My Maven clean install is failing. Below is the error message.
[ERROR] Failed to execute goal
org.apache.maven.plugins:maven-compiler-plugin:2.0.2:compile
(default-compile) on project MyProject: Compilation failure:
Compilation failure:
[ERROR] C:\..\MyClass.java:[13,2] cannot find symbol
[ERROR] symbol : class MyAnnotation
[ERROR] location: class mypackage.MyClass
MyClass.java
public class MyClass{
#MyAnnotation
public static class MyAnnotation{
//some static nested class code here...
}
MyAnnotation.java
#Retention (RetentionPolicy.RUNTIME)
public #interface MyAnnotation{
}
I have no clue why this would present problems, can anyone please give me some ideas?
Found the problem...
I apologize as I didn't include enough code for anyone to determine the root cause of the issue, normally I don't include import statements in my posts but this time I should have. The below class is a more full example. As we can see, the below class declares a static import to an object that resides in the static nested class (within the same .java file). While this is not illegal from a compilation standpoint, it was causing issues in my Maven clean install. I'm still not %100 sure why maven does not like this, but this fashion of static importing doesn't really make sense to begin with. To fix this, I removed the static import and substituded a normal static call (MyAnnoation.someObject) wherever the static import was being used.
package com.classes;
import static com.classes.MyClass.MyAnnotation.someObject;
public class MyClass{
#MyAnnotation
public static class MyAnnotation{
public static final Object someObject = new Object();
}
Again, my apologies for not providing the static import details in my original post, hopefully someone finds this helpful.
Did your maven-compiler-plugin is using 1.5. By default it used 1.4 and annotations are introduced in 1.5
Try the following:
Import just one of the classes.
Always use the FQN (fully-qualified name) for the other case, let's assume that's the annotation (#com.foo.MyAnnotation).
Also, as Vinay suggested, set the source and target versions in your maven-compiler-plugin's <configuration/>.

Categories