Skip to content
Permalink
Browse files
BATCHEE-16 adding a way to select the classloader to use to run the b…
…atch from the gui
  • Loading branch information
rmannibucau committed Mar 27, 2014
1 parent f5b540c commit f3534e29e7d66cf887bc3d5b30c82e94d51eb2c7
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 1 deletion.
@@ -48,6 +48,13 @@
<artifactId>geronimo-atinject_1.0_spec</artifactId>
</dependency>

<dependency>
<groupId>org.apache.openejb</groupId>
<artifactId>openejb-core</artifactId>
<version>4.6.0</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
@@ -16,6 +16,8 @@
*/
package org.apache.batchee.servlet;

import org.apache.batchee.servlet.spi.JobOperatorClassLoaderFinder;

import javax.batch.operations.BatchRuntimeException;
import javax.batch.operations.JobExecutionNotRunningException;
import javax.batch.operations.JobOperator;
@@ -33,6 +35,9 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
@@ -55,12 +60,15 @@ public class JBatchController extends HttpServlet {
private static final String START_MAPPING = "/start/";
private static final String DO_START_MAPPING = "/doStart/";
private static final String VIEW_MAPPING = "/view/";
public static final String BATCHEE_CLASSLOADER_FINDER_KEY = "batchee.classloader-finder";

private JobOperator operator;

private String context;
private String mapping = DEFAULT_MAPPING_SERVLET25;
private int executionByPage = DEFAULT_PAGE_SIZE;
private ClassLoader controllerLoader;
private JobOperatorClassLoaderFinder classLoaderFinder;

public JBatchController mapping(final String rawMapping) {
this.mapping = rawMapping.substring(0, rawMapping.length() - 2); // mapping pattern is /xxx/*
@@ -74,7 +82,29 @@ public JBatchController executionByPage(final int byPage) {

@Override
public void init(final ServletConfig config) throws ServletException {
this.operator = BatchRuntime.getJobOperator();
controllerLoader = Thread.currentThread().getContextClassLoader();

final JobOperator delegate = BatchRuntime.getJobOperator();
classLoaderFinder = newClassLoaderFinder(
System.getProperty(BATCHEE_CLASSLOADER_FINDER_KEY, config.getInitParameter(BATCHEE_CLASSLOADER_FINDER_KEY)),
controllerLoader);

this.operator = JobOperator.class.cast(Proxy.newProxyInstance(
controllerLoader,
new Class<?>[] { JobOperator.class },
new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
final Thread thread = Thread.currentThread();
final ClassLoader original = thread.getContextClassLoader();
thread.setContextClassLoader(findLoaderToUse(original, method.getName(), args));
try {
return method.invoke(delegate, args);
} finally {
thread.setContextClassLoader(original);
}
}
}));

this.context = config.getServletContext().getContextPath();
if ("/".equals(context)) {
@@ -84,6 +114,29 @@ public void init(final ServletConfig config) throws ServletException {
mapping = context + mapping;
}

private JobOperatorClassLoaderFinder newClassLoaderFinder(final String initParameter, final ClassLoader webappLoader) {
if (initParameter == null) {
return null;
}
try {
return JobOperatorClassLoaderFinder.class.cast(
controllerLoader.loadClass(initParameter.trim()).getConstructor(ClassLoader.class)
.newInstance(webappLoader));
} catch (final Exception e) {
throw new BatchRuntimeException(e.getMessage(), e);
}
}

protected ClassLoader findLoaderToUse(final ClassLoader original, String name, Object[] args) {
if (classLoaderFinder != null) {
final ClassLoader found = classLoaderFinder.find(name, args);
if (found != null) {
return found;
}
}
return original;
}

@Override
protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
@@ -0,0 +1,26 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.batchee.servlet.spi;

public interface JobOperatorClassLoaderFinder {
/**
* @param name JobOperator method
* @param args arguments of the method
* @return
*/
ClassLoader find(String name, Object[] args);
}
@@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.batchee.servlet.spi.internal;

import org.apache.batchee.servlet.spi.JobOperatorClassLoaderFinder;
import org.apache.openejb.AppContext;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.spi.ContainerSystem;

import java.util.List;
import java.util.Properties;

// simple impl taking just *the* other app if tomee deploys a single application
// or a specific one if you specifiy in properties batchee.application
//
// side note: it needs the persistence to be shared accross apps (case in a server or with database usage)
public class OpenEJBJobOperatorClassLoaderFinder implements JobOperatorClassLoaderFinder {
private static final String BATCHEE_APPLICATION = "batchee.application";

private final ClassLoader ignoreLoader;

public OpenEJBJobOperatorClassLoaderFinder(final ClassLoader ignoreLoader) {
this.ignoreLoader = ignoreLoader;
}

@Override
public ClassLoader find(final String name, final Object[] args) {
final String idToFind = extractId(name, args);

ClassLoader potentialLoader = null;

final List<AppContext> appContexts = SystemInstance.get().getComponent(ContainerSystem.class).getAppContexts();
for (final AppContext app : appContexts) {
final String id = app.getId();
if ("tomee".equals(id) || "openejb".equals(id)) {
continue;
}

potentialLoader = app.getClassLoader();
if (potentialLoader == ignoreLoader) {
continue;
}

if (id.equals(idToFind)) {
return potentialLoader;
}
}
return potentialLoader;
}

private String extractId(final String name, final Object[] args) {
if (args != null) {
for (final Object o : args) {
if (Properties.class.isInstance(o)) {
return Properties.class.cast(o).getProperty(BATCHEE_APPLICATION);
}
}
}
return null;
}
}

0 comments on commit f3534e2

Please sign in to comment.