/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.agent;
import java.io.IOException;
import java.io.NotSerializableException;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.testng.annotations.Test;
import org.jboss.remoting.CannotConnectException;
import org.jboss.remoting.InvocationFailureException;
import org.jboss.remoting.InvokerLocator;
import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.core.util.exception.WrappedRemotingException;
import org.rhq.enterprise.communications.ServiceContainerConfigurationConstants;
import org.rhq.enterprise.communications.command.CommandResponse;
import org.rhq.enterprise.communications.command.client.ClientCommandSender;
import org.rhq.enterprise.communications.command.client.ClientCommandSenderStateListener;
import org.rhq.enterprise.communications.command.client.ClientRemotePojoFactory;
import org.rhq.enterprise.communications.command.client.CommandResponseCallback;
import org.rhq.enterprise.communications.command.client.RemotePojoInvocationFuture;
import org.rhq.enterprise.communications.command.impl.identify.IdentifyCommand;
import org.rhq.enterprise.communications.command.impl.identify.IdentifyCommandResponse;
import org.rhq.enterprise.communications.command.server.discovery.AutoDiscoveryException;
/**
* This tests the communications layer in the agent.
*
* @author John Mazzitelli
*/
@Test(groups = "agent.comm")
public class AgentComm3Test extends AgentCommTestBase {
private static final boolean ENABLE_TESTS = true;
/**
* Tests the command listener framework and that an agent will start its sender once it receives a command from the
* server.
*
* @throws Exception
*/
@Test(enabled = ENABLE_TESTS)
public void testCommandListenerAgentSenderStart() throws Exception {
// make it so the server auto-detection features do not detect anything
Properties props1 = new Properties();
props1.setProperty(AgentConfigurationConstants.SERVER_AUTO_DETECTION, "true");
props1.setProperty(ServiceContainerConfigurationConstants.MULTICASTDETECTOR_PORT,
AGENT1_COMM_MULTICAST_DECTECTOR_PORT);
props1.setProperty(AgentConfigurationConstants.CLIENT_SENDER_SERVER_POLLING_INTERVAL, "-1");
Properties props2 = new Properties();
props2.setProperty(AgentConfigurationConstants.SERVER_AUTO_DETECTION, "true");
props2.setProperty(ServiceContainerConfigurationConstants.MULTICASTDETECTOR_PORT,
AGENT2_COMM_MULTICAST_DECTECTOR_PORT);
props2.setProperty(AgentConfigurationConstants.CLIENT_SENDER_SERVER_POLLING_INTERVAL, "-1");
m_agent1Test.setConfigurationOverrides(props1);
m_agent2Test.setConfigurationOverrides(props2);
AgentMain agent1 = m_agent1Test.createAgent(false); // don't start its sender yet
agent1.start();
agent1.getClientCommandSender().getRemoteCommunicator().setInitializeCallback(null);
AgentMain agent2 = m_agent2Test.createAgent(true); // we can start its sender now, we'll use it to send agent1 a command
try {
Thread.sleep(5000);
assert !agent1.getClientCommandSender().isSending() : "we should have been able to turn off the InitializeCallback/connectAgent should therefore not have triggered our listener and started the sender";
assert agent2.getClientCommandSender().isSending() : "Should have already started the sender";
// our canary-in-the-mine is the sending mode, the sender goes into sending mode when it detects the server
IdentifyCommand command = new IdentifyCommand();
CommandResponse response;
response = agent2.getClientCommandSender().sendSynch(command);
assert response.isSuccessful() : "agent2 should have been able to send to agent1: " + response;
assert agent1.getClientCommandSender().isSending() : "agent2 message should have started agent1 sender";
// now let's stop the agent1 sender and try again, just to make sure the command listeners work repeatedly
agent1.getClientCommandSender().stopSending(false);
assert !agent1.getClientCommandSender().isSending() : "Should not be in sending mode again";
assert agent2.getClientCommandSender().isSending() : "Should still be started";
response = agent2.getClientCommandSender().sendSynch(command);
assert response.isSuccessful() : "agent2 should have been able to send to agent1: " + response;
assert agent1.getClientCommandSender().isSending() : "agent2 message should have re-started agent1 sender again";
// now let's entirely shutdown agent1 and try again, just to make sure the command listeners can be rebuilt
agent1.shutdown();
assert agent1.getClientCommandSender() == null : "agent1 should be completely shutdown";
assert agent2.getClientCommandSender().isSending() : "Should still be started";
agent1.start();
agent1.getClientCommandSender().getRemoteCommunicator().setInitializeCallback(null);
assert agent1.getClientCommandSender() != null : "agent1 should be started again";
assert !agent1.getClientCommandSender().isSending() : "Should still not yet be in sending mode again";
assert agent2.getClientCommandSender().isSending() : "Should still be started";
response = agent2.getClientCommandSender().sendSynch(command);
assert response.isSuccessful() : "agent2 should have been able to send to agent1: " + response;
assert agent1.getClientCommandSender().isSending() : "agent2 message should have re-started agent1 sender again";
} finally {
if (agent1 != null) {
agent1.shutdown();
}
if (agent2 != null) {
agent2.shutdown();
}
}
return;
}
/**
* Tests a remote POJO invocation that throws declared and undeclared exceptions.
*
* @throws Throwable
*/
@Test(enabled = ENABLE_TESTS)
public void testRemotePojoExceptions() throws Throwable {
Properties props2 = new Properties();
props2.setProperty(ServiceContainerConfigurationConstants.REMOTE_POJOS, SimpleTestAnnotatedPojo.class.getName()
+ ':' + ITestAnnotatedPojo.class.getName());
m_agent2Test.setConfigurationOverrides(props2);
AgentMain agent1 = m_agent1Test.createAgent(true);
AgentMain agent2 = m_agent2Test.createAgent(true);
assert agent1.isStarted() : "agent1 should have been started";
assert agent2.isStarted() : "agent2 should have been started";
ITestAnnotatedPojo pojo = agent1.getClientCommandSender().getClientRemotePojoFactory().getRemotePojo(
ITestAnnotatedPojo.class);
try {
// AutoDiscoveryException matches the throws clause - will not be wrapped
pojo.throwSpecificException(new AutoDiscoveryException("should not be wrapped"));
} catch (AutoDiscoveryException expected) {
assert expected.getMessage().equals("should not be wrapped");
}
try {
// IOException matches the throws clause - will not be wrapped
pojo.throwSpecificException(new IOException("should not be wrapped"));
} catch (IOException expected) {
assert expected.getMessage().equals("should not be wrapped");
}
try {
// IllegalArgumentException doesn't match throws but is a java.* runtime exception - will not be wrapped
pojo.throwSpecificException(new IllegalArgumentException("should not be wrapped"));
} catch (IllegalArgumentException expected) {
assert expected.getMessage().equals("should not be wrapped");
}
try {
// IllegalArgumentException doesn't match throws but it and its causes are java.* exceptions - will not be wrapped
pojo.throwSpecificException(new IllegalArgumentException("should not be wrapped", new Exception("inner 1",
new IllegalStateException("inner 2"))));
} catch (IllegalArgumentException expected) {
assert expected.getMessage().equals("should not be wrapped");
assert expected.getCause() instanceof Exception;
assert expected.getCause().getMessage().equals("inner 1");
assert expected.getCause().getCause() instanceof IllegalStateException;
assert expected.getCause().getCause().getMessage().equals("inner 2");
}
try {
// AutoDiscoveryException match throws so we assume the developer knows what he is doing and only
// includes causes within it that are allowed to be sent over the wire - it is not wrapped
pojo.throwSpecificException(new AutoDiscoveryException("should not be wrapped", new CannotConnectException(
"inner exception")));
} catch (AutoDiscoveryException expected) {
assert expected.getMessage().equals("should not be wrapped");
assert expected.getCause() instanceof CannotConnectException;
assert expected.getCause().getMessage().equals("inner exception");
}
try {
// IllegalArgumentException doesn't match throws but is a java.* runtime exception - however, its cause
// is a non-java.* exception - it should be wrapped entirely
pojo.throwSpecificException(new IllegalArgumentException("should be wrapped", new CannotConnectException(
"inner exception")));
} catch (WrappedRemotingException expected) {
assert expected.getMessage().indexOf("should be wrapped") != -1;
assert expected.getCause().getMessage().indexOf("inner exception") != -1;
assert ((WrappedRemotingException) expected.getCause()).getActualException().getExceptionName().equals(
CannotConnectException.class.getName());
}
try {
// doesn't match throws and is not a java.* exception - will be wrapped
pojo.throwSpecificException(new CannotConnectException("should be wrapped"));
} catch (WrappedRemotingException expected) {
assert expected.getMessage().indexOf("should be wrapped") != -1;
assert expected.getActualException().getExceptionName().equals(CannotConnectException.class.getName());
}
try {
pojo.returnNonSerializableObject(new Thread()); // opps, Thread is not serializable, never even gets sent over the wire
} catch (UndeclaredThrowableException expected) {
assert expected.getCause().getClass().equals(InvocationFailureException.class);
assert expected.getCause().getCause().getClass().equals(NotSerializableException.class);
} catch (Throwable t) {
assert false : "Unexpected exception: " + ThrowableUtil.getAllMessages(t); // this is here so I can put a breakpoint and see what exceptions are thrown
}
try {
pojo.throwNonSerializableException();
} catch (WrappedRemotingException expected) {
assert expected.getActualException().getExceptionName() != null;
} catch (Throwable t) {
t.printStackTrace();
assert false : "Unexpected exception: " + ThrowableUtil.getAllMessages(t); // this is here so I can put a breakpoint and see what exceptions are thrown
}
return;
}
/**
* Tests client command sender state listeners.
*
* @throws Exception
*/
@Test(enabled = ENABLE_TESTS)
public void testClientCommandSenderStateListener() throws Exception {
AgentMain agent1 = m_agent1Test.createAgent(true);
m_agent2Test.createAgent(true);
final Boolean[] state = new Boolean[1];
ClientCommandSenderStateListener listener = new ClientCommandSenderStateListener() {
public boolean stoppedSending(ClientCommandSender sender) {
state[0] = Boolean.FALSE;
return true;
}
public boolean startedSending(ClientCommandSender sender) {
state[0] = Boolean.TRUE;
return true;
}
};
assert state[0] == null : "This was just to prove we are null at the start - very weird for this to fail";
assert agent1.getClientCommandSender().isSending() : "Setup of this test should have started the sender";
agent1.getClientCommandSender().addStateListener(listener, false);
assert state[0] == null : "We told it not to notify us immediately - should not have called the listener";
agent1.getClientCommandSender().removeStateListener(listener);
agent1.getClientCommandSender().addStateListener(listener, true);
assert state[0].booleanValue() : "Listener should have been told the sender is sending";
agent1.getClientCommandSender().stopSending(false);
assert !state[0].booleanValue() : "Listener should have told us we are no longer sending";
agent1.getClientCommandSender().startSending();
assert state[0].booleanValue() : "Listener should have told us we are sending again";
agent1.getClientCommandSender().removeStateListener(listener);
ClientCommandSenderStateListener one_time_listener = new ClientCommandSenderStateListener() {
public boolean stoppedSending(ClientCommandSender sender) {
state[0] = Boolean.FALSE;
return false;
}
public boolean startedSending(ClientCommandSender sender) {
state[0] = Boolean.TRUE;
return false;
}
};
state[0] = null;
agent1.getClientCommandSender().addStateListener(one_time_listener, true);
assert state[0].booleanValue() : "One-time listener should have been told the sender is sending";
agent1.getClientCommandSender().stopSending(false);
assert state[0].booleanValue() : "One-time listener should not have been told the sender is not sending";
agent1.getClientCommandSender().addStateListener(one_time_listener, true);
assert !state[0].booleanValue() : "One-time listener should have been told the sender is not sending";
agent1.getClientCommandSender().startSending();
assert !state[0].booleanValue() : "One-time listener should not have been told the sender is sending";
return;
}
/**
* Tests getting results from asynch remote pojo invocation using the invocation future object.
*
* @throws Throwable
*/
@Test(enabled = ENABLE_TESTS)
public void testRemotePojoInvocationFuture() throws Throwable {
Properties props2 = new Properties();
props2.setProperty(ServiceContainerConfigurationConstants.REMOTE_POJOS, SimpleTestAnnotatedPojo.class.getName()
+ ':' + ITestAnnotatedPojo.class.getName());
m_agent2Test.setConfigurationOverrides(props2);
AgentMain agent1 = m_agent1Test.createAgent(true);
AgentMain agent2 = m_agent2Test.createAgent(true);
assert agent1.isStarted() : "agent1 should have been started";
assert agent2.isStarted() : "agent2 should have been started";
RemotePojoInvocationFuture future = new RemotePojoInvocationFuture();
ClientRemotePojoFactory factory = agent1.getClientCommandSender().getClientRemotePojoFactory();
factory.setAsynch(false, future); // false should still honor annotations - we'll make sure that's true in this test
ITestAnnotatedPojo pojo = factory.getRemotePojo(ITestAnnotatedPojo.class);
pojo.volatileMethod("first test");
assert "first test".equals(future.get());
assert future.isDone();
long stopwatch = System.currentTimeMillis();
assert "first test".equals(future.get(10, TimeUnit.SECONDS)); // make sure its still there
long test_duration = System.currentTimeMillis() - stopwatch;
assert test_duration < 750L : "get should have returned immediately: " + test_duration;
assert future.isDone();
future.reset();
assert !future.isDone();
stopwatch = System.currentTimeMillis();
try {
future.get(2, TimeUnit.SECONDS);
assert false : "The get should have timed out";
} catch (TimeoutException toe) {
}
test_duration = System.currentTimeMillis() - stopwatch;
assert (test_duration > 1900L) && (test_duration < 2500L) : "Should have timed out after 2 seconds: "
+ test_duration;
// we want to call throwThrowable asynchronously but it isn't annotated that way, so force async
// calling this isn't enough - all existing proxies remain as-is - this call only affects newly created remote pojo proxies
factory.setAsynch(true, future);
factory.setIgnoreAnnotations(true);
// test throwing an Error
Error err = new Error("bogus error for testing");
// side-test - show that the proxy isn't forcing all methods to be async.
try {
pojo.throwThrowable(err);
assert false : "Should have called this synchronously which should have thrown Error";
} catch (Error error) {
// to be expected, the remote pojo proxy is still configured to call throwThrowable synchronously
}
// now let's get a new remote pojo proxy that is forced to call everything asynchronously
pojo = factory.getRemotePojo(ITestAnnotatedPojo.class);
pojo.throwThrowable(err);
try {
future.get();
assert false : "Should have thrown an exception";
} catch (ExecutionException ee) {
assert "bogus error for testing".equals(ee.getCause().getMessage());
assert ee.getCause() instanceof Error;
}
// test throwing an Error
future.reset();
pojo.throwThrowable(new RuntimeException("bogus runtime exception for testing"));
try {
future.getAndReset();
assert false : "Should have thrown an exception";
} catch (ExecutionException ee) {
assert "bogus runtime exception for testing".equals(ee.getCause().getMessage());
assert ee.getCause() instanceof RuntimeException;
}
return;
}
/**
* Tests multiple command preprocessors.
*
* @throws Exception
*/
@Test(enabled = ENABLE_TESTS)
public void testMultipleCommandPreprocessors() throws Exception {
Properties props1 = new Properties();
props1.setProperty(AgentConfigurationConstants.CLIENT_SENDER_COMMAND_PREPROCESSORS,
SimpleCommandPreprocessor.class.getName() + ":" + SimpleCounterCommandPreprocessor.class.getName());
m_agent1Test.setConfigurationOverrides(props1);
AgentMain agent1 = m_agent1Test.createAgent(true);
m_agent2Test.createAgent(true);
Thread.sleep(2000L); // wait for the initial connectAgent messages to get sent
assert SimpleCounterCommandPreprocessor.COUNTER.get() == 0L : "Counter should have been reset to 0";
agent1.getClientCommandSender().sendSynch(new IdentifyCommand());
assert SimpleCounterCommandPreprocessor.COUNTER.get() == 1L : "Should have counted 1 commands (there should be no auto-connectAgent at startup)";
agent1.getClientCommandSender().sendSynch(new IdentifyCommand());
assert SimpleCounterCommandPreprocessor.COUNTER.get() == 2L : "Should have counted 2 commands (there should be no auto-connectAgent at startup)";
agent1.getClientCommandSender().sendSynch(new IdentifyCommand());
assert SimpleCounterCommandPreprocessor.COUNTER.get() == 3L : "Should have counted 3 commands (there should be no auto-connectAgent at startup)";
return;
}
/**
* Sends a message from one remote server to another using command preprocessor/authenticator to perform security
* checks.
*
* @throws Exception
*/
@Test(enabled = ENABLE_TESTS)
public void testCommandPreprocessorAuthenticator() throws Exception {
// agent1 has both preprocessor and authenticator so it can send and receive securely
// agent2 only has the authenticator - no preprocessor so when it sends to agent1 it won't be authenticatable
Properties props1 = new Properties();
props1.setProperty(AgentConfigurationConstants.CLIENT_SENDER_COMMAND_PREPROCESSORS,
SimpleCommandPreprocessor.class.getName());
props1.setProperty(ServiceContainerConfigurationConstants.COMMAND_AUTHENTICATOR,
SimpleCommandAuthenticator.class.getName());
m_agent1Test.setConfigurationOverrides(props1);
Properties props2 = new Properties();
props2.setProperty(AgentConfigurationConstants.CLIENT_SENDER_COMMAND_PREPROCESSORS, "");
props2.setProperty(ServiceContainerConfigurationConstants.COMMAND_AUTHENTICATOR,
SimpleCommandAuthenticator.class.getName());
m_agent2Test.setConfigurationOverrides(props2);
AgentMain agent1 = m_agent1Test.createAgent(true);
AgentMain agent2 = m_agent2Test.createAgent(true);
IdentifyCommandResponse response;
response = (IdentifyCommandResponse) agent1.getClientCommandSender().sendSynch(new IdentifyCommand());
assert response.isSuccessful() : "Failed to send command from agent1 to agent2";
assert new InvokerLocator(response.getIdentification().getInvokerLocator()).getPort() == agent2
.getServiceContainer().getConfiguration().getConnectorBindPort() : "Didn't get the identify of agent2 - what remoting server did we just communicate with??";
CommandResponse generic_response;
generic_response = agent2.getClientCommandSender().sendSynch(new IdentifyCommand());
assert !generic_response.isSuccessful() : "Should have failed to send command from agent2 to agent1 since it didn't preprocess the command";
assert generic_response.getException() != null;
return;
}
/**
* Tests a remote POJO interface with send throttling annotation.
*
* @throws Exception
*/
@Test(enabled = ENABLE_TESTS)
public void testRemotePojoAnnotationsSendThrottled() throws Exception {
Properties props1 = new Properties();
props1.setProperty(AgentConfigurationConstants.CLIENT_SENDER_SEND_THROTTLING, "5:7000");
m_agent1Test.setConfigurationOverrides(props1);
Properties props2 = new Properties();
props2.setProperty(ServiceContainerConfigurationConstants.REMOTE_POJOS, SimpleTestAnnotatedPojo.class.getName()
+ ':' + ITestAnnotatedPojo.class.getName());
m_agent2Test.setConfigurationOverrides(props2);
AgentMain agent1 = m_agent1Test.createAgent(true);
AgentMain agent2 = m_agent2Test.createAgent(true);
assert agent1.isStarted() : "agent1 should have been started";
assert agent2.isStarted() : "agent2 should have been started";
ITestAnnotatedPojo pojo = agent1.getClientCommandSender().getClientRemotePojoFactory().getRemotePojo(
ITestAnnotatedPojo.class);
// blast 6 calls in a row - we will throttle after the 5th, thus pausing for 7 seconds
long stopwatch = System.currentTimeMillis();
for (int i = 1; i <= 6; i++) {
assert i == pojo.sendThrottled(i);
}
long test_duration = System.currentTimeMillis() - stopwatch;
assert test_duration > 6950L : "Send throttling was on - should have a pause: " + test_duration;
// test send throttling that has been explicitly turned off but our agent is configured for send throttling
stopwatch = System.currentTimeMillis();
for (int i = 1; i <= 6; i++) {
assert i == pojo.notSendThrottled(i);
}
test_duration = System.currentTimeMillis() - stopwatch;
assert test_duration < 7000L : "Send throttling was off - should have returned very fast: " + test_duration;
// let's ignore annotations and try that one that wanted to explicitly turn off send throttling
// since we are ignoring the annotations, our invocation will be send throttled
ClientRemotePojoFactory factory = agent1.getClientCommandSender().getClientRemotePojoFactory();
factory.setIgnoreAnnotations(true);
pojo = factory.getRemotePojo(ITestAnnotatedPojo.class);
stopwatch = System.currentTimeMillis();
for (int i = 1; i <= 6; i++) {
assert i == pojo.notSendThrottled(i);
}
test_duration = System.currentTimeMillis() - stopwatch;
assert test_duration > 6950L : "Annotations were ignored, should have been send throttled: " + test_duration;
return;
}
/**
* Tests a remote POJO interface with timeout annotation.
*
* @throws Exception
*/
@Test(enabled = ENABLE_TESTS)
public void testRemotePojoAnnotationsTimeout() throws Exception {
Properties props2 = new Properties();
props2.setProperty(ServiceContainerConfigurationConstants.REMOTE_POJOS, SimpleTestAnnotatedPojo.class.getName()
+ ':' + ITestAnnotatedPojo.class.getName());
m_agent2Test.setConfigurationOverrides(props2);
AgentMain agent1 = m_agent1Test.createAgent(true);
AgentMain agent2 = m_agent2Test.createAgent(true);
assert agent1.isStarted() : "agent1 should have been started";
assert agent2.isStarted() : "agent2 should have been started";
ITestAnnotatedPojo pojo = agent1.getClientCommandSender().getClientRemotePojoFactory().getRemotePojo(
ITestAnnotatedPojo.class);
boolean exception_was_thrown = false;
try {
pojo.shortAnnotatedTimeout("first");
} catch (Throwable t) {
exception_was_thrown = true;
}
assert exception_was_thrown : "The method should have timed out and thrown an exception";
ClientRemotePojoFactory factory = agent1.getClientCommandSender().getClientRemotePojoFactory();
factory.setIgnoreAnnotations(true);
pojo = factory.getRemotePojo(ITestAnnotatedPojo.class);
// since we are ignoring the annotations, our default timeout should be enough for us to succeed
assert "second".equals(pojo.shortAnnotatedTimeout("second"));
return;
}
/**
* Tests guaranteed delivery with a remote POJO interface with annotations.
*
* @throws Exception
*/
@Test(enabled = ENABLE_TESTS)
public void testRemotePojoAnnotationsGuaranteedDelivery() throws Exception {
Properties props2 = new Properties();
props2.setProperty(ServiceContainerConfigurationConstants.REMOTE_POJOS, SimpleTestAnnotatedPojo.class.getName()
+ ':' + ITestAnnotatedPojo.class.getName());
m_agent2Test.setConfigurationOverrides(props2);
// set timeout so the auto-connectAgent times out quick
Properties props1 = new Properties();
props1.setProperty(AgentConfigurationConstants.CLIENT_SENDER_COMMAND_TIMEOUT, "5000");
m_agent1Test.setConfigurationOverrides(props1);
AgentMain agent1 = m_agent1Test.createAgent(true);
assert agent1.isStarted() : "agent1 should have been started";
final String[] results = new String[] { null };
final Throwable[] throwable = new Throwable[] { null };
final Boolean[] success = new Boolean[] { null };
ClientRemotePojoFactory factory = agent1.getClientCommandSender().getClientRemotePojoFactory();
factory.setAsynch(false, new CommandResponseCallback() {
private static final long serialVersionUID = 1L;
public void commandSent(CommandResponse response) {
success[0] = Boolean.valueOf(response.isSuccessful());
results[0] = (String) response.getResults();
throwable[0] = response.getException();
synchronized (success) {
success.notify();
}
}
});
ITestAnnotatedPojo pojo = factory.getRemotePojo(ITestAnnotatedPojo.class);
// see that the volatile method failed because we haven't started our second server agent
pojo.volatileMethod("call 1");
synchronized (success) {
success.wait(30000L); // this will get notified immediately when our volatile method callback is called with the failure
}
assert !success[0].booleanValue() : "The volatile method should have failed - there is no remote endpoint to service it";
assert throwable[0] != null : "The volatile method should have failed - there is no remote endpoint to service it";
assert results[0] == null : "The volatile method should have failed - there is no remote endpoint to service it";
throwable[0] = null;
success[0] = null;
results[0] = null;
// make a guaranteed method call - it should get persisted waiting for us to start our second remote endpoint
pojo.guaranteedMethod("call 2");
Thread.sleep(5000);
assert success[0] == null : "The method invocation should not have been performed yet - it should be persisted";
// start a second agent to be used as our server-side remote endpoint
synchronized (success) {
AgentMain agent2 = m_agent2Test.createAgent(true);
assert agent2.isStarted() : "agent2 should have been started";
// see that our guaranteed method invocation worked
success.wait(30000L); // this will get notified immediately when our guaranteed method callback is called with the success
}
assert success[0].booleanValue() : "The guaranteed method should have succeeded - there is now a remote endpoint to service it";
assert throwable[0] == null : "The guaranteed method should have succeeded - there is now a remote endpoint to service it";
assert "call 2".equals(results[0]) : "The guaranteed method should have succeeded - there is now a remote endpoint to service it";
throwable[0] = null;
success[0] = null;
results[0] = null;
// for good measure, show that our volatile, non-guaranteed method will work too
pojo.volatileMethod("call 3");
synchronized (success) {
success.wait(30000L); // this will get notified immediately when our volatile method callback is called with the success
}
assert success[0].booleanValue() : "The volatile method should have succeeded - there is now a remote endpoint to service it";
assert throwable[0] == null : "The volatile method should have succeeded - there is now a remote endpoint to service it";
assert "call 3".equals(results[0]) : "The volatile method should have succeeded - there is now a remote endpoint to service it";
return;
}
}