/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat, Inc., 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.client.helpers.standalone.impl; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.jboss.as.controller.client.Operation; import org.jboss.as.controller.client.helpers.standalone.DeploymentPlan; import org.jboss.as.controller.client.helpers.standalone.DeploymentPlanBuilder; import org.jboss.as.controller.client.helpers.standalone.ServerDeploymentManager; import org.jboss.as.controller.client.helpers.standalone.ServerDeploymentPlanResult; import org.jboss.dmr.ModelNode; import org.junit.After; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /** * Tests for behavior re: cleaning up InputStreams, e.g. AS7-2392. * * @author Brian Stansberry (c) 2011 Red Hat Inc. */ public class InputStreamCleanupTestCase { private static File baseDir; private static long count = System.currentTimeMillis(); @BeforeClass public static void beforeClass() { File tmpDir = new File(System.getProperty("java.io.tmpdir")); baseDir = new File(tmpDir, InputStreamCleanupTestCase.class.getSimpleName()); baseDir.deleteOnExit(); baseDir.mkdirs(); } @AfterClass public static void afterClass() { File[] children = baseDir.listFiles(); if (children != null) { for (File child : children) { child.delete(); } } baseDir.delete(); } private InputStream testStream; @After public void after() throws IOException { if (testStream != null) { testStream.close(); } } @Test public void testFileSuccess() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeGet(sdm, planImpl); InputStream is = getInputStream(planImpl); assertClosed(is); } @Test public void testURLSuccess() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempURL()); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeGet(sdm, planImpl); InputStream is = getInputStream(planImpl); assertClosed(is); } @Test public void testStreamSuccess() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); createTempInputStream(); builder = builder.add("test", testStream); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeGet(sdm, planImpl); assertNotClosed(testStream); } @Test public void testSuccessfulCancel() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(true); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeCancel(sdm, planImpl); InputStream is = getInputStream(planImpl); assertClosed(is); } @Test public void testUnsuccessfulCancel() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(false); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeCancel(sdm, planImpl); InputStream is = getInputStream(planImpl); assertNotClosed(is); } @Test public void testInterrupted() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(new InterruptedException()); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeGet(sdm, planImpl); InputStream is = getInputStream(planImpl); assertNotClosed(is); builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); planImpl = getDeploymentPlanImpl(builder); safeGetWithTimeout(sdm, planImpl); is = getInputStream(planImpl); assertNotClosed(is); } @Test public void testTimeoutException() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(new TimeoutException()); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeGetWithTimeout(sdm, planImpl); InputStream is = getInputStream(planImpl); assertNotClosed(is); } @Test public void testExecutionException() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(new ExecutionException(new Exception())); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeGet(sdm, planImpl); InputStream is = getInputStream(planImpl); assertClosed(is); builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); planImpl = getDeploymentPlanImpl(builder); safeGetWithTimeout(sdm, planImpl); is = getInputStream(planImpl); assertClosed(is); } @Test public void testRuntimeException() throws IOException { ServerDeploymentManager sdm = new MockServerDeploymentManager(new RuntimeException()); DeploymentPlanBuilder builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); DeploymentPlanImpl planImpl = getDeploymentPlanImpl(builder); safeGet(sdm, planImpl); InputStream is = getInputStream(planImpl); assertClosed(is); builder = sdm.newDeploymentPlan(); builder = builder.add("test", createTempFile()); planImpl = getDeploymentPlanImpl(builder); safeGetWithTimeout(sdm, planImpl); is = getInputStream(planImpl); assertClosed(is); } private static File createTempFile() throws IOException { File file = new File(baseDir, String.valueOf(count++)); file.deleteOnExit(); file.createNewFile(); return file; } private static URL createTempURL() throws IOException { return createTempFile().toURI().toURL(); } private InputStream createTempInputStream() throws IOException { this.testStream = new FileInputStream(createTempFile()); return testStream; } private static DeploymentPlanImpl getDeploymentPlanImpl(DeploymentPlanBuilder builder) { return DeploymentPlanImpl.class.cast(builder.build()); } private static void safeGet(ServerDeploymentManager sdm, DeploymentPlan plan) { Future<ServerDeploymentPlanResult> future = sdm.execute(plan); try { future.get(); } catch (Exception ignored) { // ignore } } private static void safeGetWithTimeout(ServerDeploymentManager sdm, DeploymentPlan plan) { Future<ServerDeploymentPlanResult> future = sdm.execute(plan); try { future.get(1L, TimeUnit.MILLISECONDS); } catch (Exception ignored) { // ignore } } private static void safeCancel(ServerDeploymentManager sdm, DeploymentPlan plan) { Future<ServerDeploymentPlanResult> future = sdm.execute(plan); try { future.cancel(true); } catch (Exception ignored) { // ignore } } private static InputStream getInputStream(DeploymentPlanImpl planImpl) { for (DeploymentActionImpl action : planImpl.getDeploymentActionImpls()) { if (action.getContentStream() != null) { return action.getContentStream(); } } throw new IllegalStateException("no content stream available"); } private void assertClosed(InputStream is) { try { is.available(); Assert.fail("InputStream is not closed"); } catch (IOException good) { // we assume the IOE is because it's closed } } private void assertNotClosed(InputStream is) { try { is.available(); } catch (IOException good) { // we assume the IOE is because it's closed Assert.fail("InputStream is closed"); } } private static class MockServerDeploymentManager extends AbstractServerDeploymentManager { private Exception exception; private Boolean cancelled; private MockServerDeploymentManager() { } private MockServerDeploymentManager(boolean cancelled) { this.cancelled = cancelled; } private MockServerDeploymentManager(Exception e) { exception = e; } @Override protected Future<ModelNode> executeOperation(Operation context) { if (exception != null) { return new TestFuture(exception); } else if (cancelled != null) { return new TestFuture(cancelled); } else { return new TestFuture(); } } @Override public void close() throws IOException { // } } private static class TestFuture implements Future<ModelNode> { private Exception exception; private boolean cancelled; private TestFuture() { } private TestFuture(boolean cancelled) { this.cancelled = cancelled; } private TestFuture(Exception e) { exception = e; } @Override public boolean cancel(boolean mayInterruptIfRunning) { return cancelled; } @Override public boolean isCancelled() { throw new UnsupportedOperationException(); } @Override public boolean isDone() { throw new UnsupportedOperationException(); } @Override public ModelNode get() throws InterruptedException, ExecutionException { if (exception == null) { return new ModelNode(); } else if (exception instanceof InterruptedException) { throw (InterruptedException) exception; } else if (exception instanceof ExecutionException) { throw (ExecutionException) exception; } throw (RuntimeException) exception; } @Override public ModelNode get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (exception == null) { return new ModelNode(); } else if (exception instanceof InterruptedException) { throw (InterruptedException) exception; } else if (exception instanceof ExecutionException) { throw (ExecutionException) exception; } else if (exception instanceof TimeoutException) { throw (TimeoutException) exception; } throw (RuntimeException) exception; } } }