Using Java Compiler API to compile multiple java files - java-compiler-api

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

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...

Adding Apache POI 4.0.1 library not sufficient to use XSSFWorkbook

I am using the following tutorial to realize a Selenium Keyword Driven Framework : http://toolsqa.com/selenium-webdriver/keyword-driven-framework/set-excel-apache-poi/
For the part which ask to create an "util" package with an ExcelUtils class, I followed the instructions which begin by adding a jar to the project libraries.
This jar is for the library apache-poi-4.0.1 : poi-4.0.1.jar.
But even with this library and it's attached source, classes XSSFWorkbook, XSSFSheet and XSSFCell do not exist.
So my question is, which part of the tutorial I am missing? Or which library I am missing?
I am using Eclipse Oxygen with the JRE JavaSE-1.8
Package utils;
import java.io.FileInputStream;
public class ExcelUtils {
private static XSSFSheet ExcelWSheet;
private static XSSFWorkbook ExcelWBook;
private static XSSFCell Cell;
//This method is to set the File path and to open the Excel file
//Pass Excel Path and SheetName as Arguments to this method
public static void setExcelFile(String Path,String SheetName) throws Exception {
FileInputStream ExcelFile = new FileInputStream(Path);
ExcelWBook = new XSSFWorkbook(ExcelFile);
ExcelWSheet = ExcelWBook.getSheet(SheetName);
}
//This method is to read the test data from the Excel cell
//In this we are passing parameters/arguments as Row Num and Col Num
public static String getCellData(int RowNum, int ColNum) throws Exception{
Cell = ExcelWSheet.getRow(RowNum).getCell(ColNum);
String CellData = Cell.getStringCellValue();
return CellData;
}
}
You are missing the below piece of code
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
I finally found a solution.
I had to download 5 other libraries :
poi-examples-4.0.1
poi-excelant-4.0.1
poi-ooxml-4.0.1
poi-ooxml-schemas-4.0.1
poi-scratchpad-4.0.1
After that, I can use XSSF classes.
You need the poi-ooxml dependency as well.
This is how it looks in Gradle, just change $apachePoiVersion to the version you want.
implementation "org.apache.poi:poi:$apachePoiVersion"
implementation "org.apache.poi:poi-ooxml:$apachePoiVersion"

Pass dynamic value to test method parameter using TestNG class

