blob: 8885d4b7094d6e26729365b7132ecdbb61b8088a [file] [log] [blame]
/*
* 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.openejb.arquillian.common;
import org.jboss.arquillian.container.spi.client.protocol.metadata.HTTPContext;
import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.InstanceProducer;
import org.jboss.arquillian.core.api.annotation.ApplicationScoped;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.test.spi.event.enrichment.BeforeEnrichment;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Properties;
import java.util.concurrent.Callable;
public class RemoteInitialContextObserver {
private static final String REMOTE_INITIAL_CONTEXT_FACTORY = "org.apache.openejb.client.RemoteInitialContextFactory";
@Inject
@ApplicationScoped
private InstanceProducer<Context> context;
@Inject
private Instance<Context> existingContext;
@Inject
private Instance<ProtocolMetaData> protocolMetadata;
public void beforeSuite(@Observes final BeforeEnrichment event) {
final ProtocolMetaData metaData = protocolMetadata.get();
if(metaData == null || !metaData.hasContext(HTTPContext.class)) {
return;
}
try {
Thread.currentThread().getContextClassLoader().loadClass(REMOTE_INITIAL_CONTEXT_FACTORY);
final HTTPContext httpContext = metaData.getContexts(HTTPContext.class).iterator().next();
final Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, REMOTE_INITIAL_CONTEXT_FACTORY);
props.setProperty(Context.PROVIDER_URL, "http://" + httpContext.getHost() + ":" + httpContext.getPort() + "/tomee/ejb");
Context existing = null;
try {
existing = existingContext.get();
} catch (final Throwable t) {
// no-op
}
final Context proxyInstance = (Context) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{Context.class}, new MultipleContextHandler(props, existing));
context.set(new InitialContextWrapper(proxyInstance)); // cause ContextProducer of arquillian supports InitialContext
} catch (final ClassNotFoundException | NamingException e) {
// no-op
}
}
private static class MultipleContextHandler implements InvocationHandler {
private final Context context;
private final Properties properties;
public MultipleContextHandler(final Properties props, final Context initialContexts) {
properties = props;
context = initialContexts;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
Exception err = null;
for (final Callable<Context> callable : Arrays.asList( // order is important to avoid to start an embedded container for some cases
new Callable<Context>() { // then try to create a remote context
@Override
public Context call() throws Exception {
return new InitialContext(properties);
}
},
new Callable<Context>() { // then existing context
@Override
public Context call() throws Exception {
return context;
}
},
new Callable<Context>() { // then contextual context, this can start an embedded container in some cases
@Override
public Context call() throws Exception {
return new InitialContext();
}
}
)) {
try {
final Context ctx = callable.call();
if (ctx == null) {
continue;
}
return method.invoke(ctx, args);
} catch (final Exception e) {
err = e;
}
}
if (err != null) {
if (InvocationTargetException.class.isInstance(err)) {
throw err.getCause();
}
throw err;
}
return null;
}
}
private static class InitialContextWrapper extends InitialContext {
private final Context delegate;
public InitialContextWrapper(final Context proxyInstance) throws NamingException {
super(true);
this.delegate = proxyInstance;
}
@Override
protected Context getURLOrDefaultInitCtx(final String name) {
return delegate;
}
}
}