How to resolve a Maven project? - javaparser

I have a Maven project using lombok and other external dependencies. I would like to add it to the resolver so that I can work on any of the generated Java files. So far I have tried JavaParserTypeSolver and JarTypeSolver and even tried delomboking the entire codebase, however even with that the external dependencies are not getting resolved.
I feel it's a very common setup (Maven project + lombok + other depencencies), is there a way to easily set-up the resolver for this?
Exception in thread "main" UnsolvedSymbolException{context='org.slf4j.LoggerFactory.getLogger(MyClass.class)', name='org.slf4j.LoggerFactory', cause='UnsolvedSymbolException{context='null', name='LoggerFactory', cause='null'}'}
at com.github.javaparser.symbolsolver.javaparsermodel.contexts.AbstractJavaParserContext.findTypeDeclarations(AbstractJavaParserContext.java:222)
at com.github.javaparser.symbolsolver.javaparsermodel.contexts.MethodCallExprContext.solveMethod(MethodCallExprContext.java:166)
at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.solve(JavaParserFacade.java:317)
at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.solve(JavaParserFacade.java:179)
at com.github.javaparser.symbolsolver.JavaSymbolSolver.resolveDeclaration(JavaSymbolSolver.java:161)
at com.github.javaparser.ast.expr.MethodCallExpr.resolve(MethodCallExpr.java:317)
at org.javaparser.examples.chapter2.MyMethodPrinter.lambda$main$0(MyMethodPrinter.java:38)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.javaparser.examples.chapter2.MyMethodPrinter.main(MyMethodPrinter.java:37)
Caused by: UnsolvedSymbolException{context='null', name='LoggerFactory', cause='null'}
at com.github.javaparser.symbolsolver.javaparsermodel.TypeExtractor.visit(TypeExtractor.java:279)
at com.github.javaparser.symbolsolver.javaparsermodel.TypeExtractor.visit(TypeExtractor.java:71)
at com.github.javaparser.ast.expr.FieldAccessExpr.accept(FieldAccessExpr.java:90)
at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.getTypeConcrete(JavaParserFacade.java:547)
at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.getType(JavaParserFacade.java:394)
at com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.getType(JavaParserFacade.java:376)
at com.github.javaparser.symbolsolver.javaparsermodel.contexts.AbstractJavaParserContext.findTypeDeclarations(AbstractJavaParserContext.java:213)
public class MyMethodPrinter {
private static final String FILE_PATH = "/Users/john.doe/myproj1/src-delomboked/main/java/com/somepackage/MyClass.java";
public static void main(String[] args) throws Exception {
TypeSolver myTypeSolver = new CombinedTypeSolver(
new ReflectionTypeSolver(),
new JavaParserTypeSolver("/Users/john.doe/myproj1/target/generated-sources/delombok"),
new JarTypeSolver("/Users/john.doe/myproj1/target/myproj1-1.0.0.jar")
);
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(myTypeSolver);
StaticJavaParser
.getConfiguration()
.setSymbolResolver(symbolSolver);
CompilationUnit cu = StaticJavaParser.parse(new File(FILE_PATH));
cu.findAll(MethodCallExpr.class).forEach(mce ->
System.out.println(mce.resolve().getQualifiedSignature()));
}

Related

How to create a Gradle task of type KotlinCompile

I'm trying to compile generated Kotlin source code in a custom location to a custom location so that I can build a jar file with those class file only.
I had no issues setting it up for Java. Unfortunately, I'm having problems with Kotlin.
So here is the Kotlin version of what worked for me in Java:
public class MyCustomPlugin implements Plugin<Project> {
private static final String GENERATED_STUB_CLASSES_DIRECTORY = "generated-stub-classes";
private static final String GENERATED_STUB_SOURCES_DIRECTORY = "generated-stub-sources";
public void apply(Project project) {
project.getPluginManager().apply(KotlinPluginWrapper.class);
createCompileStubsTask(project);
}
private void createCompileStubsTask(final Project project) {
KotlinCompile compileKotlin = (KotlinCompile) project.getRootProject().getTasksByName("compileKotlin", true).iterator().next();
TaskProvider<KotlinCompile> compileKotlinStubs = project.getTasks().register("compileStubs", KotlinCompile.class,
compileStubs -> {
File stubsClassesDir = new File(project.getBuildDir() + "/" + GENERATED_STUB_CLASSES_DIRECTORY);
stubsClassesDir.mkdirs();
compileStubs.setClasspath(compileKotlin.getClasspath());
compileStubs.source(project.getLayout().getBuildDirectory().dir(GENERATED_STUB_SOURCES_DIRECTORY));
compileStubs.getDestinationDirectory().set(stubsClassesDir);
});
compileKotlin.finalizedBy(compileKotlinStubs);
}
}
This fails with:
Unable to determine constructor argument #1: missing parameter of type KotlinJvmOptions, or no service of type KotlinJvmOptions.
I tried to do it in the build.gradle file, like this:
task compileStubs(type: org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
File stubsClassesDir = new File(project.getBuildDir().name + "/generated-stub-classes")
stubsClassesDir.mkdirs()
compileStubs.setClasspath(compileKotlin.getClasspath())
compileStubs.source(project.getLayout().getBuildDirectory().dir("generated-stub-sources"))
compileStubs.getDestinationDirectory().set(stubsClassesDir)
}
compileKotlin.finalizedBy(compileKotlinStubs)
But the result is exactly the same.
Please help...

Is there any way of getting version from build.gradle of a kotlin project?

I want to get the version of my project inside my project which I have set in build.gradle. Is there any method to get the version inside my project as I need to show the version inside my software and used for compairing update info, so that I don't need to change twice every time I release a update. Is there any way to make it?
group 'ProjectName.group'
version "ProjectVersion"
You typically do that by loading a properties file, and configuring gradle to filter your properties file in the processResources task.
Example:
build.gradle:
version = '1.5.0'
processResources {
def properties = ['version': project.version]
inputs.properties(properties)
filesMatching('version.properties') {
expand(properties)
}
}
version.properties:
version=${version}
App.java:
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.load(App.class.getResourceAsStream("/version.properties"));
System.out.println(properties.getProperty("version"));
}