I am automating a web page which runs in multi-threading environment, so I am exporting every test result into a file system and I wanted to maintain every test result uniquely for the future reference. So is there a way to pass file name as parameter to a test method dynamically while calling it from TestNG class.
I know we can pass parameters from .xml file but if I do that the values will more like static and can be seen by all the thread running parallel.
Test class will be called from main method as bellow
public class Test {
public static void main(String[] args) throws ParseException {
try
{
TestNG testng = new TestNG();
testng.setTestClasses(new Class[] { Testing.class });
testng.run();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Bellow code is my test method
public class Testing {
#Test
#Parameters("filename")
public void testMethod(String fileName){
System.out.println("filename is: "+fileName);
// ---- remaining test logic -----
}
}
Or can we use TestListenerAdapter onStart() method to inject parameter values...?.
If you want unique file name you can just add it a time stamp
Date date = new Date();
Format formatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
String timeStamp = formatter.format(date);
String fileName = "TestResults-" + timeStamp;
You can store your values into ITestContext which will be available for all tests.
You can set up the values in a configuration method (#BeforeSuite for example) or a listener.
Pass Dynamic Parameters to TestNG suite during runtime
What the below code does:
I want to add a list of parameters to each test during runtime. These parameters are passed as maven runtime arguments. They are read using System.getProperty() method as shown below. Then these parameters are added to the test inside suite and testng is ran successfully. This can be really useful in other scenarios as well.
The below code reads the testng.xml file and adds parameter to
List<String> parameters = new ArrayList<>();
parameters = Arrays.asList(System.getProperty("parameters").split(",");
TestNG tng = new TestNG();
File initialFile = new File("testng.xml");
InputStream inputStream = FileUtils.openInputStream(initialFile);
Parser p = new Parser(inputStream);
List<XmlSuite> suites = p.parseToList();
for(XmlSuite suite:suites){
List<XmlTest> tests = suite.getTests();
for (XmlTest test : tests) {
for (int i = 0; i < parameters.size(); i++) {
HashMap<String, String> parametersMap = new HashMap<>();
parametersMap.put("parameter",parameters.get(i));
test.setParameters(parametersMap);
}
}
}
tng.setXmlSuites(suites);
tng.run();

jbehave run only specific story

I have jbehave integrated with Selenium. I am running my tests through command line as below
C:\eclipse_workspace\MySeleniumTests>mvn clean test -Dwebdriver.firefox.bin="C:\Program Files\Mozilla\Firefox\firefox.exe"
I have used jbehave-maven-plugin. Maven picks up all the Embedder impl (JunitStories in my case) from the source directory and execute them one by one. Configuration for that is <include>**/*Stories.java</include> in pom.xml
It then looks for relevant .story files in the specified dir and executes them. Say, I have two story files one.story and two.story, both of them are executed.
Over a time, number of story files are going to increase I only want to execute specific story files should there be a way to do this? I am thinking to pass specific story file names as run time parameters but don’t know what is required to make that happen.
I got it working with the below code
mvn clean test -Dwebdriver.firefox.bin="C:\Program Files\Mozilla\Firefox\firefox.exe" -Dstory=myStory.story
Override storyPaths() method in embedder class as below.
public class MyTestStories extends JUnitStories /* InjectableEmbedder */{
#Override
protected List<String> storyPaths() {
List<String> storiesToRun = new ArrayList<String>();
String storyProperty = System.getProperty("story");
if (storyProperty == null || storyProperty.isEmpty()) {
throw new RuntimeException("Please specify which stories to run");
}
String[] storyNames = storyProperty.split(",");
StoryFinder sf = new StoryFinder();
URL baseUrl = CodeLocations.codeLocationFromClass(this.getClass());
for (String storyName : storyNames) {
storiesToRun.addAll(sf.findPaths(baseUrl, storyName, ""));
}
return storiesToRun;
}
Try the following:
mvn clean test -Dwebdriver.firefox.bin="C:\Program Files\Mozilla\Firefox\firefox.exe" -Djbehave.story.name=<story filename without extension (wildcards are supported)>
You should also use custom test suite implementation:
public abstract class JBehaveTestSuite extends ThucydidesJUnitStories {
private static final String STORY_NAME_PATTERN = "**/${jbehave.story.name:*}.story";
public JBehaveTestSuite() {
findStoriesCalled(storyNamesFromEnvironmentVariable());
}
#Override
public void run() throws Throwable {
super.run();
}
private String storyNamesFromEnvironmentVariable() {
return SystemPropertyUtils.resolvePlaceholders(STORY_NAME_PATTERN);
}
}

eclipse RCP UI through java code

Can I implement Eclipse RCP UI using java code only and not plugin.xml?
While it might be possible in theory (eclipse plugins are OSGi bundle which are read by the extension registry), I don't think it is practical (unless you re-implement the extension registry lifecycle).
Eclipse Equinox precisely extends the concept of bundles with the concept of extension points, hence the mandatory presence of plugin.xml.
You can programmatically add and remove extensions. See following example methods (adapt on demand):
public void addExtension() throws UnsupportedEncodingException {
String pluginXmlAsString = "<a string with the content of plugin.xml";
InputStream pluginXmlIs = new ByteArrayInputStream(pluginXmlAsString.getBytes(StandardCharsets.UTF_8.name()));
IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
Object token = ((ExtensionRegistry) extensionRegistry).getTemporaryUserToken();
IContributor contributor = ContributorFactoryOSGi.createContributor(Platform.getBundle("org.acme.mybundle"));
extensionRegistry.addContribution(pluginXmlIs, contributor, false, null, null, token);
}
public static void removeExtensionsContributedByMe() {
String extensionPointId = "<ID of the extension point for remove an extension of";
String extensionContributor = "org.acme.mybundle";
ExtensionRegistry extensionRegistry = (ExtensionRegistry) Platform.getExtensionRegistry();
IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(extensionPointId);
IExtension[] extensions = extensionPoint.getExtensions();
Object token = extensionRegistry.getTemporaryUserToken();
for (IExtension extension : extensions) {
if (extensionContributor.equals(extension.getContributor().getName())) {
extensionRegistry.removeExtension(extension, token);
}
}
}
We use this for unit tests which add extensions as preparation and remove extension to clean up. This way the tests do not influence each other (which would be the case if the extensions are "hard coded" in plugin.xml or fragment.xml).