Resource injection in Spring Boot not working with gradle - spring

I have a Configuration bean.
#Component
public class Config {
#Value("classpath:${app.file.name.srgbicc}")
public Resource icc;
#PostConstruct
void init(){
try {
tempdir = Files.createTempDir();
newIcc = copyIccFileToTemp();
}catch (IOException e){
throw new RuntimeException(e);
}
}
private File copyIccFileToTemp() throws IOException {
File tempIcc = new File(tempdir, icc.getFilename());
FileUtils.copyFile(icc.getFile(),tempIcc);
return tempIcc;
}
}
On icc.getFile() there is a FileNotFoundException
application.properties
app.file.name.srgbicc = sRGB.icc
I looked in my classpath and found the following situation:
build/classes/main (no icc file)
build/resources/main (icc file, application.properties)
When printed out my classpath during application start I only found ...myApp/build/classes/main.
No ../build/resources/main or ../src/main/resources entries there.
So I was wondering why is the resources not on the classpath?
according to http://docs.spring.io/spring-boot/docs/1.1.5.RELEASE/reference/htmlsingle/#build-tool-plugins-gradle-running-applications
this should be.
Of course if I put the icc file in build/classes/main its all working as expected, but it is not supposed to be there. right?
I tried to run the application with gradle run or gradle bootRun in intellij I use static main. The good thing is that the application fails consistently independent of how I start it.
My Project layout
myApp
src
main
java
pkg
Config.java
resources
sRGB.icc
application.properties
For the reference:
IntelliJ 13
spring-boot 1.1.5
Fgradle 2.0
Update
Here is a stripped down example of my code
https://gist.github.com/Vad1mo/bb5d23ca251341236fbd
I removed #PostConstruct in the config.init and run the application with gradle build all test are success when i rund from intellij the tests fail. this is strange that i know have different behavior

I solved the problem now.
What helped me was just to do a simple clean build and rebuild project in intellij. I was using to much eclipse and maven so I expected it to happen automagically.

Related

spring-boot-starter-freemarker does not find templates

