Running a class with main method in new process results in ClassNotFoundException - java

I have a quarkus REST API project with a situation where i need to execute a part of my code in a new process (not new thread, because the hibernate context gets lost in the new thread for database operations) when a call to an API endpoint is made, but i do not want to extract this code into a whole new project so i can generate a separate .jar file i can execute as a new process. Instead, i saw that you can start a class in your own project as a new process here:
https://dzone.com/articles/running-a-java-class-as-a-subprocess
My calling code (the endpoint) looks like this:
package com.example.platform.bot.api;
...(imports)
#Path("/jobs")
public class JobResource {
#Inject JobBuilder jobBuilder;
#Inject JobRepository jobRepository;
#Inject JobRunnerProcessCaller jobRunnerProcessCaller;
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Job createJob(#Valid JobRequest jobRequest) throws Exception {
Job rawJob = jobBuilder.buildJobFromJobRequest(jobRequest);
Job job = jobRepository.persistWithCheckedDbConnections(rawJob);
jobRunnerProcessCaller.callJobRunnerFor(job);
return job;
}
}
Basically, the JobRunnerProcessCaller component in the createJob method starts the new process from a class that is in the same project and package (directly next to it):
package com.example.platform.bot;
...(imports)
#ApplicationScoped
public class JobRunnerProcessCaller {
public void callJobRunnerFor(final Job job) throws IOException {
ProcessBuilder processBuilder = new ProcessBuilder(getCommandWithArgumentList(job));
processBuilder.inheritIO();
processBuilder.start();
}
private List<String> getCommandWithArgumentList(Job job) {
List<String> command = new ArrayList<>();
command.add(getJavaBinaryPath());
command.add("-cp");
command.add(System.getProperty("java.class.path"));
command.add(JobRunnerProcessExecutor.class.getName());
command.add(job.getId().toString());
return command;
}
private String getJavaBinaryPath() {
String javaHome = System.getProperty("java.home");
return String.format("%s%sbin%sjava", javaHome, File.separator, File.separator);
}
}
The code (class JobRunnerProcessExecutor looks like this (with a main method, as required to be run/started in a new process):
package com.example.platform.bot;
...(imports)
public class JobRunnerProcessExecutor {
#Inject BotRepository botRepository;
#Inject JobRepository jobRepository;
#Inject SettingRepository settingRepository;
#Inject JobRunner jobRunner;
#ConfigProperty(name = "bot.id") Long ownBotId;
public static void main(String[] args) {
try {
JobRunnerProcessExecutor executor = buildExecutorFromCDI();
Job job = executor.getValidatedJobFromArguments(args);
executor.tryToExecuteJobRunnerFor(job);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
private static JobRunnerProcessExecutor buildExecutorFromCDI() {
return CDI.current()
.select(JobRunnerProcessExecutor.class).get();
}
private void tryToExecuteJobRunnerFor(Job job) {
Bot bot = botRepository.getById(ownBotId);
jobRunner.setSettings(settingRepository.getActiveSettings());
WebScraper webScraper = new BaiduWebScraper(new JsoupAdapter(), bot.getBaseUrl());
jobRunner.runJob(job, webScraper);
}
private Job getValidatedJobFromArguments(String[] args) throws Exception {
if (args.length != 1) throw new Exception("Missing job ID argument for job runner process");
Long jobId = Long.parseLong(args[0]);
Job job = jobRepository.getById(jobId);
if (job == null) throw new Exception("Could not find job matching passed ID");
return job;
}
}
Everything else executes fine, with no exception or error on the API endpoint process. However, if i look at the output console i can see the exception message being printed from the JobRunnerExecutor process:
Error: Could not find or load main class com.example.platform.bot.JobRunnerProcessExecutor Caused by: java.lang.ClassNotFoundException: com.example.platform.bot.JobRunnerProcessExecutor
I do not understand how he was unable to load this class, if it is laying directly next to the calling class in the same package. What is going wrong here with the process call?
Edit: It says "could not load main class", but the class has a correct main method, right?.

Related

Failed to create cache dir :Vert.x : Jenkins Build

My Unit Test project is running into an error when configured with Jenkins. The Tests run fine when I run the maven test locally from Command prompt.
Error I get :
feature ("Verify GET User Details API")
cucumber.runtime.CucumberException: java.lang.IllegalStateException: Failed to create cache dir
at cucumber.api.testng.TestNGCucumberRunner.runCucumber(TestNGCucumberRunner.java:78)
at com.ibm.wce.scbn.cc.runner.BaseRunner.feature(BaseRunner.java:32)
Caused by: java.lang.IllegalStateException: Failed to create cache dir
at io.vertx.core.file.impl.FileResolver.setupCacheDir(FileResolver.java:332)
at io.vertx.core.file.impl.FileResolver.<init>(FileResolver.java:87)
at io.vertx.core.impl.VertxImpl.<init>(VertxImpl.java:165)
at io.vertx.core.impl.VertxImpl.vertx(VertxImpl.java:92)
at io.vertx.core.impl.VertxFactoryImpl.vertx(VertxFactoryImpl.java:40)
at io.vertx.core.impl.VertxFactoryImpl.vertx(VertxFactoryImpl.java:32)
at io.vertx.core.impl.VertxFactoryImpl.vertx(VertxFactoryImpl.java:27)
at io.vertx.core.Vertx.vertx(Vertx.java:75)
at com.ibm.wce.scbn.cc.util.TokenUtil.<init>(TokenUtil.java:32)
at com.ibm.wce.scbn.cc.util.TokenUtil.getInstance(TokenUtil.java:46)
at com.ibm.wce.scbn.cc.stepdefinitions.AccountsByID.we_send_Get_request_to_service_for_Account_with_source_and_iui(AccountsByID.java:369)
at ✽.We send Get request to service for Account "1" with source "1" and iui "1"(./features/AccountsByID/AccountsByID.feature:7)
TokenUtil.java
public class TokenUtil {
private static final Logger LOGGER = Logger.getLogger(TokenUtil.class.getName());
private static TokenUtil TOKEN_INSTANCE = null;
private static Vertx VERTX = null;
private static JWTAuthOptions JWTAUTHOPS = new JWTAuthOptions();
private TokenUtil() throws Exception {
try {
VERTX = Vertx.vertx();
JsonObject objJason = new JsonObject(VERTX.fileSystem().readFileBlocking(System.getProperty("privatejwtpath")));
JWTAUTHOPS.addJwk(objJason);
} catch (Exception e) {
LOGGER.error("Unable to load private JWK json file", e);
throw e;
}
}
public static TokenUtil getInstance() throws Exception {
if (TOKEN_INSTANCE == null) {
synchronized (TokenUtil.class) {
TOKEN_INSTANCE = new TokenUtil();
}
}
return TOKEN_INSTANCE;
}
public String getJWT(String iui) {
JWTOptions jwtOptions = new JWTOptions();
JsonObject payLoad = new JsonObject();
jwtOptions.setAlgorithm("RS256");
jwtOptions.setExpiresInSeconds(300);
JWTAuth jwt = JWTAuth.create(VERTX, JWTAUTHOPS);
payLoad.put("ibm", new JsonObject().put("iui", iui));
return jwt.generateToken(payLoad, jwtOptions);
}
}
Any suggestions on how to fix this is highly appreciated. Thank you
Issue fixed by disabling Cache

How to use Mockito to skip invoking a void method

I have a REST controller exposing an endpoint. When this endpoint is hit, a void method gets invoked and this method then goes off and pushes a file to a remote GitHub repo. The code is working beautifully.
My problem occurs when writing unit tests for the class. I don't want the actual void method to get invoked (because it's pushing a file to github). I've mocked the method to doNothing() when it's invoked, but the file is still being pushed for some reason. Where am i going wrong?
Below is my code:
//ApplicationController.java
#RestController
public class ApplicationController {
#Autowired
GitService gitService;
#GetMapping("/v1/whatevs")
public String push_policy() throws IOException, GitAPIException {
gitService.gitPush("Successfully pushed a fie to github...i think.");
return "pushed the file to github.";
}
}
//GitService.java
public interface GitService {
public void gitPush(String fileContents) throws IOException, GitAPIException;
}
//GitServiceImpl.java
#Component
public class GitServiceImpl implements GitService {
private static final Logger log = Logger.getLogger(GitServiceImpl.class.getName());
#Override
public void gitPush(String fileContents) throws IOException, GitAPIException {
// prepare a new folder for the cloned repository
File localPath = File.createTempFile(GIT_REPO, "");
if (!localPath.delete()) {
throw new IOException("Could not delete temporary file " + localPath);
}
// now clone repository
System.out.println("Cloning from" + REMOTE_GIT_URL + "to " + localPath);
try (Git result = Git.cloneRepository().setURI(REMOTE_GIT_URL).setDirectory(localPath)
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(GIT_USER, GIT_PASSWORD)).call()) {
// Note: the call() returns an opened repository already which needs to be
// closed to avoid file handle leaks!
Repository repository = result.getRepository();
try (Git git = new Git(repository)) {
// create the file
Path path = Paths.get(String.format("%s/%s", localPath.getPath(), "someFileName"));
byte[] strToBytes = fileContents.getBytes();
Files.write(path, strToBytes);
// add the file to the repo
git.add().addFilepattern("someFileName").call();
// commit the changes
String commit_message = String
.format("[%s] Calico yaml file(s) generated by Astra Calico policy adaptor.", GIT_USER);
git.commit().setMessage(commit_message).call();
log.info("Committed file to repository at " + REMOTE_GIT_URL);
// push the commits
Iterable<PushResult> pushResults = git.push()
.setCredentialsProvider(new UsernamePasswordCredentialsProvider(GIT_USER, GIT_PASSWORD)).call();
pushResults.forEach(pushResult -> log.info(pushResult.getMessages()));
}
} finally {
// delete temp directory on disk
FileUtils.deleteDirectory(localPath);
}
}
}
My test. It's passing, but the gitService.gitpush() method I thought was being mocked, is pushing a file to github.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ApplicationControllerTest {
#Autowired
private MockMvc mockMvc;
#Mock
GitService gitService;
//System under test
#InjectMocks
ApplicationController applicationController;
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(applicationController).build();
}
#Test
public void controllerShouldReturnStatus200Ok() throws Exception {
Mockito.doNothing().when(gitService).gitPush(Mockito.anyString());
mockMvc.perform(
MockMvcRequestBuilders.get("/v1/whatevs")
).andExpect(MockMvcResultMatchers.status().isOk());
}
#Test
public void someTest() {
assertTrue(true);
}
}
How can I keep the .gitPush() method from being invoked at all? Am I simply mocking the service incorrectly?
Add #Before annotation to your setup method to
Add this to your before method MockitoAnnotations.initMocks(this).
It should work now

