diff --git a/README.md b/README.md index 1709f0d6..869b3522 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,26 @@ _A platform-independent serial port access library for Java._ For usage examples, please refer to the [Usage wiki](https://github.com/Fazecast/jSerialComm/wiki/Usage-Examples). -If you intend to use the library in multiple applications simultaneously, please make sure +If you intend to use the library in multiple applications simultaneously, you may need to set the ```fazecast.jSerialComm.appid``` property before accessing the SerialPort class -so that applications don't accidentally delete each others' temporary files during boot-up: +so that applications don't accidentally delete each others' temporary files during boot-up +(**this should almost never be necessary**): + ``` System.setProperty("fazecast.jSerialComm.appid", "YOUR_APPLICATION_IDENTIFIER") ``` +Alternately, if you plan to use the library within an Apache Tomcat application, please set +the ```jSerialComm.library.randomizeNativeName``` property to true before accessing the +SerialPort class to address an issue whereby the Tomcat bootloader tries to reinitialize +the library multiple times. This can either be done using ```-DjSerialComm.library.randomizeNativeName="true"``` +as a command line parameter or by calling the following within your own application before +using any SerialPort functionality: + +``` +System.setProperty("jSerialComm.library.randomizeNativeName", "true") +``` + In order to use the ```jSerialComm``` library in your own project, you must simply include the JAR file in your build path and import it like any other Java package using ```import com.fazecast.jSerialComm.*;```. diff --git a/src/main/c/Windows/SerialPort_Windows.c b/src/main/c/Windows/SerialPort_Windows.c index f9a9ae54..4ce3b489 100644 --- a/src/main/c/Windows/SerialPort_Windows.c +++ b/src/main/c/Windows/SerialPort_Windows.c @@ -775,7 +775,7 @@ JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative( { (*env)->ReleaseStringChars(env, portNameJString, (const jchar*)portName); checkJniError(env, __LINE__ - 1); - lastErrorLineNumber = __LINE__ - 3; + lastErrorLineNumber = __LINE__ - 4; lastErrorNumber = (!port ? 1 : 2); return 0; } diff --git a/src/main/java/com/fazecast/jSerialComm/SerialPort.java b/src/main/java/com/fazecast/jSerialComm/SerialPort.java index 6caa8e09..f7fb9094 100644 --- a/src/main/java/com/fazecast/jSerialComm/SerialPort.java +++ b/src/main/java/com/fazecast/jSerialComm/SerialPort.java @@ -111,15 +111,16 @@ public class SerialPort { // Determine the temporary file directories for native library storage String[] architectures; - String libraryPath, libraryFileName; + String libraryPath, libraryFileName, extractedFileName; final String manualLibraryPath = System.getProperty("jSerialComm.library.path", ""); final String OS = System.getProperty("os.name").toLowerCase(); final String arch = System.getProperty("os.arch").toLowerCase(); final File tempFileDirectory = new File(System.getProperty("java.io.tmpdir"), "jSerialComm" + File.separator + System.getProperty(tmpdirAppIdProperty, ".") + File.separator + versionString).getCanonicalFile(); final File userHomeDirectory = new File(System.getProperty("user.home"), ".jSerialComm" + File.separator + System.getProperty(tmpdirAppIdProperty, ".") + File.separator + versionString).getCanonicalFile(); + final boolean randomizeNativeName = System.getProperty("jSerialComm.library.randomizeNativeName", "false").equalsIgnoreCase("true"); cleanUpDirectory(new File(tempFileDirectory, "..")); cleanUpDirectory(new File(userHomeDirectory, "..")); - + // Determine Operating System and architecture if (System.getProperty("java.vm.vendor").toLowerCase().contains("android")) { @@ -177,7 +178,7 @@ else if (arch.contains("86") || arch.contains("amd")) architectures = null; System.exit(-1); } - + // Load platform-specific binaries for non-Android systems if (!isAndroid) { @@ -191,7 +192,7 @@ else if (arch.contains("86") || arch.contains("amd")) if (!libraryLoaded) libraryLoaded = loadNativeLibrary(new File(manualLibraryPath, libraryFileName).getCanonicalPath(), errorMessages); } - + // Attempt to load from the system-defined library location try { @@ -206,20 +207,21 @@ else if (arch.contains("86") || arch.contains("amd")) } catch (UnsatisfiedLinkError e) { errorMessages.add(e.getMessage()); } catch (Exception e) { errorMessages.add(e.getMessage()); } - + // Attempt to load from an existing extracted location for (int attempt = 0; !libraryLoaded && (attempt < 2); ++attempt) { File nativeLibrary = new File((attempt == 0) ? tempFileDirectory : userHomeDirectory, libraryFileName); libraryLoaded = nativeLibrary.exists() && loadNativeLibrary(nativeLibrary.getCanonicalPath(), errorMessages); } - + // Attempt to load from the expected JAR location + extractedFileName = randomizeNativeName ? ((new Date()).getTime() + "-" + libraryFileName) : libraryFileName; for (int attempt = 0; !libraryLoaded && (attempt < 2); ++attempt) { // Create a temporary working directory with open permissions deleteDirectory(new File((attempt == 0) ? tempFileDirectory : userHomeDirectory, "..").getCanonicalFile()); - File tempNativeLibrary = new File((attempt == 0) ? tempFileDirectory : userHomeDirectory, libraryFileName); + File tempNativeLibrary = new File((attempt == 0) ? tempFileDirectory : userHomeDirectory, extractedFileName); if (tempNativeLibrary.getParentFile().exists() || tempNativeLibrary.getParentFile().mkdirs()) { tempNativeLibrary.getParentFile().setReadable(true, false); @@ -228,7 +230,7 @@ else if (arch.contains("86") || arch.contains("amd")) } else continue; - + // Attempt to load the native jSerialComm library for any available architecture for (int i = 0; !libraryLoaded && (i < architectures.length); ++i) { @@ -248,7 +250,7 @@ else if (arch.contains("86") || arch.contains("amd")) tempNativeLibrary.setReadable(true, false); tempNativeLibrary.setWritable(false, false); tempNativeLibrary.setExecutable(true, false); - + // Attempt to load the native library errorMessages.add("Loading for arch: " + architectures[i]); libraryLoaded = loadNativeLibrary(tempNativeLibrary.getCanonicalPath(), errorMessages); @@ -257,7 +259,7 @@ else if (arch.contains("86") || arch.contains("amd")) } catch (Exception e) { e.printStackTrace(); } } - + // Throw an error if unable to load any native libraries if (!libraryLoaded) { @@ -288,12 +290,12 @@ public void run() } } catch (InterruptedException ignored) {} - + // Un-initialize the native library isShuttingDown = true; if (!isAndroid) uninitializeLibrary(); - + // Optionally delete all native library files if (cleanUpOnShutdown) try @@ -430,7 +432,7 @@ static public boolean setAndroidContext(Object androidApp) } return false; } - + /** * Causes the library to attempt to clean up after itself upon shutdown and automatically delete * all temporary files it may have created. @@ -577,11 +579,11 @@ public final boolean openPort(int safetySleepTime, int deviceSendQueueSize, int receiveDeviceQueueSize = deviceReceiveQueueSize; if (portHandle != 0) return (androidPort != null) ? androidPort.configPort(this) : configPort(portHandle); - + // Force a sleep to ensure that the port does not become unusable due to rapid closing/opening on the part of the user if (safetySleepTimeMS > 0) try { Thread.sleep(safetySleepTimeMS); } catch (Exception e) { Thread.currentThread().interrupt(); } - + // If this is an Android root application, we must explicitly allow serial port access to the library File portFile = isAndroidDelete ? new File(comPort) : null; if (portFile != null && (!portFile.canRead() || !portFile.canWrite())) @@ -613,7 +615,7 @@ public final boolean openPort(int safetySleepTime, int deviceSendQueueSize, int try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } } - + // Natively open the serial port, and start an event-based listener if registered portHandle = (androidPort != null) ? androidPort.openPortNative(this) : openPortNative(); if ((portHandle != 0) && (serialEventListener != null)) @@ -675,7 +677,7 @@ public final boolean closePort() // Stop a registered event listener if (serialEventListener != null) serialEventListener.stopListening(); - + // Natively close the port if (portHandle != 0) portHandle = (androidPort != null) ? androidPort.closePortNative() : closePortNative(portHandle); @@ -800,7 +802,7 @@ public final int getLastErrorLocation() { return (androidPort != null) ? androidPort.getLastErrorLocation() : getLastErrorLocation(portHandle); } - + /** * Returns the error number returned by the most recent native source code line that failed execution. *

@@ -880,7 +882,7 @@ public final int readBytes(byte[] buffer, int bytesToRead, int offset) // Ensure that the buffer is large enough to hold the requested number of bytes if (bytesToRead > (buffer.length - offset)) return -2; - + // Read all requested bytes from native code return (portHandle != 0) ? ((androidPort != null) ? androidPort.readBytes(buffer, bytesToRead, offset, timeoutMode, readTimeout) : readBytes(portHandle, buffer, bytesToRead, offset, timeoutMode, readTimeout)) : -1; } @@ -1055,7 +1057,7 @@ public final boolean clearDTR() * @return Whether or not the DSR line is asserted. */ public final boolean getDSR() { return (portHandle != 0) && ((androidPort != null) ? androidPort.getDSR() : getDSR(portHandle)); } - + /** * Returns whether the DCD line is currently asserted. * @return Whether or not the DCD line is asserted. @@ -1427,7 +1429,7 @@ else if ((newReadTimeout > 0) && (newReadTimeout <= 100)) readTimeout = 100; else readTimeout = Math.round((float)newReadTimeout / 100.0f) * 100; - + if (portHandle != 0) { if (safetySleepTimeMS > 0) @@ -1680,7 +1682,7 @@ public final boolean setRs485ModeParameters(boolean useRS485Mode, boolean rs485R rs485RxDuringTx = rxDuringTx; rs485DelayBefore = delayBeforeSendMicroseconds; rs485DelayAfter = delayAfterSendMicroseconds; - + if (portHandle != 0) { if (safetySleepTimeMS > 0) @@ -2000,7 +2002,7 @@ public final void stopListening() else configPort(portHandle); } - + public final void resetBuffers() { messageBytes.reset(); @@ -2207,7 +2209,7 @@ public final void write(byte[] b, int off, int len) throws NullPointerException, // Always ensure that the port has not been closed if (portHandle == 0) throw new SerialPortIOException("This port appears to have been shutdown or disconnected."); - + // Write the actual bytes to the serial port int numWritten = writeBytes(b, len - totalNumWritten, off + totalNumWritten); if (numWritten < 0) diff --git a/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java b/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java index 00ec70cf..02b3d415 100644 --- a/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java +++ b/src/test/java/com/fazecast/jSerialComm/SerialPortTest.java @@ -2,10 +2,10 @@ * SerialPortTest.java * * Created on: Feb 27, 2015 - * Last Updated on: Jun 19, 2023 + * Last Updated on: Apr 11, 2024 * Author: Will Hedgecock * - * Copyright (C) 2012-2023 Fazecast, Inc. + * Copyright (C) 2012-2024 Fazecast, Inc. * * This file is part of jSerialComm. * @@ -87,13 +87,13 @@ static public void main(String[] args) SerialPort[] ports = SerialPort.getCommPorts(); System.out.println("\nAvailable Ports:\n"); for (int i = 0; i < ports.length; ++i) - System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + " (" + ports[i].getSystemPortPath() + "): " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation() + " (VID = " + ports[i].getVendorID() + ", PID = " + ports[i].getProductID() + ", Serial = " + ports[i].getSerialNumber() + ")"); + System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + " (" + ports[i].getSystemPortPath() + "): " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation() + " (VID = " + ports[i].getVendorID() + ", PID = " + ports[i].getProductID() + ", Serial = " + ports[i].getSerialNumber() + ", Manufacturer = " + ports[i].getManufacturer() + ")"); System.out.println("\nRe-enumerating ports again in 2 seconds...\n"); try { Thread.sleep(2000); } catch (Exception e) {} ports = SerialPort.getCommPorts(); System.out.println("Available Ports:\n"); for (int i = 0; i < ports.length; ++i) - System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + " (" + ports[i].getSystemPortPath() + "): " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation() + " (VID = " + ports[i].getVendorID() + ", PID = " + ports[i].getProductID() + ", Serial = " + ports[i].getSerialNumber() + ")"); + System.out.println(" [" + i + "] " + ports[i].getSystemPortName() + " (" + ports[i].getSystemPortPath() + "): " + ports[i].getDescriptivePortName() + " - " + ports[i].getPortDescription() + " @ " + ports[i].getPortLocation() + " (VID = " + ports[i].getVendorID() + ", PID = " + ports[i].getProductID() + ", Serial = " + ports[i].getSerialNumber() + ", Manufacturer = " + ports[i].getManufacturer() + ")"); SerialPort ubxPort; System.out.print("\nChoose your desired serial port or enter -1 to specify a port directly: "); int serialPortChoice = -2;