Using spring-boot-starter-freemarker without further config I would expect to be able to load templates from the default template path(src/resources/templates) (note it's src/... not build/...).
Having this file here:
src/resources/templates/emails/welcome.ftl
Trying to load it as a template:
// some service class
#Autowired
private Configuration freemarkerConfig;
public void doStuff() {
Template t = freemarkerConfig.getTemplate("emails/welcome.ftl");
// ...
}
Fails with this error message:
freemarker.template.TemplateNotFoundException: Template not found for name "emails/welcome.text.ftl".
The name was interpreted by this TemplateLoader: MultiTemplateLoader(loader1 = FileTemplateLoader(baseDir="/some/path/backend/build/resources/main/templates", canonicalBasePath="/some/path/backend/build/resources/main/templates/"), loader2 = ClassTemplateLoader(resourceLoaderClass=org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer, basePackagePath="" /* relatively to resourceLoaderClass pkg */)).
So the configuration seems kind of ok-ish, but instead of the src folder it is using the build folder. When running via./gradlew bootRun we see the error. Doing a ./gradlew buildand then ./gradlew bootRun the templates are found - because they are now in the build folder. But for development it would be much appreciated to not require a full re-build.
So, I know we now could configure freemarker manually to load from the src folder, but that feels hacky.
Am I doing something wrong or is this expected behavior?
You can configure bootRun so that sources are loaded from their source location. Assuming that the templates are part of the main source set, i.e. they're in src/main/resources, the configuration would be the following:
bootRun {
sourceResources sourceSets.main
}
This is described in the reference documentation for Spring Boot's Gradle plugin.

Need to copy flink-hadoop-compatibility-2.10 jar explicitly to ${FLINK-HOME}/lib location on EMR cluster

I am currently working on an Flink application that uses some of the Hadoop dependencies to write the data to S3 location. On local environment it is working fine, however when I deploy this Flink application on EMR cluster it throws an exception related to compatibility issue.
The error message that I am getting is
java.lang.RuntimeException: Could not load the TypeInformation for the class 'org.apache.hadoop.io.Writable'. You may be missing the 'flink-hadoop-compatibility' dependency.
at org.apache.flink.api.java.typeutils.TypeExtractor.createHadoopWritableTypeInfo(TypeExtractor.java:2025)
at org.apache.flink.api.java.typeutils.TypeExtractor.privateGetForClass(TypeExtractor.java:1649)
at org.apache.flink.api.java.typeutils.TypeExtractor.privateGetForClass(TypeExtractor.java:1591)
at org.apache.flink.api.java.typeutils.TypeExtractor.createTypeInfoWithTypeHierarchy(TypeExtractor.java:778) ....
I have included the maven dependency of flink-hadoop-compatibility-2.10 jar in POM dependency. But it is not detecting it. The Flink version I am using is 1.2.0
However, when I explicitly copy the compatibility JAR to the ${FLINK-HOME}/lib location, I am not getting any exception and able to run the Flink application successfully.
Is there any way that we can use, so that without deploying the JAR file to ${FLINK-HOME}/lib we can run the application?
OR
What modifications required in POM dependencies, so that the application will detect it and it is not required to copy the compatibility JAR to flink-home/lib location?
package org.apache.flink.api.java.typeutils;
public class TypeExtractor {
/** The name of the class representing Hadoop's writable */
private static final String HADOOP_WRITABLE_CLASS = "org.apache.hadoop.io.Writable";
private static final String HADOOP_WRITABLE_TYPEINFO_CLASS = "org.apache.flink.api.java.typeutils.WritableTypeInfo";
// visible for testing
public static <T> TypeInformation<T> createHadoopWritableTypeInfo(Class<T> clazz) {
checkNotNull(clazz);
Class<?> typeInfoClass;
try {
typeInfoClass = Class.forName(HADOOP_WRITABLE_TYPEINFO_CLASS, false, TypeExtractor.class.getClassLoader());
}
catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load the TypeInformation for the class '"
+ HADOOP_WRITABLE_CLASS + "'. You may be missing the 'flink-hadoop-compatibility' dependency.");
}
...
}
}
This is because org.apache.hadoop.io.Writable is mean to be loaded by TypeExtractor.class.getClassLoader() which is AppClassLoader, and the submited flink jar is loaded by ParentFirstClassLoader, which is the child of AppClassLoader, so AppClassLoader can not load org.apache.hadoop.io.Writable from your flink jar.
I'm not sure if it's a bug, change to classLoader to Thread.currentThread().getContextClassLoader() will make it work without copy the flink-hadoop-compatibility jar file to ${FLINK-HOME}/lib location.
After looking into various posts and experimenting with POM files, I think with current version of Apache Flink (1.2.0) it is required to copy (deploy) the JAR file to ${FLINK-HOME}/lib location.

Using JavaCompiler API in a Spring Boot app - unable to properly set the compilation classpath

