diff --git a/README.md b/README.md
index ecaef38..9aec0f5 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,9 @@ class TestSystemExit
}
```
-Note that in order to be able to trap system exit, the `ExitGuard` temporarily replaces the existing security manager (if any).
+The `ExitGuard` temporarily replaces the existing security manager.
+
+From version 1.2.0 on if a security guard existed before, it serves as a delegate for all security checks with the exception of the `checkExit`.
## Asserting Data Sent to `System.out`
@@ -91,6 +93,10 @@ Capturing data sent to `System.err` works in the exact same way as in the [`Syst
Please check our [contribution guide](.github/CONTRIBUTING.md) to learn how you can help with the project, report errors or request features.
+## Changelog
+
+[Changelog](doc/changes/changelog.md)
+
## Development
### Build Time Dependencies
@@ -177,4 +183,4 @@ mvn versions:display-plugin-updates
1. Merge `develop` to `master` branch
1. Create a [release](https://github.com/itsallcode/junit5-system-extensions/releases) of the `master` branch on GitHub.
-1. After some time the release will be available at [Maven Central](https://repo1.maven.org/maven2/org/itsallcode/junit5-system-extensions/).
+1. After some time the release will be available at [Maven Central](https://repo1.maven.org/maven2/org/itsallcode/junit5-system-extensions/).
\ No newline at end of file
diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md
new file mode 100644
index 0000000..bee3298
--- /dev/null
+++ b/doc/changes/changelog.md
@@ -0,0 +1,4 @@
+# Changes
+
+* [1.2.0](changes_1.2.0.md)
+* [1.1.0](changes_1.1.0.md)
\ No newline at end of file
diff --git a/doc/changes/changes_1.1.0.md b/doc/changes/changes_1.1.0.md
new file mode 100644
index 0000000..440b61d
--- /dev/null
+++ b/doc/changes/changes_1.1.0.md
@@ -0,0 +1,11 @@
+# JUnit5 System Extensions 1.1.0, released 2019-12-28
+
+Code name: Mute output
+
+## Summary
+
+To mute the output (i.e. don't forward output to `System.out` / `System.err`) call `stream.captureMuted()` instead of `stream.capture()`. This can be useful to speed up unit tests.
+
+## Features
+
+* [#16](https://github.com/itsallcode/junit5-system-extensions/issues/16): Mute output of captured streams
diff --git a/doc/changes/changes_1.2.0.md b/doc/changes/changes_1.2.0.md
new file mode 100644
index 0000000..bce39ef
--- /dev/null
+++ b/doc/changes/changes_1.2.0.md
@@ -0,0 +1,17 @@
+# JUnit5 System Extensions 1.2.0, released 2019-04-22
+
+Code name: Mute output
+
+## Summary
+
+When trapping `System.exit` calls, the `ExitGuardSecurityManager` now doesn't simply replace an existing security manager anymore. Instead it uses the existing one as a delegate for all checks except the exit check.
+
+This way applications that require a security manager don't change their behavior.
+
+## Features
+
+* [#23](https://github.com/itsallcode/junit5-system-extensions/issues/23): Delegate security manager calls to the original security manager
+
+## Refactoring
+
+* [#21](https://github.com/itsallcode/junit5-system-extensions/issues/21): Migrate to Maven Central
diff --git a/launch/j5se - run all tests.launch b/launch/j5se - run all tests.launch
index a81b840..01aec37 100644
--- a/launch/j5se - run all tests.launch
+++ b/launch/j5se - run all tests.launch
@@ -1,27 +1,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/itsallcode/junit/sysextensions/ExitGuard.java b/src/main/java/org/itsallcode/junit/sysextensions/ExitGuard.java
index c5b5670..9b4e61d 100644
--- a/src/main/java/org/itsallcode/junit/sysextensions/ExitGuard.java
+++ b/src/main/java/org/itsallcode/junit/sysextensions/ExitGuard.java
@@ -9,13 +9,13 @@
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
- *
+ *
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
- *
+ *
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
* #L%
*/
@@ -48,7 +48,8 @@ private void saveCurrentSecurityManager(final ExtensionContext context)
private void installExitGuardSecurityManager(final ExtensionContext context)
{
- final SecurityManager exitGuardSecurityManager = new ExitGuardSecurityManager();
+ final SecurityManager previousSecurityManager = getPreviousSecurityManager(context);
+ final SecurityManager exitGuardSecurityManager = new ExitGuardSecurityManager(previousSecurityManager);
System.setSecurityManager(exitGuardSecurityManager);
context.getStore(getNamespace()).put(EXIT_GUARD_SECURITY_MANAGER_KEY, exitGuardSecurityManager);
}
@@ -78,8 +79,13 @@ public void afterTestExecution(final ExtensionContext context) throws Exception
@Override
public void afterAll(final ExtensionContext context) throws Exception
{
- final SecurityManager previousManager = (SecurityManager) context.getStore(getNamespace())
- .get(PREVIOUS_SECURITY_MANAGER_KEY);
+ final SecurityManager previousManager = getPreviousSecurityManager(context);
System.setSecurityManager(previousManager);
}
-}
+
+ private SecurityManager getPreviousSecurityManager(final ExtensionContext context)
+ {
+ return (SecurityManager) context.getStore(getNamespace())
+ .get(PREVIOUS_SECURITY_MANAGER_KEY);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/itsallcode/junit/sysextensions/security/ExitGuardSecurityManager.java b/src/main/java/org/itsallcode/junit/sysextensions/security/ExitGuardSecurityManager.java
index 38163b5..fb1d524 100644
--- a/src/main/java/org/itsallcode/junit/sysextensions/security/ExitGuardSecurityManager.java
+++ b/src/main/java/org/itsallcode/junit/sysextensions/security/ExitGuardSecurityManager.java
@@ -1,5 +1,8 @@
package org.itsallcode.junit.sysextensions.security;
+import java.io.FileDescriptor;
+import java.net.InetAddress;
+
/*-
* #%L
* JUnit5 System Extensions
@@ -9,13 +12,13 @@
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
- *
+ *
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
- *
+ *
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
* #L%
*/
@@ -25,6 +28,12 @@
public class ExitGuardSecurityManager extends SecurityManager
{
private boolean trapExit = false;
+ private final SecurityManager delegate;
+
+ public ExitGuardSecurityManager(final SecurityManager delegate)
+ {
+ this.delegate = delegate;
+ }
@Override
public synchronized void checkExit(final int status)
@@ -46,11 +55,233 @@ public void checkPermission(final Permission perm)
/**
* Switch trapping {@link System#exit(int)} calls on or off.
*
- * @param trapExit set to true
if the
- * {@link ExitGuardSecurityManager} should trap exit calls
+ * @param trapExit
+ * set to true
if the
+ * {@link ExitGuardSecurityManager} should trap exit calls
*/
public void trapExit(final boolean trapExit)
{
this.trapExit = trapExit;
}
-}
+
+ private boolean hasDelegate()
+ {
+ return this.delegate != null;
+ }
+
+ @Override
+ public void checkAccept(final String host, final int port)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkAccept(host, port);
+ }
+ }
+
+ @Override
+ public void checkAccess(final Thread thread)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkAccess(thread);
+ }
+ }
+
+ @Override
+ public void checkAccess(final ThreadGroup group)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkAccess(group);
+ }
+ }
+
+ @Override
+ public void checkConnect(final String host, final int port, final Object context)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkConnect(host, port, context);
+ }
+ }
+
+ @Override
+ public void checkConnect(final String host, final int port)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkConnect(host, port);
+ }
+ }
+
+ @Override
+ public void checkCreateClassLoader()
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkCreateClassLoader();
+ }
+ }
+
+ @Override
+ public void checkDelete(final String file)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkDelete(file);
+ }
+ }
+
+ @Override
+ public void checkExec(final String cmd)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkExec(cmd);
+ }
+ }
+
+ @Override
+ public void checkLink(final String lib)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkLink(lib);
+ }
+ }
+
+ @Override
+ public void checkListen(final int port)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkListen(port);
+ }
+ }
+
+ @Override
+ public void checkMulticast(final InetAddress maddr)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkMulticast(maddr);
+ }
+ }
+
+ @Override
+ public void checkPackageAccess(final String pkg)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkPackageAccess(pkg);
+ }
+ }
+
+ @Override
+ public void checkPackageDefinition(final String pkg)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkPackageDefinition(pkg);
+ }
+ }
+
+ @Override
+ public void checkPermission(final Permission perm, final Object context)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkPermission(perm, context);
+ }
+ }
+
+ @Override
+ public void checkPrintJobAccess()
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkPrintJobAccess();
+ }
+ }
+
+ @Override
+ public void checkPropertiesAccess()
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkPropertiesAccess();
+ }
+ }
+
+ @Override
+ public void checkPropertyAccess(final String key)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkPropertyAccess(key);
+ }
+ }
+
+ @Override
+ public void checkRead(final FileDescriptor fd)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkRead(fd);
+ }
+ }
+
+ @Override
+ public void checkRead(final String file, final Object context)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkRead(file, context);
+ }
+ }
+
+ @Override
+ public void checkRead(final String file)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkRead(file);
+ }
+ }
+
+ @Override
+ public void checkSecurityAccess(final String target)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkSecurityAccess(target);
+ }
+ }
+
+ @Override
+ public void checkSetFactory()
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkSetFactory();
+ }
+ }
+
+ @Override
+ public void checkWrite(final FileDescriptor fileDescriptor)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkWrite(fileDescriptor);
+ }
+ }
+
+ @Override
+ public void checkWrite(final String file)
+ {
+ if (this.hasDelegate())
+ {
+ this.delegate.checkWrite(file);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/itsallcode/junit/sysextensions/TestSystemExit.java b/src/test/java/org/itsallcode/junit/sysextensions/TestSystemExit.java
index e6ebc97..5d6a2b8 100644
--- a/src/test/java/org/itsallcode/junit/sysextensions/TestSystemExit.java
+++ b/src/test/java/org/itsallcode/junit/sysextensions/TestSystemExit.java
@@ -1,5 +1,3 @@
-package org.itsallcode.junit.sysextensions;
-
/*-
* #%L
* JUnit5 System Extensions
@@ -9,16 +7,17 @@
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
- *
+ *
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is
* available at https://www.gnu.org/software/classpath/license.html.
- *
+ *
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
* #L%
*/
+package org.itsallcode.junit.sysextensions;
import static org.itsallcode.junit.sysextensions.AssertExit.assertExit;
import static org.itsallcode.junit.sysextensions.AssertExit.assertExitWithStatus;
@@ -45,7 +44,8 @@ void testSystemExitMissingThrowsAssertError()
assertExit(() -> {
// intentionally empty
});
- } catch (final AssertionError assertionError)
+ }
+ catch (final AssertionError assertionError)
{
assertMissingExitMessage(assertionError);
return;
@@ -70,7 +70,8 @@ void testSystemExitWithUnexpectedStatusThrowsAssertionError()
try
{
assertExitWithStatus(0, () -> System.exit(1));
- } catch (final AssertionError assertionError)
+ }
+ catch (final AssertionError assertionError)
{
return;
}
@@ -85,11 +86,12 @@ void testSystemWithStatusMissingExitThrowsAssertError()
assertExitWithStatus(0, () -> {
// intentionally empty
});
- } catch (final AssertionError assertionError)
+ }
+ catch (final AssertionError assertionError)
{
assertMissingExitMessage(assertionError);
return;
}
fail("Code sequence where exit was expected did not exit, no assertion error was raised.");
}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/org/itsallcode/junit/sysextensions/TestSystemExitReplacingExistingSecurityManager.java b/src/test/java/org/itsallcode/junit/sysextensions/TestSystemExitReplacingExistingSecurityManager.java
new file mode 100644
index 0000000..aa51171
--- /dev/null
+++ b/src/test/java/org/itsallcode/junit/sysextensions/TestSystemExitReplacingExistingSecurityManager.java
@@ -0,0 +1,98 @@
+/*-
+ * #%L
+ * JUnit5 System Extensions
+ * %%
+ * Copyright (C) 2018 itsallcode.org
+ * %%
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the Eclipse
+ * Public License, v. 2.0 are satisfied: GNU General Public License, version 2
+ * with the GNU Classpath Exception which is
+ * available at https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ * #L%
+ */
+package org.itsallcode.junit.sysextensions;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.itsallcode.junit.sysextensions.security.ExitGuardSecurityManager;
+import org.itsallcode.junit.sysextensions.security.ExitTrapException;
+import org.junit.jupiter.api.Test;
+
+class TestSystemExitReplacingExistingSecurityManager
+{
+ @Test
+ void testFirstSystemExitIntercepted()
+ {
+ final SecurityManager previousSecuritymanager = System.getSecurityManager();
+ try
+ {
+ final SecurityManagerStub securityManagerStub = new SecurityManagerStub();
+ final ExitGuardSecurityManager exitGuardsecurityManager = new ExitGuardSecurityManager(securityManagerStub);
+ System.setSecurityManager(exitGuardsecurityManager);
+ exitGuardsecurityManager.trapExit(true);
+ assertAll(() -> assertThrows(ExitTrapException.class, () -> System.exit(1)),
+ () -> assertFalse(securityManagerStub.wasCheckExitCalled(), "Delegate exit called"));
+ }
+ finally
+ {
+ System.setSecurityManager(previousSecuritymanager);
+ }
+ }
+
+ @Test
+ void testSecurityCheckGetsDelegated()
+ {
+ final SecurityManager previousSecuritymanager = System.getSecurityManager();
+ try
+ {
+ final SecurityManagerStub securityManagerStub = new SecurityManagerStub();
+ final ExitGuardSecurityManager exitGuardsecurityManager = new ExitGuardSecurityManager(securityManagerStub);
+ System.setSecurityManager(exitGuardsecurityManager);
+ System.getProperty("any-property");
+ assertTrue(securityManagerStub.wasCheckPropertyAccessCalled());
+ }
+ finally
+ {
+ System.setSecurityManager(previousSecuritymanager);
+ }
+ }
+
+ private static class SecurityManagerStub extends SecurityManager
+ {
+ private boolean checkExitCalled = false;
+ private boolean checkPropertyAccessCalled = false;
+
+ @Override
+ public void checkExit(final int status)
+ {
+ this.checkExitCalled = true;
+ super.checkExit(status);
+ }
+
+ @Override
+ public void checkPropertyAccess(final String key)
+ {
+ this.checkPropertyAccessCalled = true;
+ }
+
+ public boolean wasCheckExitCalled()
+ {
+ return this.checkExitCalled;
+ }
+
+ public boolean wasCheckPropertyAccessCalled()
+ {
+ return this.checkPropertyAccessCalled;
+ }
+ }
+}
\ No newline at end of file