Class loading issue while running a Hadoop job - java

I have a C++ service, which exposes 2 interfaces:
a. Submit(): For submitting a DistCp job to YARNRM
b. Query(): For querying the status of the application.
This service internally calls a Java client (through JNI), which has 2 static functions:
Submit()
Query()
Submit() does:
DistCp distCp = new DistCp(configuration, distCpOptions);
Job job = distCp.execute();
Parses the "application ID" from the tracking URL and returns it.
Query() does:
Takes "application ID" returned in Submit()
YarnClient yarnClient = YarnClient.createYarnClient();
yarnClient.init(new YarnConfiguration());
yarnClient.start();
yarnClient.getApplicationReport(applicationID);
yarnClient.stop();
The problem I am facing is,
If the first call to the service is Submit(), then all the subsequent calls (both Submit() and Query()) SUCCEED
But, if the first call to the service is Query(), then all the Submit() calls FAIL.
Query() calls succeed under all the conditions.
The Submit() calls fail with errors (1st call, 2nd call and 3rd call below, with different exceptions):
java.util.ServiceConfigurationError: org.apache.hadoop.mapreduce.protocol.ClientProtocolProvider: Provider org.apache.hadoop.mapred.LocalClientProtocolProvider not found
java.util.ServiceConfigurationError: org.apache.hadoop.mapreduce.protocol.ClientProtocolProvider: Provider org.apache.hadoop.mapred.YarnClientProtocolProvider not found
java.io.IOException: Cannot initialize Cluster. Please check your configuration for mapreduce.framework.name and the correspond server addresses.
I debugged the issue and figured out that, when Query() API is called first, then classes LocalClientProtocolProvider and YarnClientProtocolProvider are not loaded. The class loader should load these classes, when Submit() is called. But, that is not happening.
I also observed that, when the Query() API is called first, the Hadoop configuration gets changed and contains lot many default settings related to "mapreduce.*" configuration.
I tried explicit loading using Class.forName(), as soon as the Submit() method is called. But, that did not help either.
When Submit() is called, why does not the class loader load the required classes? Is this the problem with Hadoop configuration or Java class loader? Or is it the problem because I am mixing MapReduce and Yarn APIs?
"mapreduce.framework.name" configuration is set to "yarn".
My environment is Hadoop 2.6.0.
My classpath contains, all the Hadoop jars present in following paths:
a. hadoop/common/
b. hadoop/common/lib
c. hadoop/hdfs/
d. hadoop/hdfs/lib
e. hadoop/mapreduce/
f. hadoop/mapreduce/lib
g. hadoop/yarn/
h. hadoop/yarn/lib

I figured out that, I am mixing Yarn and MapReduce APIs and that is causing class loading problems.
When Query() is called first, it loads all YARN related classes.
For e.g.:
org.apache.hadoop.yarn.client.api.YarnClient from file:/D:/data/hadoop-2
.6.0-SNAPSHOT/share/hadoop/yarn/hadoop-yarn-client-2.6.0-SNAPSHOT.jar
But, the MapReduce related classes are not loaded. For e.g., following class is not loaded:
org.apache.hadoop.mapred.YarnClientProtocolProvider from
file:/D:/data/hdoop-2.6.0-SNAPSHOT/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.6.0-SNAPSHOT.jar
So, when the Submit() is called, the class loader assumes that, it has loaded all the required classes. But, classes YarnClientProtocolProvider and LocalClientProtocolProvider are not loaded yet. Hence, the Submit() call fails.
To force the class loader to load all the MapReduce related classes, I added following statements in the constructor for YarnClientWrapper (which is a singleton class and wraps YarnClient).
Cluster cluster = new Cluster(configuration);
cluster.getFileSystem();
cluster.close();
This resolved the issue.
But, cleaner implementation would be to use MapReduce client in Query() instead of YarnClient. This will ensure that, we will not get into class loading issues.

Related

Tomcat update java file at runtime

