richie
6 years ago
12 changed files with 2650 additions and 0 deletions
@ -0,0 +1,92 @@
|
||||
package com.fr.third.net.bytebuddy.agent; |
||||
|
||||
import java.lang.reflect.InvocationTargetException; |
||||
|
||||
/** |
||||
* A Java program that attaches a Java agent to an external process. |
||||
*/ |
||||
public class Attacher { |
||||
|
||||
/** |
||||
* Base for access to a reflective member to make the code more readable. |
||||
*/ |
||||
private static final Object STATIC_MEMBER = null; |
||||
|
||||
/** |
||||
* The name of the {@code attach} method of the {@code VirtualMachine} class. |
||||
*/ |
||||
private static final String ATTACH_METHOD_NAME = "attach"; |
||||
|
||||
/** |
||||
* The name of the {@code loadAgent} method of the {@code VirtualMachine} class. |
||||
*/ |
||||
private static final String LOAD_AGENT_METHOD_NAME = "loadAgent"; |
||||
|
||||
/** |
||||
* The name of the {@code detach} method of the {@code VirtualMachine} class. |
||||
*/ |
||||
private static final String DETACH_METHOD_NAME = "detach"; |
||||
|
||||
/** |
||||
* Runs the attacher as a Java application. |
||||
* |
||||
* @param args A list containing the fully qualified name of the virtual machine type, |
||||
* the process id, the fully qualified name of the Java agent jar followed by |
||||
* an empty string if the argument to the agent is {@code null} or any number |
||||
* of strings where the first argument is proceeded by any single character |
||||
* which is stripped off. |
||||
*/ |
||||
public static void main(String[] args) { |
||||
try { |
||||
String argument; |
||||
if (args.length < 4 || args[3].isEmpty()) { |
||||
argument = null; |
||||
} else { |
||||
StringBuilder stringBuilder = new StringBuilder(args[3].substring(1)); |
||||
for (int index = 4; index < args.length; index++) { |
||||
stringBuilder.append(' ').append(args[index]); |
||||
} |
||||
argument = stringBuilder.toString(); |
||||
} |
||||
install(Class.forName(args[0]), args[1], args[2], argument); |
||||
} catch (Exception ignored) { |
||||
System.exit(1); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Installs a Java agent on a target VM. |
||||
* |
||||
* @param virtualMachineType The virtual machine type to use for the external attachment. |
||||
* @param processId The id of the process being target of the external attachment. |
||||
* @param agent The Java agent to attach. |
||||
* @param argument The argument to provide or {@code null} if no argument is provided. |
||||
* @throws NoSuchMethodException If the virtual machine type does not define an expected method. |
||||
* @throws InvocationTargetException If the virtual machine type raises an error. |
||||
* @throws IllegalAccessException If a method of the virtual machine type cannot be accessed. |
||||
*/ |
||||
protected static void install(Class<?> virtualMachineType, |
||||
String processId, |
||||
String agent, |
||||
String argument) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { |
||||
Object virtualMachineInstance = virtualMachineType |
||||
.getMethod(ATTACH_METHOD_NAME, String.class) |
||||
.invoke(STATIC_MEMBER, processId); |
||||
try { |
||||
virtualMachineType |
||||
.getMethod(LOAD_AGENT_METHOD_NAME, String.class, String.class) |
||||
.invoke(virtualMachineInstance, agent, argument); |
||||
} finally { |
||||
virtualMachineType |
||||
.getMethod(DETACH_METHOD_NAME) |
||||
.invoke(virtualMachineInstance); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* The attacher provides only {@code static} utility methods and should not be instantiated. |
||||
*/ |
||||
private Attacher() { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,71 @@
|
||||
package com.fr.third.net.bytebuddy.agent; |
||||
|
||||
import java.lang.instrument.Instrumentation; |
||||
|
||||
/** |
||||
* An installer class which defined the hook-in methods that are required by the Java agent specification. |
||||
*/ |
||||
public class Installer { |
||||
|
||||
/** |
||||
* A field for carrying the {@link java.lang.instrument.Instrumentation} that was loaded by the Byte Buddy |
||||
* agent. Note that this field must never be accessed directly as the agent is injected into the VM's |
||||
* system class loader. This way, the field of this class might be {@code null} even after the installation |
||||
* of the Byte Buddy agent as this class might be loaded by a different class loader than the system class
|
||||
* loader. |
||||
*/ |
||||
@SuppressWarnings("unused") |
||||
private static volatile Instrumentation instrumentation; |
||||
|
||||
/** |
||||
* The installer provides only {@code static} hook-in methods and should not be instantiated. |
||||
*/ |
||||
private Installer() { |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
/** |
||||
* <p> |
||||
* Returns the instrumentation that was loaded by the Byte Buddy agent. When a security manager is active, |
||||
* the {@link RuntimePermission} for {@code getInstrumentation} is required by the caller. |
||||
* </p> |
||||
* <p> |
||||
* <b>Important</b>: This method must only be invoked via the {@link ClassLoader#getSystemClassLoader()} where any |
||||
* Java agent is loaded. It is possible that two versions of this class exist for different class loaders. |
||||
* </p> |
||||
* |
||||
* @return The instrumentation instance of the Byte Buddy agent. |
||||
*/ |
||||
public static Instrumentation getInstrumentation() { |
||||
SecurityManager securityManager = System.getSecurityManager(); |
||||
if (securityManager != null) { |
||||
securityManager.checkPermission(new RuntimePermission("getInstrumentation")); |
||||
} |
||||
Instrumentation instrumentation = Installer.instrumentation; |
||||
if (instrumentation == null) { |
||||
throw new IllegalStateException("The Byte Buddy agent is not loaded or this method is not called via the system class loader"); |
||||
} |
||||
return instrumentation; |
||||
} |
||||
|
||||
/** |
||||
* Allows the installation of this agent via a command line argument. |
||||
* |
||||
* @param agentArguments The unused agent arguments. |
||||
* @param instrumentation The instrumentation instance. |
||||
*/ |
||||
public static void premain(String agentArguments, Instrumentation instrumentation) { |
||||
Installer.instrumentation = instrumentation; |
||||
} |
||||
|
||||
/** |
||||
* Allows the installation of this agent via the Attach API. |
||||
* |
||||
* @param agentArguments The unused agent arguments. |
||||
* @param instrumentation The instrumentation instance. |
||||
*/ |
||||
@SuppressWarnings("unused") |
||||
public static void agentmain(String agentArguments, Instrumentation instrumentation) { |
||||
Installer.instrumentation = instrumentation; |
||||
} |
||||
} |
@ -0,0 +1,336 @@
|
||||
package com.fr.third.net.bytebuddy.agent; |
||||
|
||||
import com.fr.third.org.newsclub.net.unix.AFUNIXSocket; |
||||
import com.fr.third.org.newsclub.net.unix.AFUNIXSocketAddress; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.nio.charset.Charset; |
||||
import java.util.Locale; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
/** |
||||
* <p> |
||||
* An implementation for attachment on a virtual machine. This interface mimics the tooling API's virtual |
||||
* machine interface to allow for similar usage by {@link ByteBuddyAgent} where all calls are made via |
||||
* reflection such that this structural typing suffices for interoperability. |
||||
* </p> |
||||
* <p> |
||||
* <b>Note</b>: Implementations are required to declare a static method {@code attach(String)} returning an |
||||
* instance of a class that declares the methods defined by {@link VirtualMachine}. |
||||
* </p> |
||||
*/ |
||||
public interface VirtualMachine { |
||||
|
||||
/** |
||||
* Loads an agent into the represented virtual machine. |
||||
* |
||||
* @param jarFile The jar file to attach. |
||||
* @param argument The argument to provide or {@code null} if no argument should be provided. |
||||
* @throws IOException If an I/O exception occurs. |
||||
*/ |
||||
@SuppressWarnings("unused") |
||||
void loadAgent(String jarFile, String argument) throws IOException; |
||||
|
||||
/** |
||||
* Detaches this virtual machine representation. |
||||
* |
||||
* @throws IOException If an I/O exception occurs. |
||||
*/ |
||||
@SuppressWarnings("unused") |
||||
void detach() throws IOException; |
||||
|
||||
/** |
||||
* A virtual machine implementation for a HotSpot VM or any compatible VM. |
||||
*/ |
||||
abstract class ForHotSpot implements VirtualMachine { |
||||
|
||||
/** |
||||
* The UTF-8 charset. |
||||
*/ |
||||
private static final Charset UTF_8 = Charset.forName("UTF-8"); |
||||
|
||||
/** |
||||
* The protocol version to use for communication. |
||||
*/ |
||||
private static final String PROTOCOL_VERSION = "1"; |
||||
|
||||
/** |
||||
* The {@code load} command. |
||||
*/ |
||||
private static final String LOAD_COMMAND = "load"; |
||||
|
||||
/** |
||||
* The {@code instrument} command. |
||||
*/ |
||||
private static final String INSTRUMENT_COMMAND = "instrument"; |
||||
|
||||
/** |
||||
* A delimiter to be used for attachment. |
||||
*/ |
||||
private static final String ARGUMENT_DELIMITER = "="; |
||||
|
||||
/** |
||||
* A blank line argument. |
||||
*/ |
||||
private static final byte[] BLANK = new byte[]{0}; |
||||
|
||||
/** |
||||
* The target process's id. |
||||
*/ |
||||
protected final String processId; |
||||
|
||||
/** |
||||
* Creates a new HotSpot-compatible VM implementation. |
||||
* |
||||
* @param processId The target process's id. |
||||
*/ |
||||
protected ForHotSpot(String processId) { |
||||
this.processId = processId; |
||||
} |
||||
|
||||
@Override |
||||
public void loadAgent(String jarFile, String argument) throws IOException { |
||||
connect(); |
||||
write(PROTOCOL_VERSION.getBytes(UTF_8)); |
||||
write(BLANK); |
||||
write(LOAD_COMMAND.getBytes(UTF_8)); |
||||
write(BLANK); |
||||
write(INSTRUMENT_COMMAND.getBytes(UTF_8)); |
||||
write(BLANK); |
||||
write(Boolean.FALSE.toString().getBytes(UTF_8)); |
||||
write(BLANK); |
||||
write((argument == null |
||||
? jarFile |
||||
: jarFile + ARGUMENT_DELIMITER + argument).getBytes(UTF_8)); |
||||
write(BLANK); |
||||
byte[] buffer = new byte[1]; |
||||
StringBuilder stringBuilder = new StringBuilder(); |
||||
int length; |
||||
while ((length = read(buffer)) != -1) { |
||||
if (length > 0) { |
||||
if (buffer[0] == 10) { |
||||
break; |
||||
} |
||||
stringBuilder.append((char) buffer[0]); |
||||
} |
||||
} |
||||
switch (Integer.parseInt(stringBuilder.toString())) { |
||||
case 0: |
||||
return; |
||||
case 101: |
||||
throw new IOException("Protocol mismatch with target VM"); |
||||
default: |
||||
buffer = new byte[1024]; |
||||
stringBuilder = new StringBuilder(); |
||||
while ((length = read(buffer)) != -1) { |
||||
stringBuilder.append(new String(buffer, 0, length, UTF_8)); |
||||
} |
||||
throw new IllegalStateException(stringBuilder.toString()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Connects to the target VM. |
||||
* |
||||
* @throws IOException If an I/O exception occurs. |
||||
*/ |
||||
protected abstract void connect() throws IOException; |
||||
|
||||
/** |
||||
* Reads from the communication channel. |
||||
* |
||||
* @param buffer The buffer to read into. |
||||
* @return The amount of bytes read. |
||||
* @throws IOException If an I/O exception occurs. |
||||
*/ |
||||
protected abstract int read(byte[] buffer) throws IOException; |
||||
|
||||
/** |
||||
* Writes to the communication channel. |
||||
* |
||||
* @param buffer The buffer to write from. |
||||
* @throws IOException If an I/O exception occurs. |
||||
*/ |
||||
protected abstract void write(byte[] buffer) throws IOException; |
||||
|
||||
/** |
||||
* A virtual machine implementation for a HotSpot VM running on Unix. |
||||
*/ |
||||
public static class OnUnix extends ForHotSpot { |
||||
|
||||
/** |
||||
* The default amount of attempts to connect. |
||||
*/ |
||||
private static final int DEFAULT_ATTEMPTS = 10; |
||||
|
||||
/** |
||||
* The default pause between two attempts. |
||||
*/ |
||||
private static final long DEFAULT_PAUSE = 200; |
||||
|
||||
/** |
||||
* The default socket timeout. |
||||
*/ |
||||
private static final long DEFAULT_TIMEOUT = 5000; |
||||
|
||||
/** |
||||
* The temporary directory on Unix systems. |
||||
*/ |
||||
private static final String TEMPORARY_DIRECTORY = "/tmp"; |
||||
|
||||
/** |
||||
* The name prefix for a socket. |
||||
*/ |
||||
private static final String SOCKET_FILE_PREFIX = ".java_pid"; |
||||
|
||||
/** |
||||
* The name prefix for an attachment file indicator. |
||||
*/ |
||||
private static final String ATTACH_FILE_PREFIX = ".attach_pid"; |
||||
|
||||
/** |
||||
* The Unix socket to use for communication. The containing object is supposed to be an instance |
||||
* of {@link AFUNIXSocket} which is however not set to avoid eager loading |
||||
*/ |
||||
private final Object socket; |
||||
|
||||
/** |
||||
* The number of attempts to connect. |
||||
*/ |
||||
private final int attempts; |
||||
|
||||
/** |
||||
* The time to pause between attempts. |
||||
*/ |
||||
private final long pause; |
||||
|
||||
/** |
||||
* The socket timeout. |
||||
*/ |
||||
private final long timeout; |
||||
|
||||
/** |
||||
* The time unit of the pause time. |
||||
*/ |
||||
private final TimeUnit timeUnit; |
||||
|
||||
/** |
||||
* Creates a new VM implementation for a HotSpot VM running on Unix. |
||||
* |
||||
* @param processId The process id of the target VM. |
||||
* @param socket The Unix socket to use for communication. |
||||
* @param attempts The number of attempts to connect. |
||||
* @param pause The pause time between two VMs. |
||||
* @param timeout The socket timeout. |
||||
* @param timeUnit The time unit of the pause time. |
||||
*/ |
||||
public OnUnix(String processId, Object socket, int attempts, long pause, long timeout, TimeUnit timeUnit) { |
||||
super(processId); |
||||
this.socket = socket; |
||||
this.attempts = attempts; |
||||
this.pause = pause; |
||||
this.timeout = timeout; |
||||
this.timeUnit = timeUnit; |
||||
} |
||||
|
||||
/** |
||||
* Asserts the availability of this virtual machine implementation. If the Unix socket library is missing or |
||||
* if this VM does not support Unix socket communication, a {@link Throwable} is thrown. |
||||
* |
||||
* @return This virtual machine type. |
||||
* @throws Throwable If this VM does not support POSIX sockets or is not running on a HotSpot VM. |
||||
*/ |
||||
public static Class<?> assertAvailability() throws Throwable { |
||||
if (!AFUNIXSocket.isSupported()) { |
||||
throw new IllegalStateException("POSIX sockets are not supported on the current system"); |
||||
} else if (!System.getProperty("java.vm.name").toLowerCase(Locale.US).contains("hotspot")) { |
||||
throw new IllegalStateException("Cannot apply attachment on non-Hotspot compatible VM"); |
||||
} else { |
||||
return OnUnix.class; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Attaches to the supplied VM process. |
||||
* |
||||
* @param processId The process id of the target VM. |
||||
* @return An appropriate virtual machine implementation. |
||||
* @throws IOException If an I/O exception occurs. |
||||
*/ |
||||
public static VirtualMachine attach(String processId) throws IOException { |
||||
return new OnUnix(processId, AFUNIXSocket.newInstance(), DEFAULT_ATTEMPTS, DEFAULT_PAUSE, DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS); |
||||
} |
||||
|
||||
@Override |
||||
protected void connect() throws IOException { |
||||
File socketFile = new File(TEMPORARY_DIRECTORY, SOCKET_FILE_PREFIX + processId); |
||||
if (!socketFile.exists()) { |
||||
String target = ATTACH_FILE_PREFIX + processId, path = "/proc/" + processId + "/cwd/" + target; |
||||
File attachFile = new File(path); |
||||
try { |
||||
if (!attachFile.createNewFile() && !attachFile.isFile()) { |
||||
throw new IllegalStateException("Could not create attach file: " + attachFile); |
||||
} |
||||
} catch (IOException ignored) { |
||||
attachFile = new File(TEMPORARY_DIRECTORY, target); |
||||
if (!attachFile.createNewFile() && !attachFile.isFile()) { |
||||
throw new IllegalStateException("Could not create attach file: " + attachFile); |
||||
} |
||||
} |
||||
try { |
||||
// The HotSpot attachment API attempts to send the signal to all children of a process
|
||||
Process process = Runtime.getRuntime().exec("kill -3 " + processId); |
||||
int attempts = this.attempts; |
||||
boolean killed = false; |
||||
do { |
||||
try { |
||||
if (process.exitValue() != 0) { |
||||
throw new IllegalStateException("Error while sending signal to target VM: " + processId); |
||||
} |
||||
killed = true; |
||||
break; |
||||
} catch (IllegalThreadStateException ignored) { |
||||
attempts -= 1; |
||||
Thread.sleep(timeUnit.toMillis(pause)); |
||||
} |
||||
} while (attempts > 0); |
||||
if (!killed) { |
||||
throw new IllegalStateException("Target VM did not respond to signal: " + processId); |
||||
} |
||||
attempts = this.attempts; |
||||
while (attempts-- > 0 && !socketFile.exists()) { |
||||
Thread.sleep(timeUnit.toMillis(pause)); |
||||
} |
||||
if (!socketFile.exists()) { |
||||
throw new IllegalStateException("Target VM did not respond: " + processId); |
||||
} |
||||
} catch (InterruptedException exception) { |
||||
throw new IllegalStateException("Interrupted during wait for process", exception); |
||||
} finally { |
||||
if (!attachFile.delete()) { |
||||
attachFile.deleteOnExit(); |
||||
} |
||||
} |
||||
} |
||||
((AFUNIXSocket) socket).setSoTimeout((int) timeUnit.toMillis(timeout)); |
||||
((AFUNIXSocket) socket).connect(new AFUNIXSocketAddress(socketFile)); |
||||
} |
||||
|
||||
@Override |
||||
public int read(byte[] buffer) throws IOException { |
||||
return ((AFUNIXSocket) this.socket).getInputStream().read(buffer); |
||||
} |
||||
|
||||
@Override |
||||
public void write(byte[] buffer) throws IOException { |
||||
((AFUNIXSocket) this.socket).getOutputStream().write(buffer); |
||||
} |
||||
|
||||
@Override |
||||
public void detach() throws IOException { |
||||
((AFUNIXSocket) this.socket).close(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,4 @@
|
||||
/** |
||||
* The Byte Buddy agent allows the redefinition of classes at runtime. |
||||
*/ |
||||
package com.fr.third.net.bytebuddy.agent; |
@ -0,0 +1,141 @@
|
||||
/** |
||||
* junixsocket |
||||
* |
||||
* Copyright (c) 2009,2014 Christian Kohlschütter |
||||
* |
||||
* The author licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.fr.third.org.newsclub.net.unix; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.ServerSocket; |
||||
import java.net.Socket; |
||||
import java.net.SocketAddress; |
||||
import java.net.SocketException; |
||||
|
||||
/** |
||||
* The server part of an AF_UNIX domain socket. |
||||
* |
||||
* @author Christian Kohlschütter |
||||
*/ |
||||
public class AFUNIXServerSocket extends ServerSocket { |
||||
private final AFUNIXSocketImpl implementation; |
||||
private AFUNIXSocketAddress boundEndpoint = null; |
||||
|
||||
private final Thread shutdownThread = new Thread() { |
||||
@Override |
||||
public void run() { |
||||
try { |
||||
if (boundEndpoint != null) { |
||||
NativeUnixSocket.unlink(boundEndpoint.getSocketFile()); |
||||
} |
||||
} catch (IOException e) { |
||||
// ignore
|
||||
} |
||||
} |
||||
}; |
||||
|
||||
protected AFUNIXServerSocket() throws IOException { |
||||
super(); |
||||
this.implementation = new AFUNIXSocketImpl(); |
||||
NativeUnixSocket.initServerImpl(this, implementation); |
||||
|
||||
Runtime.getRuntime().addShutdownHook(shutdownThread); |
||||
NativeUnixSocket.setCreatedServer(this); |
||||
} |
||||
|
||||
/** |
||||
* Returns a new, unbound AF_UNIX {@link ServerSocket}. |
||||
* |
||||
* @return The new, unbound {@link AFUNIXServerSocket}. |
||||
*/ |
||||
public static AFUNIXServerSocket newInstance() throws IOException { |
||||
AFUNIXServerSocket instance = new AFUNIXServerSocket(); |
||||
return instance; |
||||
} |
||||
|
||||
/** |
||||
* Returns a new AF_UNIX {@link ServerSocket} that is bound to the given |
||||
* {@link AFUNIXSocketAddress}. |
||||
* |
||||
* @return The new, unbound {@link AFUNIXServerSocket}. |
||||
*/ |
||||
public static AFUNIXServerSocket bindOn(final AFUNIXSocketAddress addr) throws IOException { |
||||
AFUNIXServerSocket socket = newInstance(); |
||||
socket.bind(addr); |
||||
return socket; |
||||
} |
||||
|
||||
@Override |
||||
public void bind(SocketAddress endpoint, int backlog) throws IOException { |
||||
if (isClosed()) { |
||||
throw new SocketException("Socket is closed"); |
||||
} |
||||
if (isBound()) { |
||||
throw new SocketException("Already bound"); |
||||
} |
||||
if (!(endpoint instanceof AFUNIXSocketAddress)) { |
||||
throw new IOException("Can only bind to endpoints of type " |
||||
+ AFUNIXSocketAddress.class.getName()); |
||||
} |
||||
implementation.bind(backlog, endpoint); |
||||
boundEndpoint = (AFUNIXSocketAddress) endpoint; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isBound() { |
||||
return boundEndpoint != null; |
||||
} |
||||
|
||||
@Override |
||||
public Socket accept() throws IOException { |
||||
if (isClosed()) { |
||||
throw new SocketException("Socket is closed"); |
||||
} |
||||
AFUNIXSocket as = AFUNIXSocket.newInstance(); |
||||
implementation.accept(as.impl); |
||||
as.addr = boundEndpoint; |
||||
NativeUnixSocket.setConnected(as); |
||||
return as; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
if (!isBound()) { |
||||
return "AFUNIXServerSocket[unbound]"; |
||||
} |
||||
return "AFUNIXServerSocket[" + boundEndpoint.getSocketFile() + "]"; |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
if (isClosed()) { |
||||
return; |
||||
} |
||||
|
||||
super.close(); |
||||
implementation.close(); |
||||
if (boundEndpoint != null) { |
||||
NativeUnixSocket.unlink(boundEndpoint.getSocketFile()); |
||||
} |
||||
try { |
||||
Runtime.getRuntime().removeShutdownHook(shutdownThread); |
||||
} catch (IllegalStateException e) { |
||||
// ignore
|
||||
} |
||||
} |
||||
|
||||
public static boolean isSupported() { |
||||
return NativeUnixSocket.isLoaded(); |
||||
} |
||||
} |
@ -0,0 +1,131 @@
|
||||
/** |
||||
* junixsocket |
||||
* |
||||
* Copyright (c) 2009,2014 Christian Kohlschütter |
||||
* |
||||
* The author licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.fr.third.org.newsclub.net.unix; |
||||
|
||||
import java.io.IOException; |
||||
import java.net.Socket; |
||||
import java.net.SocketAddress; |
||||
|
||||
/** |
||||
* Implementation of an AF_UNIX domain socket. |
||||
* |
||||
* @author Christian Kohlschütter |
||||
*/ |
||||
public class AFUNIXSocket extends Socket { |
||||
protected AFUNIXSocketImpl impl; |
||||
AFUNIXSocketAddress addr; |
||||
|
||||
private AFUNIXSocket(final AFUNIXSocketImpl impl) throws IOException { |
||||
super(impl); |
||||
try { |
||||
NativeUnixSocket.setCreated(this); |
||||
} catch (UnsatisfiedLinkError e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new, unbound {@link AFUNIXSocket}. |
||||
* |
||||
* This "default" implementation is a bit "lenient" with respect to the specification. |
||||
* |
||||
* In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and |
||||
* {@link Socket#setTcpNoDelay(boolean)}. |
||||
* |
||||
* @return A new, unbound socket. |
||||
*/ |
||||
public static AFUNIXSocket newInstance() throws IOException { |
||||
final AFUNIXSocketImpl impl = new AFUNIXSocketImpl.Lenient(); |
||||
AFUNIXSocket instance = new AFUNIXSocket(impl); |
||||
instance.impl = impl; |
||||
return instance; |
||||
} |
||||
|
||||
/** |
||||
* Creates a new, unbound, "strict" {@link AFUNIXSocket}. |
||||
* |
||||
* This call uses an implementation that tries to be closer to the specification than |
||||
* {@link #newInstance()}, at least for some cases. |
||||
* |
||||
* @return A new, unbound socket. |
||||
*/ |
||||
public static AFUNIXSocket newStrictInstance() throws IOException { |
||||
final AFUNIXSocketImpl impl = new AFUNIXSocketImpl(); |
||||
AFUNIXSocket instance = new AFUNIXSocket(impl); |
||||
instance.impl = impl; |
||||
return instance; |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@link AFUNIXSocket} and connects it to the given {@link AFUNIXSocketAddress}. |
||||
* |
||||
* @param addr The address to connect to. |
||||
* @return A new, connected socket. |
||||
*/ |
||||
public static AFUNIXSocket connectTo(AFUNIXSocketAddress addr) throws IOException { |
||||
AFUNIXSocket socket = newInstance(); |
||||
socket.connect(addr); |
||||
return socket; |
||||
} |
||||
|
||||
/** |
||||
* Binds this {@link AFUNIXSocket} to the given bindpoint. Only bindpoints of the type |
||||
* {@link AFUNIXSocketAddress} are supported. |
||||
*/ |
||||
@Override |
||||
public void bind(SocketAddress bindpoint) throws IOException { |
||||
super.bind(bindpoint); |
||||
this.addr = (AFUNIXSocketAddress) bindpoint; |
||||
} |
||||
|
||||
@Override |
||||
public void connect(SocketAddress endpoint) throws IOException { |
||||
connect(endpoint, 0); |
||||
} |
||||
|
||||
@Override |
||||
public void connect(SocketAddress endpoint, int timeout) throws IOException { |
||||
if (!(endpoint instanceof AFUNIXSocketAddress)) { |
||||
throw new IOException("Can only connect to endpoints of type " |
||||
+ AFUNIXSocketAddress.class.getName()); |
||||
} |
||||
impl.connect(endpoint, timeout); |
||||
this.addr = (AFUNIXSocketAddress) endpoint; |
||||
NativeUnixSocket.setConnected(this); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
if (isConnected()) { |
||||
return "AFUNIXSocket[fd=" + impl.getFD() + ";path=" + addr.getSocketFile() + "]"; |
||||
} |
||||
return "AFUNIXSocket[unconnected]"; |
||||
} |
||||
|
||||
/** |
||||
* Returns <code>true</code> iff {@link AFUNIXSocket}s are supported by the current Java VM. |
||||
* |
||||
* To support {@link AFUNIXSocket}s, a custom JNI library must be loaded that is supplied with |
||||
* <em>junixsocket</em>. |
||||
* |
||||
* @return {@code true} iff supported. |
||||
*/ |
||||
public static boolean isSupported() { |
||||
return NativeUnixSocket.isLoaded(); |
||||
} |
||||
} |
@ -0,0 +1,76 @@
|
||||
/** |
||||
* junixsocket |
||||
* |
||||
* Copyright (c) 2009,2014 Christian Kohlschütter |
||||
* |
||||
* The author licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.fr.third.org.newsclub.net.unix; |
||||
|
||||
import java.io.File; |
||||
import java.io.IOException; |
||||
import java.net.InetSocketAddress; |
||||
|
||||
/** |
||||
* Describes an {@link InetSocketAddress} that actually uses AF_UNIX sockets instead of AF_INET. |
||||
* |
||||
* The ability to specify a port number is not specified by AF_UNIX sockets, but we need it |
||||
* sometimes, for example for RMI-over-AF_UNIX. |
||||
* |
||||
* @author Christian Kohlschütter |
||||
*/ |
||||
public class AFUNIXSocketAddress extends InetSocketAddress { |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
private final String socketFile; |
||||
|
||||
/** |
||||
* Creates a new {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the |
||||
* given file. |
||||
* |
||||
* @param socketFile The socket to connect to. |
||||
*/ |
||||
public AFUNIXSocketAddress(final File socketFile) throws IOException { |
||||
this(socketFile, 0); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@link AFUNIXSocketAddress} that points to the AF_UNIX socket specified by the |
||||
* given file, assigning the given port to it. |
||||
* |
||||
* @param socketFile The socket to connect to. |
||||
* @param port The port associated with this socket, or {@code 0} when no port should be assigned. |
||||
*/ |
||||
public AFUNIXSocketAddress(final File socketFile, int port) throws IOException { |
||||
super(0); |
||||
if (port != 0) { |
||||
NativeUnixSocket.setPort1(this, port); |
||||
} |
||||
this.socketFile = socketFile.getCanonicalPath(); |
||||
} |
||||
|
||||
/** |
||||
* Returns the (canonical) file path for this {@link AFUNIXSocketAddress}. |
||||
* |
||||
* @return The file path. |
||||
*/ |
||||
public String getSocketFile() { |
||||
return socketFile; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getName() + "[host=" + getHostName() + ";port=" + getPort() + ";file=" |
||||
+ socketFile + "]"; |
||||
} |
||||
} |
@ -0,0 +1,53 @@
|
||||
/** |
||||
* junixsocket |
||||
* |
||||
* Copyright (c) 2009,2014 Christian Kohlschütter |
||||
* |
||||
* The author licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.fr.third.org.newsclub.net.unix; |
||||
|
||||
import java.net.SocketException; |
||||
|
||||
/** |
||||
* Something went wrong with the communication to a Unix socket. |
||||
* |
||||
* @author Christian Kohlschütter |
||||
*/ |
||||
public class AFUNIXSocketException extends SocketException { |
||||
private static final long serialVersionUID = 1L; |
||||
private final String socketFile; |
||||
|
||||
public AFUNIXSocketException(String reason) { |
||||
this(reason, (String) null); |
||||
} |
||||
|
||||
public AFUNIXSocketException(String reason, final Throwable cause) { |
||||
this(reason, (String) null); |
||||
initCause(cause); |
||||
} |
||||
|
||||
public AFUNIXSocketException(String reason, final String socketFile) { |
||||
super(reason); |
||||
this.socketFile = socketFile; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
if (socketFile == null) { |
||||
return super.toString(); |
||||
} else { |
||||
return super.toString() + " (socket: " + socketFile + ")"; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,411 @@
|
||||
/** |
||||
* junixsocket |
||||
* |
||||
* Copyright (c) 2009,2014 Christian Kohlschütter |
||||
* |
||||
* The author licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.fr.third.org.newsclub.net.unix; |
||||
|
||||
import java.io.FileDescriptor; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.io.OutputStream; |
||||
import java.net.InetAddress; |
||||
import java.net.Socket; |
||||
import java.net.SocketAddress; |
||||
import java.net.SocketException; |
||||
import java.net.SocketImpl; |
||||
import java.net.SocketOptions; |
||||
|
||||
/** |
||||
* The Java-part of the {@link AFUNIXSocket} implementation. |
||||
* |
||||
* @author Christian Kohlschütter |
||||
*/ |
||||
class AFUNIXSocketImpl extends SocketImpl { |
||||
private static final int SHUT_RD = 0; |
||||
private static final int SHUT_WR = 1; |
||||
private static final int SHUT_RD_WR = 2; |
||||
|
||||
private String socketFile; |
||||
private boolean closed = false; |
||||
private boolean bound = false; |
||||
private boolean connected = false; |
||||
|
||||
private boolean closedInputStream = false; |
||||
private boolean closedOutputStream = false; |
||||
|
||||
private final AFUNIXInputStream in = new AFUNIXInputStream(); |
||||
private final AFUNIXOutputStream out = new AFUNIXOutputStream(); |
||||
|
||||
AFUNIXSocketImpl() { |
||||
super(); |
||||
this.fd = new FileDescriptor(); |
||||
} |
||||
|
||||
FileDescriptor getFD() { |
||||
return fd; |
||||
} |
||||
|
||||
@Override |
||||
protected void accept(SocketImpl socket) throws IOException { |
||||
final AFUNIXSocketImpl si = (AFUNIXSocketImpl) socket; |
||||
NativeUnixSocket.accept(socketFile, fd, si.fd); |
||||
si.socketFile = socketFile; |
||||
si.connected = true; |
||||
} |
||||
|
||||
@Override |
||||
protected int available() throws IOException { |
||||
return NativeUnixSocket.available(fd); |
||||
} |
||||
|
||||
protected void bind(SocketAddress addr) throws IOException { |
||||
bind(0, addr); |
||||
} |
||||
|
||||
protected void bind(int backlog, SocketAddress addr) throws IOException { |
||||
if (!(addr instanceof AFUNIXSocketAddress)) { |
||||
throw new SocketException("Cannot bind to this type of address: " + addr.getClass()); |
||||
} |
||||
final AFUNIXSocketAddress socketAddress = (AFUNIXSocketAddress) addr; |
||||
socketFile = socketAddress.getSocketFile(); |
||||
NativeUnixSocket.bind(socketFile, fd, backlog); |
||||
bound = true; |
||||
this.localport = socketAddress.getPort(); |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("hiding") |
||||
protected void bind(InetAddress host, int port) throws IOException { |
||||
throw new SocketException("Cannot bind to this type of address: " + InetAddress.class); |
||||
} |
||||
|
||||
private void checkClose() throws IOException { |
||||
if (closedInputStream && closedOutputStream) { |
||||
// close();
|
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected synchronized void close() throws IOException { |
||||
if (closed) { |
||||
return; |
||||
} |
||||
closed = true; |
||||
if (fd.valid()) { |
||||
NativeUnixSocket.shutdown(fd, SHUT_RD_WR); |
||||
NativeUnixSocket.close(fd); |
||||
} |
||||
if (bound) { |
||||
NativeUnixSocket.unlink(socketFile); |
||||
} |
||||
connected = false; |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("hiding") |
||||
protected void connect(String host, int port) throws IOException { |
||||
throw new SocketException("Cannot bind to this type of address: " + InetAddress.class); |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("hiding") |
||||
protected void connect(InetAddress address, int port) throws IOException { |
||||
throw new SocketException("Cannot bind to this type of address: " + InetAddress.class); |
||||
} |
||||
|
||||
@Override |
||||
protected void connect(SocketAddress addr, int timeout) throws IOException { |
||||
if (!(addr instanceof AFUNIXSocketAddress)) { |
||||
throw new SocketException("Cannot bind to this type of address: " + addr.getClass()); |
||||
} |
||||
final AFUNIXSocketAddress socketAddress = (AFUNIXSocketAddress) addr; |
||||
socketFile = socketAddress.getSocketFile(); |
||||
NativeUnixSocket.connect(socketFile, fd); |
||||
this.address = socketAddress.getAddress(); |
||||
this.port = socketAddress.getPort(); |
||||
this.localport = 0; |
||||
this.connected = true; |
||||
} |
||||
|
||||
@Override |
||||
protected void create(boolean stream) throws IOException { |
||||
} |
||||
|
||||
@Override |
||||
protected InputStream getInputStream() throws IOException { |
||||
if (!connected && !bound) { |
||||
throw new IOException("Not connected/not bound"); |
||||
} |
||||
return in; |
||||
} |
||||
|
||||
@Override |
||||
protected OutputStream getOutputStream() throws IOException { |
||||
if (!connected && !bound) { |
||||
throw new IOException("Not connected/not bound"); |
||||
} |
||||
return out; |
||||
} |
||||
|
||||
@Override |
||||
protected void listen(int backlog) throws IOException { |
||||
NativeUnixSocket.listen(fd, backlog); |
||||
} |
||||
|
||||
@Override |
||||
protected void sendUrgentData(int data) throws IOException { |
||||
NativeUnixSocket.write(fd, new byte[] {(byte) (data & 0xFF)}, 0, 1); |
||||
} |
||||
|
||||
private final class AFUNIXInputStream extends InputStream { |
||||
private boolean streamClosed = false; |
||||
|
||||
@Override |
||||
public int read(byte[] buf, int off, int len) throws IOException { |
||||
if (streamClosed) { |
||||
throw new IOException("This InputStream has already been closed."); |
||||
} |
||||
if (len == 0) { |
||||
return 0; |
||||
} |
||||
int maxRead = buf.length - off; |
||||
if (len > maxRead) { |
||||
len = maxRead; |
||||
} |
||||
try { |
||||
return NativeUnixSocket.read(fd, buf, off, len); |
||||
} catch (final IOException e) { |
||||
throw (IOException) new IOException(e.getMessage() + " at " |
||||
+ AFUNIXSocketImpl.this.toString()).initCause(e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int read() throws IOException { |
||||
final byte[] buf1 = new byte[1]; |
||||
final int numRead = read(buf1, 0, 1); |
||||
if (numRead <= 0) { |
||||
return -1; |
||||
} else { |
||||
return buf1[0] & 0xFF; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
if (streamClosed) { |
||||
return; |
||||
} |
||||
streamClosed = true; |
||||
if (fd.valid()) { |
||||
NativeUnixSocket.shutdown(fd, SHUT_RD); |
||||
} |
||||
|
||||
closedInputStream = true; |
||||
checkClose(); |
||||
} |
||||
|
||||
@Override |
||||
public int available() throws IOException { |
||||
final int av = NativeUnixSocket.available(fd); |
||||
return av; |
||||
} |
||||
} |
||||
|
||||
private final class AFUNIXOutputStream extends OutputStream { |
||||
private boolean streamClosed = false; |
||||
|
||||
@Override |
||||
public void write(int oneByte) throws IOException { |
||||
final byte[] buf1 = new byte[] {(byte) oneByte}; |
||||
write(buf1, 0, 1); |
||||
} |
||||
|
||||
@Override |
||||
public void write(byte[] buf, int off, int len) throws IOException { |
||||
if (streamClosed) { |
||||
throw new AFUNIXSocketException("This OutputStream has already been closed."); |
||||
} |
||||
if (len > buf.length - off) { |
||||
throw new IndexOutOfBoundsException(); |
||||
} |
||||
try { |
||||
while (len > 0 && !Thread.interrupted()) { |
||||
final int written = NativeUnixSocket.write(fd, buf, off, len); |
||||
if (written == -1) { |
||||
throw new IOException("Unspecific error while writing"); |
||||
} |
||||
len -= written; |
||||
off += written; |
||||
} |
||||
} catch (final IOException e) { |
||||
throw (IOException) new IOException(e.getMessage() + " at " |
||||
+ AFUNIXSocketImpl.this.toString()).initCause(e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
if (streamClosed) { |
||||
return; |
||||
} |
||||
streamClosed = true; |
||||
if (fd.valid()) { |
||||
NativeUnixSocket.shutdown(fd, SHUT_WR); |
||||
} |
||||
closedOutputStream = true; |
||||
checkClose(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return super.toString() + "[fd=" + fd + "; file=" + this.socketFile + "; connected=" |
||||
+ connected + "; bound=" + bound + "]"; |
||||
} |
||||
|
||||
private static int expectInteger(Object value) throws SocketException { |
||||
try { |
||||
return (Integer) value; |
||||
} catch (final ClassCastException e) { |
||||
throw new AFUNIXSocketException("Unsupported value: " + value, e); |
||||
} catch (final NullPointerException e) { |
||||
throw new AFUNIXSocketException("Value must not be null", e); |
||||
} |
||||
} |
||||
|
||||
private static int expectBoolean(Object value) throws SocketException { |
||||
try { |
||||
return ((Boolean) value).booleanValue() ? 1 : 0; |
||||
} catch (final ClassCastException e) { |
||||
throw new AFUNIXSocketException("Unsupported value: " + value, e); |
||||
} catch (final NullPointerException e) { |
||||
throw new AFUNIXSocketException("Value must not be null", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Object getOption(int optID) throws SocketException { |
||||
try { |
||||
switch (optID) { |
||||
case SocketOptions.SO_KEEPALIVE: |
||||
case SocketOptions.TCP_NODELAY: |
||||
return NativeUnixSocket.getSocketOptionInt(fd, optID) != 0 ? true : false; |
||||
case SocketOptions.SO_LINGER: |
||||
case SocketOptions.SO_TIMEOUT: |
||||
case SocketOptions.SO_RCVBUF: |
||||
case SocketOptions.SO_SNDBUF: |
||||
return NativeUnixSocket.getSocketOptionInt(fd, optID); |
||||
default: |
||||
throw new AFUNIXSocketException("Unsupported option: " + optID); |
||||
} |
||||
} catch (final AFUNIXSocketException e) { |
||||
throw e; |
||||
} catch (final Exception e) { |
||||
throw new AFUNIXSocketException("Error while getting option", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void setOption(int optID, Object value) throws SocketException { |
||||
try { |
||||
switch (optID) { |
||||
case SocketOptions.SO_LINGER: |
||||
|
||||
if (value instanceof Boolean) { |
||||
final boolean b = (Boolean) value; |
||||
if (b) { |
||||
throw new SocketException("Only accepting Boolean.FALSE here"); |
||||
} |
||||
NativeUnixSocket.setSocketOptionInt(fd, optID, -1); |
||||
return; |
||||
} |
||||
NativeUnixSocket.setSocketOptionInt(fd, optID, expectInteger(value)); |
||||
return; |
||||
case SocketOptions.SO_RCVBUF: |
||||
case SocketOptions.SO_SNDBUF: |
||||
case SocketOptions.SO_TIMEOUT: |
||||
NativeUnixSocket.setSocketOptionInt(fd, optID, expectInteger(value)); |
||||
return; |
||||
case SocketOptions.SO_KEEPALIVE: |
||||
case SocketOptions.TCP_NODELAY: |
||||
NativeUnixSocket.setSocketOptionInt(fd, optID, expectBoolean(value)); |
||||
return; |
||||
default: |
||||
throw new AFUNIXSocketException("Unsupported option: " + optID); |
||||
} |
||||
} catch (final AFUNIXSocketException e) { |
||||
throw e; |
||||
} catch (final Exception e) { |
||||
throw new AFUNIXSocketException("Error while setting option", e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void shutdownInput() throws IOException { |
||||
if (!closed && fd.valid()) { |
||||
NativeUnixSocket.shutdown(fd, SHUT_RD); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void shutdownOutput() throws IOException { |
||||
if (!closed && fd.valid()) { |
||||
NativeUnixSocket.shutdown(fd, SHUT_WR); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Changes the behavior to be somewhat lenient with respect to the specification. |
||||
* |
||||
* In particular, we ignore calls to {@link Socket#getTcpNoDelay()} and |
||||
* {@link Socket#setTcpNoDelay(boolean)}. |
||||
*/ |
||||
static class Lenient extends AFUNIXSocketImpl { |
||||
Lenient() { |
||||
super(); |
||||
} |
||||
|
||||
@Override |
||||
public void setOption(int optID, Object value) throws SocketException { |
||||
try { |
||||
super.setOption(optID, value); |
||||
} catch (SocketException e) { |
||||
switch (optID) { |
||||
case SocketOptions.TCP_NODELAY: |
||||
return; |
||||
default: |
||||
throw e; |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Object getOption(int optID) throws SocketException { |
||||
try { |
||||
return super.getOption(optID); |
||||
} catch (SocketException e) { |
||||
switch (optID) { |
||||
case SocketOptions.TCP_NODELAY: |
||||
case SocketOptions.SO_KEEPALIVE: |
||||
return false; |
||||
default: |
||||
throw e; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,131 @@
|
||||
/** |
||||
* junixsocket |
||||
* |
||||
* Copyright (c) 2009,2014 Christian Kohlschütter |
||||
* |
||||
* The author licenses this file to You under the Apache License, Version 2.0 |
||||
* (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
package com.fr.third.org.newsclub.net.unix; |
||||
|
||||
import java.io.FileDescriptor; |
||||
import java.io.IOException; |
||||
import java.lang.reflect.Field; |
||||
import java.net.InetSocketAddress; |
||||
|
||||
/** |
||||
* JNI connector to native JNI C code. |
||||
* |
||||
* @author Christian Kohlschütter |
||||
*/ |
||||
final class NativeUnixSocket { |
||||
private static boolean loaded = false; |
||||
|
||||
static { |
||||
try { |
||||
Class.forName("org.newsclub.net.unix.NarSystem").getMethod("loadLibrary").invoke(null); |
||||
} catch (ClassNotFoundException e) { |
||||
throw new IllegalStateException( |
||||
"Could not find NarSystem class.\n\n*** ECLIPSE USERS ***\nIf you're running from " |
||||
+ "within Eclipse, please try closing the \"junixsocket-native-common\" " |
||||
+ "project\n", e); |
||||
} catch (Exception e) { |
||||
throw new IllegalStateException(e); |
||||
} |
||||
loaded = true; |
||||
} |
||||
|
||||
static boolean isLoaded() { |
||||
return loaded; |
||||
} |
||||
|
||||
static void checkSupported() { |
||||
} |
||||
|
||||
static native void bind(final String socketFile, final FileDescriptor fd, final int backlog) |
||||
throws IOException; |
||||
|
||||
static native void listen(final FileDescriptor fd, final int backlog) throws IOException; |
||||
|
||||
static native void accept(final String socketFile, final FileDescriptor fdServer, |
||||
final FileDescriptor fd) throws IOException; |
||||
|
||||
static native void connect(final String socketFile, final FileDescriptor fd) throws IOException; |
||||
|
||||
static native int read(final FileDescriptor fd, byte[] buf, int off, int len) throws IOException; |
||||
|
||||
static native int write(final FileDescriptor fd, byte[] buf, int off, int len) throws IOException; |
||||
|
||||
static native void close(final FileDescriptor fd) throws IOException; |
||||
|
||||
static native void shutdown(final FileDescriptor fd, int mode) throws IOException; |
||||
|
||||
static native int getSocketOptionInt(final FileDescriptor fd, int optionId) throws IOException; |
||||
|
||||
static native void setSocketOptionInt(final FileDescriptor fd, int optionId, int value) |
||||
throws IOException; |
||||
|
||||
static native void unlink(final String socketFile) throws IOException; |
||||
|
||||
static native int available(final FileDescriptor fd) throws IOException; |
||||
|
||||
static native void initServerImpl(final AFUNIXServerSocket serverSocket, |
||||
final AFUNIXSocketImpl impl); |
||||
|
||||
static native void setCreated(final AFUNIXSocket socket); |
||||
|
||||
static native void setConnected(final AFUNIXSocket socket); |
||||
|
||||
static native void setBound(final AFUNIXSocket socket); |
||||
|
||||
static native void setCreatedServer(final AFUNIXServerSocket socket); |
||||
|
||||
static native void setBoundServer(final AFUNIXServerSocket socket); |
||||
|
||||
static native void setPort(final AFUNIXSocketAddress addr, int port); |
||||
|
||||
static void setPort1(AFUNIXSocketAddress addr, int port) throws AFUNIXSocketException { |
||||
if (port < 0) { |
||||
throw new IllegalArgumentException("port out of range:" + port); |
||||
} |
||||
|
||||
boolean setOk = false; |
||||
try { |
||||
final Field holderField = InetSocketAddress.class.getDeclaredField("holder"); |
||||
if (holderField != null) { |
||||
holderField.setAccessible(true); |
||||
|
||||
final Object holder = holderField.get(addr); |
||||
if (holder != null) { |
||||
final Field portField = holder.getClass().getDeclaredField("port"); |
||||
if (portField != null) { |
||||
portField.setAccessible(true); |
||||
portField.set(holder, port); |
||||
setOk = true; |
||||
} |
||||
} |
||||
} else { |
||||
setPort(addr, port); |
||||
} |
||||
} catch (final RuntimeException e) { |
||||
throw e; |
||||
} catch (final Exception e) { |
||||
if (e instanceof AFUNIXSocketException) { |
||||
throw (AFUNIXSocketException) e; |
||||
} |
||||
throw new AFUNIXSocketException("Could not set port", e); |
||||
} |
||||
if (!setOk) { |
||||
throw new AFUNIXSocketException("Could not set port"); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue