From f6ea1d8e33c37849b2d637ab2c57cc8b18890be3 Mon Sep 17 00:00:00 2001 From: Adam Murdoch Date: Sat, 8 Sep 2012 10:11:25 +1000 Subject: [PATCH] - Changed Native.get() to cache integration instances. - Extracted DefaultSystemInfo out of a couple of separate places. --- .../net/rubygrapefruit/platform/Native.java | 23 ++++++++---- .../platform/internal/AbstractTerminals.java | 7 ++-- .../platform/internal/DefaultPosixFile.java | 11 +----- .../platform/internal/DefaultSystemInfo.java | 37 +++++++++++++++++++ .../platform/internal/Platform.java | 10 +---- .../platform/FileSystemsTest.groovy | 5 +++ .../platform/PosixFileTest.groovy | 5 +++ .../platform/ProcessTest.groovy | 5 +++ .../platform/SystemInfoTest.groovy | 5 +++ .../platform/TerminalsTest.groovy | 15 +++++--- 10 files changed, 90 insertions(+), 33 deletions(-) create mode 100644 src/main/java/net/rubygrapefruit/platform/internal/DefaultSystemInfo.java diff --git a/src/main/java/net/rubygrapefruit/platform/Native.java b/src/main/java/net/rubygrapefruit/platform/Native.java index 465b84b..99c4e73 100755 --- a/src/main/java/net/rubygrapefruit/platform/Native.java +++ b/src/main/java/net/rubygrapefruit/platform/Native.java @@ -5,6 +5,8 @@ import net.rubygrapefruit.platform.internal.Platform; import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; import java.io.File; +import java.util.HashMap; +import java.util.Map; /** * Provides access to the native integrations. Use {@link #get(Class)} to load a particular integration. @@ -12,6 +14,7 @@ import java.io.File; @ThreadSafe public class Native { private static boolean loaded; + private static final Map, Object> integrations = new HashMap, Object>(); private Native() { } @@ -59,13 +62,19 @@ public class Native { public static T get(Class type) throws NativeIntegrationUnavailableException, NativeException { init(null); - try { - Platform platform = Platform.current(); - return platform.get(type); - } catch (NativeException e) { - throw e; - } catch (Throwable t) { - throw new NativeException(String.format("Failed to load native integration %s.", type.getSimpleName()), t); + synchronized (Native.class) { + Object instance = integrations.get(type); + if (instance == null) { + try { + instance = Platform.current().get(type); + } catch (NativeException e) { + throw e; + } catch (Throwable t) { + throw new NativeException(String.format("Failed to load native integration %s.", type.getSimpleName()), t); + } + integrations.put(type, instance); + } + return type.cast(instance); } } } diff --git a/src/main/java/net/rubygrapefruit/platform/internal/AbstractTerminals.java b/src/main/java/net/rubygrapefruit/platform/internal/AbstractTerminals.java index 1b6bcc1..0bd28ca 100644 --- a/src/main/java/net/rubygrapefruit/platform/internal/AbstractTerminals.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/AbstractTerminals.java @@ -4,11 +4,12 @@ import net.rubygrapefruit.platform.Terminal; import net.rubygrapefruit.platform.Terminals; public abstract class AbstractTerminals implements Terminals { - private static Output currentlyOpen; - private static AbstractTerminal current; + private final Object lock = new Object(); + private Output currentlyOpen; + private AbstractTerminal current; public Terminal getTerminal(Output output) { - synchronized (AbstractTerminals.class) { + synchronized (lock) { if (currentlyOpen != null && currentlyOpen != output) { throw new UnsupportedOperationException("Currently only one output can be used as a terminal."); } diff --git a/src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFile.java b/src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFile.java index 9f43348..ba2a47b 100755 --- a/src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFile.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/DefaultPosixFile.java @@ -2,7 +2,6 @@ package net.rubygrapefruit.platform.internal; import net.rubygrapefruit.platform.NativeException; import net.rubygrapefruit.platform.PosixFile; -import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; import net.rubygrapefruit.platform.internal.jni.PosixFileFunctions; import java.io.File; @@ -12,14 +11,8 @@ public class DefaultPosixFile implements PosixFile { private final String characterEncoding; public DefaultPosixFile() { - MutableSystemInfo systemInfo = new MutableSystemInfo(); - FunctionResult result = new FunctionResult(); - NativeLibraryFunctions.getSystemInfo(systemInfo, result); - if (result.isFailed()) { - throw new NativeException(String.format("Could not fetch system information: %s", - result.getMessage())); - } - this.characterEncoding = systemInfo.characterEncoding; + DefaultSystemInfo systemInfo = new DefaultSystemInfo(); + this.characterEncoding = systemInfo.getCharacterEncoding(); } public void setMode(File file, int perms) { diff --git a/src/main/java/net/rubygrapefruit/platform/internal/DefaultSystemInfo.java b/src/main/java/net/rubygrapefruit/platform/internal/DefaultSystemInfo.java new file mode 100644 index 0000000..33c935f --- /dev/null +++ b/src/main/java/net/rubygrapefruit/platform/internal/DefaultSystemInfo.java @@ -0,0 +1,37 @@ +package net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.NativeException; +import net.rubygrapefruit.platform.SystemInfo; +import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; + +public class DefaultSystemInfo implements SystemInfo { + MutableSystemInfo systemInfo = new MutableSystemInfo(); + + public DefaultSystemInfo() { + FunctionResult result = new FunctionResult(); + NativeLibraryFunctions.getSystemInfo(systemInfo, result); + if (result.isFailed()) { + throw new NativeException(String.format("Could not fetch system information: %s", + result.getMessage())); + } + } + + @Override + public String getKernelName() { + return systemInfo.getKernelName(); + } + + @Override + public String getKernelVersion() { + return systemInfo.getKernelVersion(); + } + + @Override + public String getMachineArchitecture() { + return systemInfo.getMachineArchitecture(); + } + + public String getCharacterEncoding() { + return systemInfo.characterEncoding; + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java index fcdc3ab..cfda151 100755 --- a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java @@ -2,7 +2,6 @@ package net.rubygrapefruit.platform.internal; import net.rubygrapefruit.platform.*; import net.rubygrapefruit.platform.Process; -import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions; public abstract class Platform { private static Platform platform; @@ -75,14 +74,7 @@ public abstract class Platform { return type.cast(new TerminfoTerminals()); } if (type.equals(SystemInfo.class)) { - MutableSystemInfo systemInfo = new MutableSystemInfo(); - FunctionResult result = new FunctionResult(); - NativeLibraryFunctions.getSystemInfo(systemInfo, result); - if (result.isFailed()) { - throw new NativeException(String.format("Could not fetch system information: %s", - result.getMessage())); - } - return type.cast(systemInfo); + return type.cast(new DefaultSystemInfo()); } return super.get(type); } diff --git a/src/test/groovy/net/rubygrapefruit/platform/FileSystemsTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/FileSystemsTest.groovy index 4b5a107..984467e 100755 --- a/src/test/groovy/net/rubygrapefruit/platform/FileSystemsTest.groovy +++ b/src/test/groovy/net/rubygrapefruit/platform/FileSystemsTest.groovy @@ -8,6 +8,11 @@ class FileSystemsTest extends Specification { @Rule TemporaryFolder tmpDir final FileSystems fileSystems = Native.get(FileSystems.class) + def "caches file systems instance"() { + expect: + Native.get(FileSystems.class) == fileSystems + } + def "can query filesystem details"() { expect: fileSystems.fileSystems.collect() { it.mountPoint }.containsAll(File.listRoots()) diff --git a/src/test/groovy/net/rubygrapefruit/platform/PosixFileTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/PosixFileTest.groovy index 0656b31..03f4834 100755 --- a/src/test/groovy/net/rubygrapefruit/platform/PosixFileTest.groovy +++ b/src/test/groovy/net/rubygrapefruit/platform/PosixFileTest.groovy @@ -11,6 +11,11 @@ class PosixFileTest extends Specification { @Rule TemporaryFolder tmpDir final PosixFile file = Native.get(PosixFile.class) + def "caches file instance"() { + expect: + Native.get(PosixFile.class) == file + } + def "can set mode on a file"() { def testFile = tmpDir.newFile("test.txt") diff --git a/src/test/groovy/net/rubygrapefruit/platform/ProcessTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/ProcessTest.groovy index 8f65b97..655d587 100755 --- a/src/test/groovy/net/rubygrapefruit/platform/ProcessTest.groovy +++ b/src/test/groovy/net/rubygrapefruit/platform/ProcessTest.groovy @@ -8,6 +8,11 @@ class ProcessTest extends Specification { @Rule TemporaryFolder tmpDir final Process process = Native.get(Process.class) + def "caches process instance"() { + expect: + Native.get(Process.class) == process + } + def "can get PID"() { expect: process.getProcessId() != 0 diff --git a/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy index 963054d..174db52 100755 --- a/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy +++ b/src/test/groovy/net/rubygrapefruit/platform/SystemInfoTest.groovy @@ -8,6 +8,11 @@ class SystemInfoTest extends Specification { @Rule TemporaryFolder tmpDir final SystemInfo systemInfo = Native.get(SystemInfo.class) + def "caches system info instance"() { + expect: + Native.get(SystemInfo.class) == systemInfo + } + def "can query OS details"() { expect: systemInfo.kernelName diff --git a/src/test/groovy/net/rubygrapefruit/platform/TerminalsTest.groovy b/src/test/groovy/net/rubygrapefruit/platform/TerminalsTest.groovy index cf3e6f8..a5a6c90 100755 --- a/src/test/groovy/net/rubygrapefruit/platform/TerminalsTest.groovy +++ b/src/test/groovy/net/rubygrapefruit/platform/TerminalsTest.groovy @@ -8,18 +8,23 @@ import spock.lang.IgnoreIf class TerminalsTest extends Specification { @Rule TemporaryFolder tmpDir - final Terminals terminal = Native.get(Terminals.class) + final Terminals terminals = Native.get(Terminals.class) + + def "caches terminals instance"() { + expect: + Native.get(Terminals.class) == terminals + } def "can check if attached to terminal"() { expect: - !terminal.isTerminal(Terminals.Output.Stdout); - !terminal.isTerminal(Terminals.Output.Stderr); + !terminals.isTerminal(Terminals.Output.Stdout); + !terminals.isTerminal(Terminals.Output.Stderr); } @IgnoreIf({Platform.current().windows}) def "cannot access posix terminal from a test"() { when: - terminal.getTerminal(Terminals.Output.Stdout) + terminals.getTerminal(Terminals.Output.Stdout) then: NativeException e = thrown() @@ -29,7 +34,7 @@ class TerminalsTest extends Specification { @IgnoreIf({!Platform.current().windows}) def "cannot access windows console from a test"() { when: - terminal.getTerminal(Terminals.Output.Stdout) + terminals.getTerminal(Terminals.Output.Stdout) then: NativeException e = thrown()