NativeKeyAccessFactory.java
/*
* Copyright (c) 2013 Robert Varga. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.tcpmd5.jni;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.Channel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.opendaylight.tcpmd5.api.KeyAccess;
import org.opendaylight.tcpmd5.api.KeyAccessFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class NativeKeyAccessFactory implements KeyAccessFactory {
private static final Logger LOG = LoggerFactory.getLogger(NativeKeyAccessFactory.class);
private static final String LIBNAME = "libtcpmd5-jni.so";
private static volatile NativeKeyAccessFactory instance = null;
private final Map<Channel, KeyAccess> channels = new WeakHashMap<>();
private NativeKeyAccessFactory() {
}
/**
* Get the singleton instance.
*
* @return Singleton service instance.
* @throws NativeSupportUnavailableException if run-time does not support the functions needed to instantiate an
* operational factory.
*/
public static NativeKeyAccessFactory getInstance() throws NativeSupportUnavailableException {
if (instance != null) {
return instance;
}
synchronized (NativeKeyAccessFactory.class) {
if (instance == null) {
try {
loadLibrary();
} catch (IOException | RuntimeException e) {
throw new NativeSupportUnavailableException("Failed to load native library. Is it present and are you running a supported operating system?", e);
}
int rt = NarSystem.runUnitTests();
if (rt == 0) {
throw new NativeSupportUnavailableException("Run-time does not support required functionality. Is your operating system configured correcty?");
}
LOG.debug("Run-time found {} supported channel classes", rt);
instance = new NativeKeyAccessFactory();
}
return instance;
}
}
@Override
public KeyAccess getKeyAccess(final Channel channel) {
if (!NativeKeyAccess.isClassSupported0(channel.getClass())) {
LOG.debug("No support available for class {}", channel.getClass());
return null;
}
synchronized (channels) {
KeyAccess e = channels.get(channel);
if (e == null) {
e = new NativeKeyAccess(channel);
channels.put(channel, e);
}
return e;
}
}
@Override
public boolean canHandleChannelClass(final Class<? extends Channel> clazz) {
if (!NativeKeyAccess.isClassSupported0(Preconditions.checkNotNull(clazz))) {
LOG.debug("No support available for class {}", clazz);
return false;
}
return true;
}
private static void loadStream(final InputStream is) throws IOException {
final Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
final Path p = Files.createTempFile(LIBNAME, null, PosixFilePermissions.asFileAttribute(perms));
try {
LOG.debug("Copying {} to {}", is, p);
Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
try {
Runtime.getRuntime().load(p.toString());
LOG.info("Library {} loaded", p);
} catch (UnsatisfiedLinkError e) {
throw new IOException(String.format("Failed to load library %s", p), e);
}
} finally {
try {
Files.deleteIfExists(p);
} catch (IOException e) {
LOG.warn("Failed to remove temporary file {}", p, e);
}
}
}
private static void loadLibrary() throws IOException {
try (final InputStream is = NativeKeyAccessFactory.class.getResourceAsStream('/' + LIBNAME)) {
if (is == null) {
throw new IOException(String.format("Failed to open library resource %s", LIBNAME));
}
loadStream(is);
}
}
}