How to execute several maven deploy commands on the same process

I am trying to develop a tool that get a directory of maven artifacts and upload them to Nexus 3. The tool is working but I have a performance issue.
My program launch a separate maven process for each artifact that must be uploaded.I'm curious whether these could be batched somehow.
I am using the maven-invoker library for executing maven commands.
public class MavenUploader {
private final MavenDeployer mavenDeployer;
#Inject
public MavenUploader(MavenDeployer mavenDeployer) {
this.mavenDeployer = mavenDeployer;
}
#Override
public void uploadToRepository(Path pathToUpload) {
try (Stream<Path> files = Files.walk(pathToUpload)){
files.forEach(mavenDeployer::deployArtifact);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
This is the class that resposible to upload the artifacts
import org.apache.maven.shared.invoker.*;
public class MavenDeployer {
private final InvocationRequest invocationRequest;
private final Invoker invoker;
#Inject
public MavenDeployer(InvocationRequest invocationRequest,
Invoker invoker) {
this.invocationRequest = invocationRequest;
this.invoker = invoker;
}
public void deployArtifact(Path pathToPom, String commandToExecute) {
invocationRequest.setGoals(Collections.singletonList(commandToExecute));
InvocationResult invocationResult = invoker.execute(invocationRequest);
}
for each time that the deployArtifact method called new process is opened, There is a way to batch all the uploads to use the same process?
Not maven-deploy but you can use this if you'd like: https://github.com/DarthHater/nexus-repository-import-scripts
I think it accomplishes what you want to do.

HASingleton fails to lookup JNDI bean

I am trying to deploy a HASingleton on a JBoss6.4. I have followed this tutorial to come up with the following:
I create a Service which is supposed to start a timer (own timer interface), by injecting the timer bean through JNDI.
public class HATimerService implements Service<String> {
private Logger logger = Logger.getLogger(HATimerService.class);
private final AtomicBoolean started = new AtomicBoolean(false);
private ServiceName serviceName;
private final InjectedValue<ServerEnvironment> env = new InjectedValue();
private String JNDI = "java:global/my-ear/my-module/MyTimer"
public HATimerService() {
serviceName = ServiceName.JBOSS.append(new String[]{"my", "ha", "singleton", "MyHaService"});
}
public String getValue() throws IllegalStateException, IllegalArgumentException {
return "";
}
public void start(StartContext context) throws StartException {
if(!started.compareAndSet(false, true)) {
throw new StartException("The service is still started!");
} else {
try {
InitialContext e = new InitialContext();
TimerScheduler myTimer = (TimerScheduler)e.lookup(JNDI);
timer.startTimer();
} catch (NamingException var6) {
throw new StartException("Could not initialize timer", var6);
}
}
}
public void stop(StopContext context) {
if(started.compareAndSet(true, false)) {
try {
InitialContext e = new InitialContext();
((TimerScheduler)e.lookup(JNDI)).stopTimer();
} catch (NamingException var4) {
logger.error("Could not stop timer", var4);
}
}
}
public ServiceName getServiceName() {
return serviceName;
}
public InjectedValue<ServerEnvironment> getEnvironment() {
return env;
}
}
I also have an activator which activates the service.
public class HATimerServiceActivator implements ServiceActivator {
private final Logger log = Logger.getLogger(this.getClass());
public HATimerServiceActivator() {
}
public void activate(ServiceActivatorContext context) {
HATimerService service = new HATimerService();
this.log.info(service.getServiceName() + "HATimerService will be installed");
SingletonService singleton = new SingletonService(service, service.getServiceName());
singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry()))
.addDependency(ServerEnvironmentService.SERVICE_NAME, ServerEnvironment.class, service.getEnvironment())
.setInitialMode(Mode.ACTIVE)
.install();
}
}
The timer bean, HATimerService, and the HATimerServiceActivator are all deployed in an ear called my-ear. In the log files I can see:
JNDI bindings for session bean named MyTimer.... :
java:global/my-ear/my-module/MyTimer
However, every once in a while (approx. 1/3 of all deploys), this setup fails due to a NameNotFoundException where the JNDI lookup fails. The full exception is: Caused by: javax.naming.NameNotFoundException: Error looking up my-ear/my-module/MyTimer, service service jboss.naming.context.java.global.my-ear.my-module.MyTimer is not started
My guess is that this can be some sort of race condition where the bean isn't registered in the JNDI-tree yet. How can I make the service wait with the lookup until the bean is available?
It seems that there exists a possibility to create dependencies on deployment units. When creating the SingletonService, the following dependency can be added:
ServiceName ejbDependency = ServiceName.of("jboss", "deployment", "subunit", "my-ear.ear", "my-module.jar", "component", "MyTimerBean", "START");
singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry()))
.addDependency(ServerEnvironmentService.SERVICE_NAME, ServerEnvironment.class, service.getEnvironment())
.setInitialMode(Mode.ACTIVE)
.addDependency(ejbDependency)
.install();
As long as ejbDependency is a correct dependency, the lookup will be performed after bean start.

