NativeKeyAccess.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.net.InetAddress;
import java.nio.channels.Channel;
import java.util.Arrays;
import java.util.Map.Entry;

import org.opendaylight.tcpmd5.api.KeyAccess;
import org.opendaylight.tcpmd5.api.KeyMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation of KeyAccess using Java Native Interface plugin to talk directly to the underlying operating system.
 */
final class NativeKeyAccess implements KeyAccess {
    private static final Logger LOG = LoggerFactory.getLogger(NativeKeyAccess.class);

    private static native void setChannelKey0(Channel channel, byte[] address, byte[] key) throws IOException;

    static native boolean isClassSupported0(Class<?> channel);

    private KeyMapping currentKeys = new KeyMapping();
    private final Channel channel;

    NativeKeyAccess(final Channel channel) {
        this.channel = Preconditions.checkNotNull(channel);
    }

    @Override
    public void setKeys(final KeyMapping keys) throws IOException {
        for (Entry<InetAddress, byte[]> e : keys.entrySet()) {
            Preconditions.checkArgument(e.getKey() != null, "Null address is not allowed");
            Preconditions.checkArgument(e.getValue() != null, "Key for address %s is null", e.getKey());
            Preconditions.checkArgument(e.getValue().length != 0, "Key for address %s is empty", e.getKey());
        }

        synchronized (this) {
            // Walk current keys and delete any that are not in the new map
            for (Entry<InetAddress, byte[]> e : currentKeys.entrySet()) {
                if (!keys.containsKey(e.getKey())) {
                    LOG.debug("Channel {} removing key for address {}", channel, e.getKey());
                    setChannelKey0(channel, e.getKey().getAddress(), null);
                }
            }

            for (Entry<InetAddress, byte[]> e : keys.entrySet()) {
                final byte[] currentValue = currentKeys.get(e.getKey());
                if (!Arrays.equals(currentValue, e.getValue())) {
                    LOG.debug("Channel {} adding key for address {}", channel, e.getKey());
                    setChannelKey0(channel, e.getKey().getAddress(), e.getValue());
                }
            }

            this.currentKeys = new KeyMapping(keys);
            LOG.debug("Channel {} key mapping update completed", channel);
        }
    }

    @Override
    public synchronized KeyMapping getKeys() {
        return currentKeys;
    }
}