/**
* 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.server.ejbd;
import junit.framework.TestCase;
import org.apache.openejb.OpenEJB;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.core.ServerFederation;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.StatelessBean;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.server.DiscoveryAgent;
import org.apache.openejb.server.DiscoveryListener;
import org.apache.openejb.server.ServerService;
import org.apache.openejb.server.ServerServiceFilter;
import org.apache.openejb.server.ServiceDaemon;
import org.apache.openejb.server.ServiceException;
import javax.ejb.EJBException;
import javax.ejb.Remote;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.Socket;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* @version $Rev$ $Date$
*/
public class FailoverTest extends TestCase {
private static DiscoveryAgent agent;
public void _testCleanShutdown() throws Exception {
agent = new TestAgent();
try {
// Properties initProps = System.getProperties();
final Properties initProps = new Properties();
initProps.setProperty("openejb.deployments.classpath.include", "");
initProps.setProperty("openejb.deployments.classpath.filter.descriptors", "true");
OpenEJB.init(initProps, new ServerFederation());
} catch (final Exception e) {
}
final Assembler assembler = SystemInstance.get().getComponent(Assembler.class);
final ConfigurationFactory config = new ConfigurationFactory();
final EjbJar ejbJar = new EjbJar();
ejbJar.addEnterpriseBean(new StatelessBean(Target.class));
assembler.createApplication(config.configureApplication(ejbJar));
SystemInstance.get().setComponent(DiscoveryAgent.class, agent);
final ServerService red = server(Host.RED);
final ServerService blue = server(Host.BLUE);
final ServerService green = server(Host.GREEN);
red.start();
blue.start();
green.start();
final TargetRemote target = getBean(red);
assertEquals(Host.RED, target.getHost());
red.stop();
assertEquals(Host.BLUE, target.getHost());
blue.stop();
assertEquals(Host.GREEN, target.getHost());
green.stop();
try {
target.getHost();
fail("EJBException should have been thrown");
} catch (final EJBException e) {
// pass
}
red.start();
assertEquals(Host.RED, target.getHost());
}
public void testCrash() throws Exception {
agent = new TestAgent();
try {
// Properties initProps = System.getProperties();
final Properties initProps = new Properties();
initProps.setProperty("openejb.deployments.classpath.include", "");
initProps.setProperty("openejb.deployments.classpath.filter.descriptors", "true");
OpenEJB.init(initProps, new ServerFederation());
} catch (final Exception e) {
}
final Assembler assembler = SystemInstance.get().getComponent(Assembler.class);
final ConfigurationFactory config = new ConfigurationFactory();
final EjbJar ejbJar = new EjbJar();
ejbJar.addEnterpriseBean(new StatelessBean(Target.class));
assembler.createApplication(config.configureApplication(ejbJar));
SystemInstance.get().setComponent(DiscoveryAgent.class, agent);
final ServerService red = server(Host.RED);
final ServerService blue = server(Host.BLUE);
final ServerService green = server(Host.GREEN);
red.start();
blue.start();
green.start();
final TargetRemote target = getBean(red);
assertEquals(Host.GREEN, target.kill(Host.RED, Host.BLUE).host);
assertEquals(Host.GREEN, target.getHost());
red.stop();
blue.stop();
green.stop();
try {
target.getHost();
fail("EJBException should have been thrown");
} catch (final EJBException e) {
// pass
}
red.start();
assertEquals(Host.RED, target.getHost());
}
private TargetRemote getBean(final ServerService server) throws NamingException, IOException, OpenEJBException {
final int port = server.getPort();
// good creds
final Properties props = new Properties();
props.put("java.naming.factory.initial", "org.apache.openejb.client.RemoteInitialContextFactory");
props.put("java.naming.provider.url", "ejbd://localhost:" + port + "/RED");
System.setProperty("openejb.client.keepalive", "ping_pong");
System.setProperty("openejb.client.requestretry", "true");
final Context context = new InitialContext(props);
return (TargetRemote) context.lookup("TargetRemote");
}
private ServerService server(final Host host) throws Exception {
ServerService server = new EjbServer();
server = new HostFilter(server, host);
server = new ServiceDaemon(server, 0, "localhost");
server = new AgentFilter(server, agent, host);
server.init(new Properties());
return server;
}
// Simple single-threaded version, way easier on testing
public static class TestAgent implements DiscoveryAgent {
private final List<DiscoveryListener> listeners = new ArrayList<DiscoveryListener>();
@Override
public void registerService(final URI serviceUri) throws IOException {
for (final DiscoveryListener listener : listeners) {
listener.serviceAdded(serviceUri);
}
}
@Override
public void reportFailed(final URI serviceUri) throws IOException {
}
@Override
public void setDiscoveryListener(final DiscoveryListener listener) {
listeners.add(listener);
}
@Override
public void unregisterService(final URI serviceUri) throws IOException {
for (final DiscoveryListener listener : listeners) {
listener.serviceRemoved(serviceUri);
}
}
}
public static enum Host {
RED,
BLUE,
GREEN
}
public static final ThreadLocal<Host> host = new ThreadLocal<Host>();
public static Socket serverSideSocket;
public static class AgentFilter extends ServerServiceFilter {
private final Host host;
private final DiscoveryAgent agent;
private URI uri;
public AgentFilter(final ServerService service, final DiscoveryAgent agent, final Host host) {
super(service);
this.agent = agent;
this.host = host;
}
@Override
public void start() throws ServiceException {
super.start();
try {
uri = new URI("ejb:ejbd://localhost:" + getPort() + "/" + host);
agent.registerService(uri);
} catch (final Exception e) {
throw new ServiceException(e);
}
}
@Override
public void stop() throws ServiceException {
super.stop();
try {
agent.unregisterService(uri);
} catch (final Exception e) {
throw new ServiceException(e);
}
}
}
public static class HostFilter extends ServerServiceFilter {
private final Host me;
public HostFilter(final ServerService service, final Host me) {
super(service);
this.me = me;
}
@Override
public void service(final InputStream in, final OutputStream out) throws ServiceException, IOException {
try {
host.set(me);
super.service(in, out);
} finally {
host.remove();
}
}
@Override
public void service(final Socket socket) throws ServiceException, IOException {
serverSideSocket = socket;
try {
host.set(me);
super.service(socket);
} finally {
host.remove();
}
}
}
public static class Target implements TargetRemote {
@Override
public Host getHost() {
return host.get();
}
@Override
public Wrapper kill(final Host... hosts) {
final Host host = getHost();
for (final Host h : hosts) {
if (h == host) {
return new Wrapper(host, serverSideSocket);
}
}
return new Wrapper(host, null);
}
}
public static class Wrapper implements Serializable {
private static final long serialVersionUID = 4604591462681914507L;
transient Socket socket;
private final Host host;
public Wrapper(final Host host, final Socket socket) {
this.host = host;
this.socket = socket;
}
private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
if (socket != null) {
socket.close();
}
}
}
@Remote
public static interface TargetRemote {
Host getHost();
Wrapper kill(Host... hosts);
}
}