Spring controllers extending external abstract class throws classNotFoundException - java

So I have a spring controller that looks like:
#Controller // the org.springframework.stereotype.Controller
#RequestMapping("/test")
public class TestController extends BaseController {
...
}
And this works fine. However when I am trying to refactor the code, to move BaseController to an external project "common", the project build will fail with followint stacktrace:
SEVERE: Servlet /mee threw load() exception
java.io.FileNotFoundException: class path resource [ca/wherego/common/controllers/BaseController.class] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:157)
at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:49)
at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:80)
I have tried two ways of using the external project:
1) Let maven uses it as:
<dependency>
<groupId>my.api</groupId>
<artifactId>common</artifactId>
<version>1.0.1</version>
<scope>system</scope>
<systemPath>${basedir}/lib/common-1.0.1.jar</systemPath>
</dependency>
and place the maven in the correct folder ( I don't have a private maven build, so I can't properly upload it as I use other public maven dependencies).
2) I tried to use the "common" project as the project dependency in the build path.
However none of the methods work.. Am I missing anything? Thanks!

Related

SpringBoot : ServletContext resource cannot resolve S3 URL

I'm developing a Spring Boot application in which I'm integrating Amazon S3 service. This class is my repository to access the S3 bucket :
public class S3FileRepository implements ImageRepository {
private String bucket;
private AmazonS3 s3Client;
private ResourceLoader resourceLoader;
public S3FileRepository(ResourceLoader resourceLoader, AmazonS3 s3Client, String bucket) {
this.resourceLoader = resourceLoader;
this.s3Client = s3Client;
this.bucket = bucket;
}
private static String toS3Uri(String bucket, String imageName) {
return String.format("s3://%s/%s", bucket, imageName);
}
#Override
public Resource getImage(String name) {
return resourceLoader.getResource(S3FileRepository.toS3Uri(this.bucket, name).concat(this.IMAGE_EXTENSION));
}
using Spring Boot Autoconfiguration as suggested. So in my pom.xml, among other things, I've
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-aws-autoconfigure</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-aws-context</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
Moreover I've an application.properties done like this:
cloud.aws.credentials.accessKey= (mykey)
cloud.aws.credentials.secretKey= (mysecret)
cloud.aws.credentials.instanceProfile=true
cloud.aws.region.static=eu-west-2
cloud.aws.stack.auto=false
The problem:
Everything works fine if i compile my project and then I simply run the JAR with java -jar target/myproject.jar, i correctly get the image that I ask for and everything is fine.
Instead if I run the project with the IDE default mvn spring-boot:run when I try to get an image (present in the bucket) an Exception occour saying following:
ServletContext resource [/s3://mybucket/test.jpeg] cannot be resolved to URL because it does not exist
java.io.FileNotFoundException: ServletContext resource [/s3://mybucket/test.jpeg] cannot be resolved to URL because it does not exist
So what I think is that it throws an Exception because it's like it goes inside the jar to look for something that match s3://mybucket/test.jpeg but I can't get why, and why it happens only running the project with mvn spring-boot:run and not running the jar.
You're likely hitting the spring-cloud-aws issue #384 whereupon the spring-boot-devtools dependency, which is activated when you start the application from the IDE, activates a different code path in resource loading.
You can test whether you're hitting this issue by removing the spring-boot-devtools dependency from your pom.xml file, reloading the project in your IDE, and running the same test.
There is a difference between app's launching with "java -jar" and "mvn spring-boot:run". From Spring Boot docs: "The Spring Boot Maven plugin includes a run goal that can be used to quickly compile and run your application. Applications run in an exploded form, as they do in your IDE". It can be a reason of problem.

Implementing annotation based spring security

I need to implement spring security in my web application I have created user repository custom Service class and also POJO required to communicate with backend and i am not using in-memory configuration where I am using,
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager).userDetailsService(customUserDetailsService);
}
Dependencies in pom.xml,
<spring.version>4.2.2.RELEASE</spring.version> <spring.security.version>4.0.1.RELEASE</spring.security.version>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
<scope>provided</scope>
</dependency>
While running the application I am getting below error
Caused by: java.io.FileNotFoundException: class path resource
[org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.class]
cannot be opened because it does not exist
but jar is already downloaded
add maven dependencies in the deployment assembly and try again.(I guess you are using eclipse ide) open the project properties got to deployment assembly and click on add and select Java build path entries. move next and select maven dependencies

Problems with Testing Spring Boot Application with Multi Maven Project Setup

I am currently running into some problems with spring boot and multi maven project structure. I am using Spring Boot 4.3.1.
My project structure looks as follows:
parent
-- pom.xml
-- application
-- pom.xml
-- src
-- main
-- java
-- Application.java (annotated with #SpringBootApplication)
-- test
-- java
-- MyApplicationTest.java (annotated with #SpringBootTest)
-- library
-- pom.xml
-- src
-- main
-- java (...)
-- test
-- java
-- MyLibraryTest.java (annotated with #SpringBootTest)
application module has a dependency on library.
MyApplicationTest works perfectly fine, but running MyLibraryTest instead, I fail with the following error:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test at org.springframework.util.Assert.state(Assert.java:392)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.getOr FindConfigurationClasses(SpringBootTestContextBootstrapper.java:173)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.processMergedContextConfiguration(SpringBootTestContextBootstrapper.java:133)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:409)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:305)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildTestContext(AbstractTestContextBootstrapper.java:112)
at org.springframework.boot.test.context.SpringBootTestContextBootstrapper.buildTestContext(SpringBootTestContextBootstrapper.java:78)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:120)
at org.springframework.test.context.TestContextManager.<init>(TestContextManager.java:105)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTestContextManager(SpringJUnit4ClassRunner.java:152)
My first guess is that library needs a dependency on application, but this causes a cycle.
Is there any solution to that problem?
How can I structure my application correctly?
thanks a lot for suggestions.
MyLibraryTest looks as follow:
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional
public class MyLibraryTest {
#Autowired
private MyService service;
#Test
public void testMyService_Save() {...}
}
You need to make sure that the library module's pom.xml includes -
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>1.5.2.RELEASE</version>
</dependency>
which is required for the module to use the #SpringBootTest annotation. You might already be using the same in your app module but is not included in the library module.
Well post the Edit, found the question to be possibly a duplicate of Unable to find a #SpringBootConfiguration when doing a JpaTest
Also quoting from Thomas's answer from the same thread, here
The thing about #DataJpaTest and a few other annotations is that they
look for a #SpringBootConfiguration annotation in the current package,
and if they cannot find it there, they traverse the package hierarchy
until they find it.
For example, if the fully qualified name for your test class was
com.example.test.JpaTest and the one for your application was
com.example.Application, then your test class would be able to find
the #SpringBootApplication (and therein, the
#SpringBootConfiguration).
If the application resided in a different branch of the package
hierarchy, however, like com.example.application.Application, it would
not find it.
which seems to be the case for you, where you are trying to test an application in a different module itself. Hence the error that you see.

Invocation of MavenCli fails within a Maven plugin

I've created small util to wrap MavenCli, which generates a new Maven project, using the quickstart archetype.
When executing the Util as a unit test, it is working quite well (just generating an empty Maven project).
Now I want to integrate this small wrapper into a Maven plugin. But when I execute the mojo (within a third Maven project), the invocation of MavenCli fails with exception:
[ERROR] Error executing Maven.
[ERROR] java.util.NoSuchElementException
role: org.apache.maven.eventspy.internal.EventSpyDispatcher
roleHint:
[ERROR] Caused by: null
The util looks like:
public void createProject() {
final MavenCli cli = new MavenCli();
System.setProperty("maven.multiModuleProjectDirectory", "/usr/share/maven");
cli.doMain(new String[] { "archetype:generate", "-DgroupId=com.my.company",
"-DartifactId=hello-world", "-DarchetypeArtifactId=maven-archetype-quickstart",
"-DinteractiveMode=false" }, "/tmp", System.out, System.out);
}
relevant dependency of the util:
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
<version>3.3.9</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.3.9</version>
</dependency>
The mojo code looks like:
#Mojo(name = "custommojo", requiresProject = false)
public class CustomMojo extends AbstractMojo {
#Override
public void execute() throws MojoExecutionException, MojoFailureException {
Util.createProject();
}
}
The POM of the mojo just includes dependencies to relevant Maven artifacts (plugin-api, plugin-annotation, etc.) and the util.
The third project I mentioned, is an empty "maven-quickstart" project which have dependency to the mojo-project and a configuration for the mojo to execute in compile phase.
I have no idea why it works in context of unit test, but not in context of a mojo.
Can anybody help?
This is a class loading issue.
MavenCli will try to load classes from the context classloader of the current thread. Inside of a Maven plugin, there is a special, restricted, classloader, which has access to:
its own classes;
the classes used in its dependencies block;
exported classes as part of a possible build extension of the project;
exported classes from the Maven core and core extensions;
and has the bootstrap classloader as parent.
However, the specific class org.apache.maven.eventspy.internal.EventSpyDispatcher is part of Maven core, but it is not part of the exported APIs (the package org.apache.maven.eventspy is not listed as an exportedPackage). So the plugin cannot load that class. This is also why it works in your tests: you're not inside of a plugin, so the classloader is different and has access to that class.
You won't even be able to get away with adding explictly a dependency on maven-core for the plugin: it will be disgarded since it is supposedly already provided.
There are 2 solutions here:
Don't use the Maven Embedder API from within a plugin, but the Invoker API. The difference between the two is that the Invoker API will launch Maven in a clean environment, completely distinct with regard to the current one. Since it starts everything anew, you won't have any classloading issue.
Use the mojo-executor library, that provides an easy way to invoke other Mojos from within a Maven plugin. You could use it here to invoke the archetype:generate Mojo.
This works for me inside a custom maven plugin (using Maven 3.5.0):
ClassRealm classRealm = (ClassRealm) Thread.currentThread().getContextClassLoader();
MavenCli cli = new MavenCli(classRealm.getWorld());
cli.doMain( ... );
The plexus Launcher sets the context class loader to its ClassRealm, which has access to the "global" ClassWorld.
Not sure how stable that solution is, but so far looking good.
Used imports:
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.apache.maven.cli.MavenCli;
#Alexander:
You answer put me in the right direction, I was getting the mentioned error when trying to run to doMain maven commands subsequently (one command would succeed).
However your code gives me a ClassCast exception :
Exception in thread "main" java.lang.ClassCastException: sun.misc.Launcher$AppClassLoader cannot be cast to org.codehaus.plexus.classworlds.realm.ClassRealm
at com.misys.tools.integration.codegen.cli.cmd.InstallApi.mavenInstallMchApi(InstallApi.java:58)
at com.misys.tools.integration.codegen.cli.cmd.InstallApi.run(InstallApi.java:50)
at com.misys.tools.integration.codegen.cli.OpenApiGen.main(OpenApiGen.java:26)
I managed to rewrite the code to:
MavenCli cli = new MavenCli(new ClassWorld("maven",Thread.currentThread().getContextClassLoader()));
And then 2 subsequent doMain invocations of embedded maven succeed!

Spring import config from another project, transient dependencies, ClassNotFound

Here's the situation. I have 2 projects.
Project A
Imports mongo-java-driver
Has a ConfigurationA class that defines a Bean like so:
public #Bean MongoDatabase getMongoDatabase() {
MongoClient client = new MongoClient();
return client.getDatabase("mydb");
}
Project B
Imports Project A
<dependency>
<groupId>com.project</groupId>
<artifactId>project-a</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
Has a ConfigurationB class that imports ConfigurationA
Does not have import for mongo-java-driver
When I try to run project B I am getting the following error:
java.lang.IllegalStateException: Could not evaluate condition on org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration#propertySourcesPlaceholderConfigurer due to internal class not found. This can happen if you are #ComponentScanning a springframework package (e.g. if you put a #ComponentScan in the default package by mistake)
...
Caused by: java.lang.NoClassDefFoundError: com/mongodb/client/MongoDatabase
If I add an explicit import to Project B for mongo-java-driver, it works fine.
My question is: Why do I need the import in Project B? Is there anything I can do to prevent this? I don't want Project B to know about the dependencies in Project A.

Categories