/**
* 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.discovery;
import junit.framework.TestCase;
import org.apache.openejb.monitoring.LocalMBeanServer;
import org.apache.openejb.monitoring.ManagedMBean;
import org.apache.openejb.monitoring.ObjectNameBuilder;
import org.apache.openejb.server.DiscoveryListener;
import org.apache.openejb.server.DiscoveryRegistry;
import org.apache.openejb.util.Join;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.ConsoleHandler;
import java.util.logging.ErrorManager;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* @version $Rev$ $Date$
*/
public class MultipointDiscoveryAgentTest extends TestCase {
public void test() throws Exception {
final URI testService = new URI("green://localhost:0");
final String[] names = {"red"};
final int PEERS = names.length;
final CountDownLatch[] latches = {
new CountDownLatch(PEERS + 1),
new CountDownLatch(PEERS + 1)
};
final DiscoveryListener listener = new DiscoveryListener() {
public void serviceAdded(final URI service) {
System.out.println("added = " + service);
if (testService.equals(service)) {
latches[0].countDown();
}
}
public void serviceRemoved(final URI service) {
System.out.println("removed = " + service);
if (testService.equals(service)) {
latches[1].countDown();
}
}
};
final List<Node> nodes = new ArrayList<Node>();
final Node root = new Node(0, listener, "root");
nodes.add(root);
for (final String name : names) {
final Node node = new Node(0, listener, name, root.getURI());
nodes.add(node);
}
final Node owner = nodes.get(nodes.size() / 2);
for (int i = 0; i < 3; i++) {
latches[0] = new CountDownLatch(PEERS + 1);
latches[1] = new CountDownLatch(PEERS + 1);
// OK, do the broadcast
owner.getRegistry().registerService(testService);
// Notification should have reached all participants
assertTrue("round=" + i + ". Add failed", latches[0].await(30, TimeUnit.SECONDS));
owner.getRegistry().unregisterService(testService);
assertTrue("round=" + i + ". Remove failed", latches[1].await(60, TimeUnit.SECONDS));
for (final Node node : nodes) {
final Set<URI> services = node.getRegistry().getServices();
assertEquals("round=" + i + ". Service retained", 0, services.size());
}
}
}
public void _debug() throws Exception {
System.setProperty("log4j.category.OpenEJB.server.discovery", "debug");
System.setProperty("log4j.appender.C.layout", "org.apache.log4j.PatternLayout");
System.setProperty("log4j.appender.C.layout.ConversionPattern", "%d - %m%n");
final URI greenService = new URI("green://localhost:5555");
final Node green = new Node(5555, new Listener("green"), true, "green", 100, 2);
green.getRegistry().registerService(greenService);
// launch(green, "blue", 4444);
// launch(green, "red", 6666);
// launch(green, "yellow", 8888);
final Node orange = launch(green, "orange", 7777);
Thread.sleep(500000);
orange.getAgent().stop();
Thread.sleep(5000);
}
private Node launch(final Node green, final String color, final int port) throws Exception {
final URI orangeService = new URI(color + "://localhost:" + port);
final Node orange = new Node(port, new Listener(color), color, green.getURI());
orange.getRegistry().registerService(orangeService);
Thread.sleep(100);
return orange;
}
public static class Node {
private final MultipointDiscoveryAgent agent;
private final DiscoveryRegistry registry;
private final String name;
public Node(final int p, final DiscoveryListener listener, String name, URI... uris) throws Exception {
this(p, listener, false, name, 100, 2, uris);
}
public Node(final int p, final DiscoveryListener listener, final boolean debug, String name, int heartRate, int maxMissHeartBeats, URI... uris) throws Exception {
this.agent = new MultipointDiscoveryAgent(debug, name);
this.name = name;
final Properties props = new Properties();
props.put("port", p + "");
props.put("initialServers", Join.join(",", uris));
props.put("max_missed_heartbeats", "1");
props.put("heart_rate", "" + heartRate);
props.put("max_missed_heartbeats", "" + maxMissHeartBeats);
agent.init(props);
this.registry = new DiscoveryRegistry(agent);
this.registry.addDiscoveryListener(listener);
agent.start();
// final ObjectNameBuilder jmxName = new ObjectNameBuilder("openejb.management");
// jmxName.set("type", "ServerService");
// jmxName.set("name", "multipoint");
// jmxName.set("port", Integer.toString(agent.getPort()));
//
// MBeanServer server = LocalMBeanServer.get();
// try {
// final ObjectName objectName = jmxName.build();
// server.registerMBean(new ManagedMBean(agent), objectName);
// } catch (Exception e) {
// e.printStackTrace();
// }
}
public MultipointDiscoveryAgent getAgent() {
return agent;
}
public DiscoveryRegistry getRegistry() {
return registry;
}
public int getPort() {
return agent.getPort();
}
public URI getURI() {
return URI.create("conn://localhost:" + getPort() + "/" + name);
}
}
private static class Listener implements DiscoveryListener {
private final String name;
public String getName() {
return name;
}
private Listener(final String name) {
this.name = name;
}
public void serviceAdded(final URI service) {
// System.out.printf("[%s] added = %s\n", name, service);
}
public void serviceRemoved(final URI service) {
// System.out.printf("[%s] removed = %s\n", name, service);
}
@Override
public String toString() {
return name;
}
}
public static interface LogRecordFilter {
public boolean accept(LogRecord record);
}
public static class FilteredHandler extends Handler {
private final Handler handler;
private final LogRecordFilter filter;
public FilteredHandler(final Handler handler, LogRecordFilter filter) {
this.handler = handler;
this.filter = filter;
}
@Override
public void publish(final LogRecord record) {
handler.publish(record);
// if (filter.accept(record)) {
// }
}
@Override
public void flush() {
handler.flush();
}
@Override
public void close() throws SecurityException {
handler.close();
}
@Override
public void setFormatter(final Formatter newFormatter) throws SecurityException {
handler.setFormatter(newFormatter);
}
@Override
public Formatter getFormatter() {
return handler.getFormatter();
}
@Override
public void setEncoding(final String encoding) throws SecurityException, UnsupportedEncodingException {
handler.setEncoding(encoding);
}
@Override
public String getEncoding() {
return handler.getEncoding();
}
@Override
public void setFilter(final Filter newFilter) throws SecurityException {
handler.setFilter(newFilter);
}
@Override
public Filter getFilter() {
return handler.getFilter();
}
@Override
public void setErrorManager(final ErrorManager em) {
handler.setErrorManager(em);
}
@Override
public ErrorManager getErrorManager() {
return handler.getErrorManager();
}
@Override
public void setLevel(final Level newLevel) throws SecurityException {
handler.setLevel(newLevel);
}
@Override
public Level getLevel() {
return handler.getLevel();
}
@Override
public boolean isLoggable(final LogRecord record) {
return handler.isLoggable(record);
}
}
}