/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.controller; import static java.security.AccessController.doPrivileged; import static org.junit.Assert.assertEquals; import java.io.ByteArrayInputStream; import java.io.DataOutput; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.jboss.as.controller.client.MessageSeverity; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.as.controller.client.Operation; import org.jboss.as.controller.client.OperationAttachments; import org.jboss.as.controller.client.OperationBuilder; import org.jboss.as.controller.client.OperationMessageHandler; import org.jboss.as.controller.client.impl.ExistingChannelModelControllerClient; import org.jboss.as.controller.client.impl.InputStreamEntry; import org.jboss.as.controller.registry.NotificationHandlerRegistration; import org.jboss.as.controller.remote.ModelControllerClientOperationHandler; import org.jboss.as.controller.remote.ResponseAttachmentInputStreamSupport; import org.jboss.as.controller.support.RemoteChannelPairSetup; import org.jboss.as.protocol.mgmt.ManagementChannelHandler; import org.jboss.as.protocol.mgmt.ManagementClientChannelStrategy; import org.jboss.as.protocol.mgmt.support.ManagementChannelInitialization; import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; import org.jboss.remoting3.Channel; import org.jboss.threads.AsyncFuture; import org.jboss.threads.JBossThreadFactory; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.xnio.IoUtils; /** * * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> * @version $Revision: 1.1 $ */ public class ModelControllerClientTestCase { Logger log = Logger.getLogger(ModelControllerClientTestCase.class); private RemoteChannelPairSetup channels; @Before public void start() throws Exception { channels = new RemoteChannelPairSetup(); } @After public void stop() throws Exception { channels.stopChannels(); channels.shutdownRemoting(); } private ModelControllerClient setupTestClient(final ModelController controller) throws IOException { try { channels.setupRemoting(new ManagementChannelInitialization() { @Override public ManagementChannelHandler startReceiving(Channel channel) { final ManagementClientChannelStrategy strategy = ManagementClientChannelStrategy.create(channel); final ManagementChannelHandler support = new ManagementChannelHandler(strategy, channels.getExecutorService()); support.addHandlerFactory(new ModelControllerClientOperationHandler(controller, support, new ResponseAttachmentInputStreamSupport(), getClientRequestExecutor())); channel.receiveMessage(support.getReceiver()); return support; } private ExecutorService getClientRequestExecutor() { final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(512); final ThreadFactory threadFactory = doPrivileged(new PrivilegedAction<ThreadFactory>() { public ThreadFactory run() { return new JBossThreadFactory(new ThreadGroup("management-handler-thread"), Boolean.FALSE, null, "%G - %t", null, null); } }); ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 4, 250L, TimeUnit.MILLISECONDS, workQueue, threadFactory); // Allow the core threads to time out as well executor.allowCoreThreadTimeOut(true); return executor; } }); channels.startClientConnetion(); } catch (Exception e) { throw new RuntimeException(e); } final Channel clientChannel = channels.getClientChannel(); return ExistingChannelModelControllerClient.createReceiving(clientChannel, channels.getExecutorService()); } @Test @Ignore("WFCORE-1125") public void testSynchronousOperationMessageHandler() throws Exception { final CountDownLatch executeLatch = new CountDownLatch(1); MockModelController controller = new MockModelController() { @Override public ModelNode execute(ModelNode operation, OperationMessageHandler handler, OperationTransactionControl control, OperationAttachments attachments) { this.operation = operation; handler.handleReport(MessageSeverity.INFO, "Test1"); handler.handleReport(MessageSeverity.INFO, "Test2"); executeLatch.countDown(); ModelNode result = new ModelNode(); result.get("testing").set(operation.get("test")); return result; } }; final ModelControllerClient client = setupTestClient(controller); try { ModelNode operation = new ModelNode(); operation.get("test").set("123"); final BlockingQueue<String> messages = new LinkedBlockingQueue<String>(); ModelNode result = client.execute(operation, new OperationMessageHandler() { @Override public void handleReport(MessageSeverity severity, String message) { if (severity == MessageSeverity.INFO && message.startsWith("Test")) { messages.add(message); } } }); assertEquals("123", controller.getOperation().get("test").asString()); assertEquals("123", result.get("testing").asString()); assertEquals("Test1", messages.take()); assertEquals("Test2", messages.take()); } finally { IoUtils.safeClose(client); } } @Test public void testSynchronousAttachmentInputStreams() throws Exception { final byte[] firstBytes = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; final byte[] secondBytes = new byte[] {10, 9, 8 , 7 , 6, 5, 4, 3, 2, 1}; final byte[] thirdBytes = new byte[] {1}; final CountDownLatch executeLatch = new CountDownLatch(1); final AtomicInteger size = new AtomicInteger(); final AtomicReference<byte[]> firstResult = new AtomicReference<byte[]>(); final AtomicReference<byte[]> secondResult = new AtomicReference<byte[]>(); final AtomicReference<byte[]> thirdResult = new AtomicReference<byte[]>(); MockModelController controller = new MockModelController() { @Override public ModelNode execute(ModelNode operation, OperationMessageHandler handler, OperationTransactionControl control, OperationAttachments attachments) { int streamIndex = 0; for (InputStream in : attachments.getInputStreams()) { try { ArrayList<Integer> readBytes = new ArrayList<Integer>(); int b = in.read(); while (b != -1) { readBytes.add(b); b = in.read(); } byte[] bytes = new byte[readBytes.size()]; for (int i = 0 ; i < bytes.length ; i++) { bytes[i] = (byte)readBytes.get(i).intValue(); } if (streamIndex == 0) { firstResult.set(bytes); } else if (streamIndex == 1) { secondResult.set(bytes); } else if (streamIndex == 2) { thirdResult.set(bytes); } streamIndex++; } catch (IOException e) { e.printStackTrace(); } } size.set(streamIndex); executeLatch.countDown(); return new ModelNode(); } }; // Set the handler final ModelControllerClient client = setupTestClient(controller); try { ModelNode op = new ModelNode(); op.get("operation").set("fake"); op.get("name").set(123); OperationBuilder builder = new OperationBuilder(op); builder.addInputStream(new ByteArrayInputStream(firstBytes)); builder.addInputStream(new ByteArrayInputStream(secondBytes)); builder.addInputStream(new ByteArrayInputStream(thirdBytes)); client.execute(builder.build()); executeLatch.await(); assertEquals(3, size.get()); assertArrays(firstBytes, firstResult.get()); assertArrays(secondBytes, secondResult.get()); assertArrays(new byte[] { 1 }, thirdResult.get()); } finally { IoUtils.safeClose(client); } } @Test @Ignore("WFCORE-1125") public void testAsynchronousOperationWithMessageHandler() throws Exception { final CountDownLatch executeLatch = new CountDownLatch(1); MockModelController controller = new MockModelController() { @Override public ModelNode execute(ModelNode operation, OperationMessageHandler handler, OperationTransactionControl control, OperationAttachments attachments) { this.operation = operation; handler.handleReport(MessageSeverity.INFO, "Test1"); handler.handleReport(MessageSeverity.INFO, "Test2"); executeLatch.countDown(); ModelNode result = new ModelNode(); result.get("testing").set(operation.get("test")); return result; } }; // Set the handler final ModelControllerClient client = setupTestClient(controller); try { ModelNode operation = new ModelNode(); operation.get("test").set("123"); operation.get("operation").set("fake"); final BlockingQueue<String> messages = new LinkedBlockingQueue<String>(); AsyncFuture<ModelNode> resultFuture = client.executeAsync(operation, new OperationMessageHandler() { @Override public void handleReport(MessageSeverity severity, String message) { if (severity == MessageSeverity.INFO && message.startsWith("Test")) { messages.add(message); } } }); ModelNode result = resultFuture.get(); assertEquals("123", controller.getOperation().get("test").asString()); assertEquals("123", result.get("testing").asString()); assertEquals("Test1", messages.take()); assertEquals("Test2", messages.take()); } finally { IoUtils.safeClose(client); } } @Test public void testCancelAsynchronousOperation() throws Exception { final CountDownLatch executeLatch = new CountDownLatch(1); final CountDownLatch interrupted = new CountDownLatch(1); MockModelController controller = new MockModelController() { @Override public ModelNode execute(ModelNode operation, OperationMessageHandler handler, OperationTransactionControl control, OperationAttachments attachments) { this.operation = operation; executeLatch.countDown(); try { log.debug("Waiting for interrupt"); //Wait for this operation to be cancelled Thread.sleep(10000000); ModelNode result = new ModelNode(); result.get("testing").set(operation.get("test")); return result; } catch (InterruptedException e) { interrupted.countDown(); throw new RuntimeException(e); } } }; // Set the handler final ModelControllerClient client = setupTestClient(controller); try { ModelNode operation = new ModelNode(); operation.get("test").set("123"); operation.get("operation").set("fake"); final BlockingQueue<String> messages = new LinkedBlockingQueue<String>(); AsyncFuture<ModelNode> resultFuture = client.executeAsync(operation, new OperationMessageHandler() { @Override public void handleReport(MessageSeverity severity, String message) { if (severity == MessageSeverity.INFO && message.startsWith("Test")) { messages.add(message); } } }); executeLatch.await(); resultFuture.cancel(false); interrupted.await(); } finally { IoUtils.safeClose(client); } } @Test public void testCloseInputStreamEntry() throws Exception { final MockModelController controller = new MockModelController() { @Override public ModelNode execute(ModelNode operation, OperationMessageHandler handler, OperationTransactionControl control, OperationAttachments attachments) { ModelNode result = new ModelNode(); result.get("testing").set(operation.get("test")); return result; } }; final ModelControllerClient client = setupTestClient(controller); try { final ModelNode op = new ModelNode(); op.get("operation").set("fake"); final TestEntry entry = new TestEntry(); final Operation operation = OperationBuilder.create(op) .addInputStream(entry) .build(); // Execute the operation client.execute(operation); // Check closed entry.latch.await(); Assert.assertTrue(entry.closed); } finally { IoUtils.safeClose(client); } } private void assertArrays(byte[] expected, byte[] actual) { assertEquals(expected.length, actual.length); for (int i = 0 ; i < expected.length ; i++) { assertEquals(expected[i], actual[i]); } } private abstract static class MockModelController extends org.jboss.as.controller.MockModelController { protected volatile ModelNode operation; private final NotificationHandlerRegistration notificationRegistry = NotificationHandlerRegistration.Factory.create(); ModelNode getOperation() { return operation; } @Override public NotificationHandlerRegistration getNotificationRegistry() { return notificationRegistry; } } static class TestEntry extends FilterInputStream implements InputStreamEntry { final CountDownLatch latch = new CountDownLatch(1); boolean closed = false; TestEntry() { super(new ByteArrayInputStream(new byte[0])); } @Override public void copyStream(DataOutput output) throws IOException { return; } @Override public int initialize() throws IOException { return 0; } @Override public void close() throws IOException { super.close(); closed = true; latch.countDown(); } } }