Camel file unit test

I am new to Apache Camel, I have written a simple route to scan a directory (/test), file will be processed when it was copied into the directory. Anyone has an idea on how to write a camel unit test to test the following route? Is there a way to mock the process of copying the file into the /test directory so that the route will be triggered.
public void configure() {
from( "file:/test?preMove=IN_PROGRESS" +
"&move=completed/${date:now:yyyyMMdd}/${file:name}" +
"&moveFailed=FAILED/${file:name.noext}-${date:now:yyyyMMddHHmmssSSS}.${file:ext}" )
.process(new Processor() {
public void process(Exchange exchange) throws IOException {
File file = (File) exchange.getIn().getBody();
// read file content ......
}
});
}
You have done the routing by one of many correct ways. But there exist some more important pieces to make your code run - you should create a context, create a router with this your configure(), add it to a context, and run this context.
Sorry, I prefer beans to processors, so you have also to register a bean. And make you processing a normal named method in a named class.
I think, the most compact info is here. JUnit test is a standalone app and you need to run Camel as a standalone app for JUnit testing.
I think the basic idea is that you mock the end endpoint so you can check what is coming out your route. There are a few different ways, but you could test your route as follows:
public class MyRouteTest extends CamelSpringTestSupport {
private static final String INPUT_FILE = "myInputFile.xml";
private static final String URI_START = "direct:start";
private static final String URI_END = "mock:end";
#Override
public boolean isUseAdviceWith() {
return true;
}
#Override
protected AbstractApplicationContext createApplicationContext() {
return new AnnotationConfigApplicationContext(CamelTestConfig.class); // this is my Spring test config, where you wire beans
}
#Override
protected RouteBuilder createRouteBuilder() {
MyRoute route = new MyRoute();
route.setFrom(URI_START); // I have added getter and setters to MyRoute so I can mock 'start' and 'end'
route.setTo(URI_END);
return route;
}
#Test
public void testMyRoute() throws Exception {
MockEndpoint result = getMockEndpoint(URI_END);
context.start();
// I am just checking I receive 5 messages, but you should actually check the content with expectedBodiesReceived() depending on what your processor does to the those files.
result.expectedMessageCount(5);
// I am just sending the same file 5 times
for (int i = 0; i < 5; i++) {
template.sendBody(URI_START, getInputFile(INPUT_FILE));
}
result.assertIsSatisfied();
context.stop();
}
private File getInputFile(String name) throws URISyntaxException, IOException {
return FileUtils.getFile("src", "test", "resources", name);
}
I am sure you already solved your issue is 2013, but this is how I would solve it in 2017. Regards

Categories

Resources