How to share the camel context between 2 different applications or war's

I have created 2 different application and started the camel context in one of them. How do I use this already started context in the second application ?
I tried getting the context by using lookUpByname() and binding camel context with jndi context but could on load the existing context.
Also tried by setting NameStrategy in context in application 1 and getting the same in application 2 but looks like camel auto generates name and prefix in DefaultCamelContextNameStrategy.
code snippet:
Application 1 :
public static void main(String[] args)
{
CamelContext ctx = new DefaultCamelContext();
String camelContextId= "sample";
ctx.setNameStrategy(new DefaultCamelContextNameStrategy(
camelContextId));
ctx.start();
}
Application 2:
public static void main(String[] args)
{
sampleRouter testobj = new sampleRouter();
testobj.test();
}
public class sampleRouter extends RouteBuilder
{
public static CamelContext camelContext;
public void test()
try
{
camelContext = getContext();
try {
camelContext.stop();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Please guide me to get the already started context in different applications as I want to avoid creating a new context every time.
Why do you want to avoid having multiple CamelContexts? What goal are you trying to accomplish?
Without a clear requirement it's not easy to help you, however I'll try and suggest a couple of ideas.
Looking at your code you are using two different JVMs, since you have 2 main methods.
If your applications run in different JVMs, use a JMS Message Broker like ActiveMQ as communication layer.
If you deploy 2 wars / applications in the same JVM, you can use two CamelContexts and have them communicate through VM endpoints, like seda-vm and direct-vm.

Arquillian ShrinkWrap how to add an asset to the file system path

I am importing a library that reads from the file system instead of my web archive's resource folder. I want to be able to essentially mock that file by adding an asset with that path using ShrinkWrap, so I can run tests on my build server without guaranteeing the file system has all these files. I tried to add a String Asset in the appropriate path, but the code can't find that asset. Here's an example of what I'm trying to achieve.
Rest Resource
#Path("/hello-world")
public class HelloWorldResource {
#GET
public Response getHelloWorld(){
return Response.ok(getFileContent()).build();
}
private String getFileContent() {
StringBuilder builder = new StringBuilder();
try {
BufferedReader bufferedReader = new BufferedReader(
new FileReader(
"/usr/myFile.txt"));
String line = bufferedReader.readLine();
while (line != null) {
builder.append(line);
line = bufferedReader.readLine();
}
}
catch (Exception e) {
e.printStackTrace();
}
return builder.toString();
}
}
Test
#RunWith(Arquillian.class)
public class HelloWorldResourceTest {
#Deployment
public static WebArchive createDeployment()
{
WebArchive webArchive = ShrinkWrap
.create(WebArchive.class)
.addPackages(true,
HelloWorldApplication.class.getPackage(),
HelloWorldResource.class.getPackage(),
Hello.class.getPackage())
.add(new StringAsset("Blah"),"/usr/myFile.txt")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
System.out.println("WebArchive: " + webArchive.toString(true));
return webArchive;
}
#Test
#RunAsClient
public void testHello(
#ArquillianResteasyResource("hello-world") final WebTarget webTarget)
{
final Response response = webTarget
.request(MediaType.APPLICATION_JSON)
.get();
String hello = response.readEntity(String.class);
System.err.println("Hello: " + hello);
Assert.assertEquals("Status is not OK", response.getStatus(), 200);
}
}
Web Archive toString
/WEB-INF/
/WEB-INF/classes/
/WEB-INF/classes/com/
/WEB-INF/classes/com/
/WEB-INF/classes/com/
/WEB-INF/classes/com/helloworld/
/WEB-INF/classes/com/helloworld/application/
/WEB-INF/classes/com/helloworld/application/HelloWorldApplication.class
/WEB-INF/classes/com/helloworld/resource/
/WEB-INF/classes/com/helloworld/resource/HelloWorldResourceTest.class
/WEB-INF/classes/com/helloworld/resource/HelloWorldResource.class
/WEB-INF/classes/com/helloworld/dataobjects/
/WEB-INF/classes/com/helloworld/dataobjects/Hello.class
/WEB-INF/beans.xml
/usr/
/usr/myFile.txt
I get the following error:
java.io.FileNotFoundException: /usr/myFile.txt (No such file or
directory)
Seems like ShrinkWrap is adding /usr/myFile.txt as a relative path within the archive instead of making it seem like /usr/myFile.txt is at the root directory of my file system. Is there any way I can get ShrinkWrap to do what I want?
Shrinkwrap is intended to create archives, so the API is scoped to create assets within the archive you are creating. If you want to have resources created in the regular filesystem simply use JDK, there is nothing Shrinkwrap could help you with.
Alternatively, if possible, change your resource to read resources from the classpath, not filesystem path. With this approach, you can easily swap content for the test using Shrinkwrap as you are trying now with your example.

Using Java Compiler API to compile multiple java files

Hi I have requirement to create ,compile and load java classes run time. Using FTL i am creating java source files , and able to compile the source if there is no dynamic dependency.
To elaborate with an instance, I have two java source file, one interface and its implementation class. I am able to compile the interface using java compiler api as follows
String classpath=System.getProperty("java.class.path");
String testpath =classpath+";"+rootPath+"/lib/is_wls_client.jar;"+rootPath+"/rtds_wls_proxyclient.jar;.;";
File javaFile = new File(javaFileName+".java");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
List<String> optionList = new ArrayList<String>();
optionList.addAll(Arrays.asList("-classpath",testpath));
StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null);
Iterable fileObjects = sjfm.getJavaFileObjects(javaFile);
JavaCompiler.CompilationTask task = compiler.getTask(null, null, null,optionList,null,fileObjects);
task.call();
sjfm.close();
I set class path for static classes which are already in the classpath , but this approach do not work for dynamically created classes? Any custom class loader will do the fix? My final implementation will be in web/app server
Any feedback will be highly appreciated
Satheesh
I was able to solve this issue by compiling all the java files together. Using FTL I generate the java classes, and then compile it using java compiler api and load classes with custom class loader
Java Complier
private void compile(File[] files) throws IOException{
String classpath=System.getProperty("java.class.path");
String rootPath=getServletContext().getRealPath("/");
System.out.println("--> root Path "+rootPath);
String testpath=classpath+";.;xx.jar;yy.jar";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
List<String> optionList = new ArrayList<String>();
optionList.addAll(Arrays.asList("-classpath",testpath));
// optionList.addAll(Arrays.asList("-d",rootPath+"/target"));
StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null);
Iterable fileObjects = sjfm.getJavaFileObjects(files);
JavaCompiler.CompilationTask task = compiler.getTask(null, null, null,optionList,null,fileObjects);
task.call();
sjfm.close();
}
Below code snippet shows how to use custom class loader
class CustomClassLoader extends ClassLoader {
public CustomClassLoader(ClassLoader parent) {
super(parent);
}
public Class findClass(String className,String path) {
byte[] classData = null;
try {
FileInputStream f = new FileInputStream(path);
int num = f.available();
classData = new byte[num];
f.read(classData);
} catch (IOException e) {
System.out.println(e);
}
Class x = defineClass(className, classData, 0, classData.length);
return x;
}
}
thanks
Satheesh