We have an app that's been migrated from a traditional WAR Spring web application to what we had hoped would be a modern Spring Boot executable Jar.
One of the app modules uses the JavaCompiler API to generate Java code and compile it in runtime.
The generated code requires dependencies that reside in the web app classpath, and so we had roughly the following code:
StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);
List<String> optionList = new ArrayList<String>();
// set compiler's classpath to be same as the runtime's
String classpathString = System.getProperty("java.class.path");
optionList.addAll(Arrays.asList("-nowarn",
"-classpath",
classpathString,
"-g",
"-source",
javaVersion,
"-target",
javaVersion));
Collection classpathFiles = getClasspathJars(classpathString);
addPreviousCompiledClassesToClasspathFiles(rootClassPath, classpathFiles);
try {
File file = new File(getTargetRoot(tempClassPath));
file.mkdirs();
standardFileManager.setLocation(StandardLocation.CLASS_OUTPUT, Lists.newArrayList(file));
standardFileManager.setLocation(StandardLocation.CLASS_PATH, classpathFiles);
// adding current dir to the source path, to find the compiled DV
if (generateSeparateJarsForEachDecision.equals(NO_JAR)) {
standardFileManager.setLocation(StandardLocation.SOURCE_PATH, Lists.newArrayList(file));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
List<CharSequenceJavaFileObject> compilationUnits = Lists
.newArrayList(new CharSequenceJavaFileObject( StringUtils.capitalize(filename),
toString(javaCode)));
JavaCompiler.CompilationTask task = compiler
.getTask(writer, standardFileManager, listener, optionList, null, compilationUnits);
status = task.call();
```
However, that doesn't work with Boot.
The classpath only contains my-app.jar, and I can't add any of the nested jars to the -cp of the task - it just won't those nested jars.
I've also attempted to manually add the to the -cp parameter them like so: {absolute-path}/my-app.jar!/BOOT-INF/lib/my-dep.jar
{absolute-path}/my-app.jar!/BOOT-INF/lib/*.jar
None of those worked.
Thought about using the <requiresUnpack> tag on build too, but that didn't seem to help because I couldn't get a hold of the expanded dir in order to add it to the classpath.
Faced similar issue.
Looks like a limitation with spring boot.
So created a standalone application with Jersey and Tomcat embedded.
Now jar contains all libraries and able to set it as class path to Java Compiler

Spring does not find WebApplicationInitializer when packaged in a fat Jetty jar

I am trying to create a single "FAT" jar of embedded Jetty and my code. Unfortunatly if I run from the jar I get this error: No Spring WebApplicationInitializer types detected on classpath
If I unpack the jar it works just fine.
The question is can I run the app from the jar or does it have to be unpacked?
Here is a sample project and the steps to reproduce the issue:
git clone https://github.com/steveliles/jetty-embedded-spring-mvc-noxml.git
cd jetty-embedded-spring-mvc-noxml
mvn clean install
cd target
java -jar jetty-noxml-1.0-SNAPSHOT.jar
You will get this output "...No Spring WebApplicationInitializer types detected on classpath...".
but if you do this:
mkdir temp
cd temp
unzip jetty-noxml-1.0-SNAPSHOT.jar
java -cp . com.sjl.Main
This will work just fine.
So what can be done to make this work directly from the jar without unpacking it first?
== Update ==
The author of the project resolved the issue. The following code was needed to pickup webapps from a shaded jar:
parser = new AnnotationParser() {
#Override
public void parse(Resource aDir, ClassNameResolver aResolver) throws Exception {
if (aDir.isDirectory()) {
super.parse(aDir, aResolver);
} else {
super.parse(aDir.getURI(), aResolver);
}
}
};
See: https://github.com/steveliles/jetty-embedded-spring-mvc-noxml/commit/789663310b2fa2bdc0b101658275758a26cec229

JMS and ActiveMQ exception

I'm trying a project for school using JMS and ActiveMQ.
I copied the block of code from O'Reilly's books "Java Message Service 2nd Edition Jun 2009". It uses the publish and subscribe method and is in fact a small chat where everyone connected to the topic can send messages to everyone and everyone can see everyone else's messages. I compile the program and everything is ok, i try to run it and it gives me the following exception:
Exception in thread "main" javax.naming.NoInitialContextException: Cannot instantiate class: org.apache.activemq.jndi.ActiveMQInitialContextFactory [Root exception is java.lang.ClassNotFoundException: org.apache.activemq.jndi.ActiveMQInitialContextFactory]
I found that this problem might be because of 2 reasons:
activemq-all-5.2.0.jar is not added to classpath.
BUT added it the classpath (EnvironmentVariables->select ClassPath->Edit and add the following: "D:\Programming\JMS\ActiveMQ\apache-activemq-5.2.0" (THIS IS HOW YOU ADD IT NO?!?!)
jndi.properties file is not defined properly or has not been added to the classpath.
BUT i CREATED IT and added it's folder to the classpath. Here is what it contains:
java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url = tcp://localhost:61616
java.naming.security.principal=system
java.naming.security.credentials=manager
connectionFactoryNames = TopicCF
topic.topic1 = jms.topic1
What is the problem? I have tried for ages to make it work. Am i doing something wrong? :(
Does the jndi.properties file path matter? or it only has to be placed in classpath and from here it can be found?
I also ran the activemq.bat from the bin folder D:\Programming\JMS\ActiveMQ\apache-activemq-5.2.0\bin\
[Edit]---------------------
So it works in Eclipse, BUT
Now i've properly added the .jar file in environment variables and i've run the client from windows's cmd. It doesn't give any errors, when i write in Eclipse's console, it appears in cmd console, everything ok, but when i try to write in cmd it gives an error at this line:
publisher.publish(message);
and it says
java.lang.NoSuchMethodError: org.apache.activemq.ActiveMQMessageProducerSupport.getDestination()Ljavax/jms/Destination;
Any ideas? I'd really like to be able to run it in CMD. :(
---------------------[/Edit]
Well I'm on Linux right now, but I bet it has to be:
D:\Programming\JMS\ActiveMQ\apache-activemq-5.2.0.jar
Also, if you run it with Eclipse and go to Project -> Build Path and this jar then there shouldn't be any problems. Anyhow can you post the CLASSPATH variable?
EDIT
I can't help you if you can't help me. This is related to any other future questions or work in general, provide details - it is always helpful. Will be much helpful if you would provide the EXACT command that you are running in CMD and the code of the class where this happens.
java.lang.NoSuchMethodError
generally it means that the jar is in place, class also, BUT the method is not. It happens when you compile with one version of the jar and at runtime provide a jar where this method was removed, thus the JRE can't find it throwing the error.
I just tested on my computer
I do not understand why it does not work for you, but it does for me. Here is my class:
package com.test;
public class Publisher {
public static void main(String[] args) {
try{
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = factory.createConnection();
ActiveMQSession session = (ActiveMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic destination = session.createTopic("FOO.TEST");
TextMessage textMessage = session.createTextMessage("Sample Payload");
TopicPublisher publisher = session.createPublisher(destination);
publisher.publish(textMessage);
session.close();
connection.close();
} catch(Exception e){
e.printStackTrace();
}
}
}
Everything is fine if I run it from eclipse with one single dependency in Maven:
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.2.0</version>
Then I do it with java and javac
javac -classpath /home/eugen/.m2/repository/org/apache/activemq/activemq-core/5.2.0/activemq-core-5.2.0.jar:/home/eugen/.m2/repository/javax/jms/jms/1.1/jms-1.1.jar Publisher.java
Notice that the only thing I added is the two jars.
Then java:
java -classpath /home/eugen/.m2/repository/org/apache/activemq/activemq-core/5.2.0/activemq-core-5.2.0.jar:/home/eugen/.m2/repository/commons-logging/commons-logging-api/1.1/commons-logging-api-1.1.jar:/home/eugen/.m2/repository/org/apache/camel/camel-core/1.5.0/camel-core-1.5.0.jar:/home/eugen/workspace/t/src/main/java/:/home/eugen/.m2/repository/javax/jms/jms/1.1/jms-1.1.jar:/home/eugen/.m2/repository/org/apache/geronimo/specs/geronimo-j2ee-management_1.0_spec/1.0/geronimo-j2ee-management_1.0_spec-1.0.jar com.test.Publisher
I added a few needed jars to the classpath and run it - it works perfectly.
Cheers, Eugene.
I ran into the same issue and it was a space (or what appeared to be a space) at the end of my property config.
java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
Also note that you don't necessarily have to embed the jar file into your client code. Simply including the activemq-all as a maven dependency will work as well.

Resources