I am building a dynamic Web Project (in Eclipse with Tomcat as server) using servlets and JSPs. The general purpose of the project is to let the user solve small code problems. In order to do that I take a method written by the user and use it to build a java file that I can run via Reflection. The problem I can't seem to figure out is that Tomcat (or Eclipse?) does not update the file at runtime. So when I create the file using the code of the current user and try to compile it, my program always executes the file as it was when I started the server using the code of the previous user. How can I tell it to update the file before running it?
Edit:
That's how I create the file:
public boolean writeFile() {
try {
PrintWriter writer = new PrintWriter(relativePath + "src\\testfiles\\TestFile.java");
writer.print(content);
writer.close();
return true; }...
Here I call the writer and try running the file:
FileWriter writer = new FileWriter(content);
if(writer.writeFile()){
Class<?> TestFile;
Method m;
try {
TestFile = cl.loadClass("testfiles.TestFile");
m = TestFile.getDeclaredMethod("testSolution");
m.invoke(null);
Thanks in advance!
Ok, it's now clear what the issue is. Your issue is not with Tomcat not reloading the file, but with the classloader not reloading the class.
Normal classloaders will only load a class once, and keep it cached forever. The only way for a class to get unloaded is by its classloader being garbage collected. To reload a class you either have to use a different classloader each time (with the previous one getting garbage collected or you'll run out of memory), or to have a custom loader thar doesn't cache.
See this article for an implementation of a custom classloader that does what you want.
You could theoretically just have a new class each time (by changing its name on each save) but you'd eventually run out of memory.
Maybe the easiest way is to instantiate a new classloader in the method itself, load a class, run the method on it, and don't keep any other references to the loader or the class. That way, you'll get a freshly loaded class each time, and the old instances of both classes and loaders will get garbage collected.
UPDATE: I was operating under the assumption that you already know how to compile a class at runtime but, based on the comments, that's not the case.
A classloader can, of course, only load a compiled class, so a source directly is not very useful.
Java internally provides a a compiler interface under javax.tools.JavaCompiler, but it's not easy to use and requires a different handling of Java versions before and after Java 9. So it is much easier to use a library like jOOR that hides the gory parts:
Class clazz = Reflect.compile("com.example.Test",
"package com.example;" +
"public class Test {\n" +
" public String hello() {\n" +
" return \"hello\";\n" +
" }\n" +
" }")
.type();
Instead of type() to simply get the class, you can actually keep using jOOR's fluent reflection API to trigger the methods on the generated class or whatever it is you'd normally do via regular reflection.
For direct JavaCompiler usage, see e.g. this or, even better, jOOR's source code.

How to use an class Instance created by another Jenkins Plugin

I'd like to use an instance of a class that another plugin creates.
In particular, I'd like to use the instance of MQConnection that the mq-notifier-plugin creates and maintains.
I've declared this plugin as a dependency in the POM:
<dependency>
<groupId>com.sonymobile.jenkins.plugins.mq</groupId>
<artifactId>mq-notifier</artifactId>
<version>1.2.5</version>
</dependency>
Imported the class:
import com.sonymobile.jenkins.plugins.mq.mqnotifier.MQConnection;
Tried to get the instance and add a message within the workflowstep:
..
public static class TestConnectionWorkflowStep extends AbstractSynchronousNonBlockingStepExecution<Void> {
private static final long serialVersionUID = 1L;
#StepContextParameter
private transient Run build;
#StepContextParameter
transient TaskListener listener;
#Override
protected Void run() throws Exception {
..
// fill in with exchange, routing_key, data, properties
MQConnection.getInstance().addMessageToQueue(..);
}
}
It compiles fine. I've also instrumented the MQConnection class to log whenever a message is added.
It seems that none of my build step messages are added to the instance's queue and just silently continues.
And as expected, I do still see messages from the mq-notifier-plugin showing up fine.
I've tried using Jenkins.getInstance().getPlugin(MQConnection.class) but doesn't work since MQConnection isn't a subclass of Plugin.
How can I access the MQConnection instance from my plugin?
getInstance() likely assumes an instance was already created when the application was started up, and it retrieves that instance. Since you're calling the method from a library, that startup hasn't happened, so there's no instance to return.
Look at the getInstance() code if you can, and also check any mq-notifier application startup or main methods in the library class. See how it instantiates the MQConnection instance, and you'll need to do the same thing.
There's probably some dependency injection going on in the other project.
I'd like to use the instance of MQConnection that the mq-notifier-plugin creates and maintains.
You're either going to have to have the two applications running side-by-side and communicating with each other, or you're going to have to figure out how to instantiate MQConnection yourself.
It seems that none of my build step messages are added to the instance's queue and just silently continues.
Is this running remotely then? If you have a remote MQConnection instance running then simply calling getInstance will not be enough for the two seperate programs to communicate with each other.

RMI Naming.lookup throws NotBoundException

I have some objects registered in my Rmi registry, i check that it's done because when i do a LocateRegistry.getRegistry().list() it results 2 registries like:
0 = "rmi://Mac.local/192.168.1.40:1099/DataService"
1 = "rmi://Mac.local/192.168.1.40:1099/AuthService"
Then, i call a
ServicioAutenticacionInterface authService = (ServicioAutenticacionInterface) Naming.lookup("rmi://Mac.local/192.168.1.40:1099/AuthService");
It throws a NotBoundException..
Just say that interfaces are in a package named commons defined as a dependency for server package who is it´s trying to invoke that lookup.
You passed a URL to Registry.bind()/rebind() instead of just a name.
URLs are passed to Naming.bind()/rebind()/unbind()/lookup(), and returned by Naming.list()`.
Simple names (such as "AuthService") are passed to Registry.bind()/rebind()/unbind()/lookup()
Whatever you passed to Registry.bind()/rebind() is returned verbatim by Registry.list().
Ergo, as Registry.list() is returning URLs, you must have supplied them via Registry.bind()/rebind().
For proof, try Naming.list("rmi://Mac.local/192.168.1.40:1099"). It will return this:
0 = "rmi://Mac.local/192.168.1.40:1099/rmi://Mac.local/192.168.1.40:1099/DataService"
1 = "rmi://Mac.local/192.168.1.40:1099/rmi://Mac.local/192.168.1.40:1099/AuthService"
which is obviously not what you want.
So you need to either use Naming.bind()/rebind() with the same URL strings, or else remove the URL part of the strings and keep using Registry.bind()/rebind().
java.rmi.NotBoundException:
My RMI-based application was working fine until I introduced another function which utilizes a service(WatchService), the service had an internal infinite loop and so this would stall the whole application.
My thought was that, when the server was started, maybe binding process did not completely happen because of the loop implemented inside the service, and the service was started at the same time during binding phase, and so when the client came looking up for the server stub, it could not find it because it wasn't bound or registered/fully in the first place.
When I removed the function/service everything worked fine again, but since I needed the service/function, I had to start it on a new thread inside the same class of the server stub like so
private class FileWatcherThread implements Runnable {
public FileWatcherThread() {
}
#Override
public void run() {
startMonitors();
}
}
Then somewhere inside your main code start the defined thread above.
new Thread(new FileWatcherThread()).start();
And this startMonitors(); is the method that has infinite loop and is defined in the main class, FileWatcherThread is an inner class of the main server class- it actually depends on how you have done your implementation and design. Just get the idea then see if it suits your problem.

Oracle BPM Human Task Comments Callback Errors When Instantiating AppModule in Called Class

Oracle BPM Version 11.1.1.7. In a Humantask.task, Events tab, Content Change Callbacks section, I have entered the fully qualified class name of a class that implements NotesStore and the addNote and getNotes methods.
The class uses public methods in an AppModule to write and read comments using our custom table and these methods were well tested during development using the the BC tester and a temporary main in the callback class.
The project is compiled to a jar and placed in the BPM project's SCA-INF/lib folder, then the SCA and related ADF human task forms are deployed.
When a comment is made in the out of box human task comments section during a process instance, the class is called, but an exception occurs in the getNotes method at the line the AppModule is created:
java.lang.ClassCastException: oracle.jbo.common.ampool.PoolMgr
In the class, the AppModule is created as so:
AuditModule service = (AuditModule)Configuration.createRootApplicationModule("com.co.modules.AuditModule", "AuditModuleLocal");
I've tried adding a web.xml config file to the SCA BPM project with a filter as discussed in this post (last answer). This discusses triggering the ADF Context initialization, but I'm still getting the error.
The question is, how can I use a call back from a human task to call a method that uses AppModule public methods to do the DB work? Oracle's documentation is very sparse in this area (29.11.1).
UPDATE
Turns out that the stack trace shows that it is having problems looking up the data source name and is actually throwing a JBO error. If anyone runs in to this, check the stack trace for other issues.
UPDATE2
Finally got this to write task comments into the custom comments table. It turns out it doesn't seem possible to use an AppModule/Model approach in a comments callback class as there appears no way to initiate the needed ADF context when the class is called. By rewriting the class to access the DB directly in code the comment callback class does write the table. But, I am getting the same error as this post. Namely:
Exception invoking method from XML data control. Cause:oracle.bpel.services.workflow.client.WorkflowServiceClientException: java.rmi.UnmarshalException: cannot unmarshaling return; nested exception is:
Supplemental Detail java.io.IOException: Error: Unexpected type encountered in writeExternal oracle.bpel.services.workflow.client.WorkflowServiceClientException: java.rmi.UnmarshalException: cannot unmarshaling return; nested exception is:
java.io.IOException: Error: Unexpected type encountered in writeExternal
I suspect this is an Oracle framework issue as the types that are passed back are from the NotesStore implementation which are all passed back to the framework:
public class CommentsCallback implements NotesStore, Serializable...
public List<CommentType> getNotes(Task task)
Has anyone solved this? Full stacktrace at:
https://community.oracle.com/thread/3638940
After discussion with Oracle, the key to avoiding the unexpected type error is to use an ObjectFactory to populate the CommentType object. While we took a different approach ultimately, the below code was provided by Oracle as an example and might help someone trying to do this:
import oracle.bpel.services.workflow.task.model.ObjectFactory; 
import oracle.bpel.services.workflow.task.model.CommentType; 
import oracle.bpel.services.workflow.task.model.IdentityType; 
...
ObjectFactory factory = new ObjectFactory() 
CommentType commentType = factory.createCommentType(); 
IdentityType updatedBy = factory.createIdentityType(); 
updatedBy.setId("some user"); 
updatedBy.setType(IWorkflowConstants.IDENTITY_TYPE_USER); 
updatedBy.setDisplayName("some user display name"); 
commentType.setUpdatedBy(updatedBy); 
commentType.setComment("some comment"); 
...set the rest of the comment fields as necessary... 

how to build a container kind of thing for executing my jobs

I want to develop a framework(eg automation) which people can use.The idea is i build a container kind of thing (eg tomcat but not exactly like a webserver).The container will have one xml file which will hold the enties of classes that are to be executed. (eg like web.xml of tomcat)
for eg xml (run.xml) can be like follows
<job>jobNameXyz</job>
<class>com.pkg.jobXyz <class>
Each of the xml job class should extedn MyBaseClass like how servlet extends HttpServlet and override
specified method doJob
public class MyClass extends Job{
public String doJob(){
//do some thing
}
}
When i place Myclass inside my continner and trigger container the jobmust run and the method doJob
How can this be done?Any one can enter deploy his jobs by putting his class file in our dir and then editing run.xml
How can this be done?Any guidelines
Your container should parse the XML file, extract a list of class names, call Class.forName for all the class names, to get a list of Class objects. Then use Class.newInstance to call the default constructor of each of the class, cast the created objects to Job, and call their doJob method.
I would use (and have been using) ANT for this purpose.
EDIT:
The couple of techniques that I have used are:
Using ANT exec task
Using customized ANT task.
Whichever technique you use, the idea is to call ANT to execute the build file. This can be invoked via invoking ANT process externally (using Runtime.exec java ant for instance) or invoking the build file programmatically.

Categories

Resources