void unsetDataPacketService(IDataPacketService s) {
if (this.dataPacketService == s) {
- this.dataPacketService = s;
+ this.dataPacketService = null;
}
}
# of.listenPort=6633
# The time (in milliseconds) the controller will wait for a response after sending a Barrier Request or a Statistic Request message (default 2000 msec)
# of.messageResponseTimer=2000
+
+# TLS configuration
+# To enable TLS, set secureChannelEnabled=true and specify the location of controller Java KeyStore and TrustStore files.
+# The Java KeyStore contains controller's private key and certificate. The Java TrustStore contains the trusted certificate
+# entries, including switches' Certification Authority (CA) certificates. For example,
+# secureChannelEnabled=true
+# controllerKeyStore=./configuration/ctlKeyStore
+# controllerKeyStorePassword=xxxxx (this password should match the password used for KeyStore generation)
+# controllerTrustStore=./configuration/ctlTrustStore
+# controllerTrustStorePassword=xxxxx (this password should match the password used for TrustStore generation)
+
+secureChannelEnabled=false
+controllerKeyStore=
+controllerKeyStorePassword=
+controllerTrustStore=
+controllerTrustStorePassword=
<logger name="org.opendaylight.controller.protocol_plugin.openflow.internal.InventoryServiceShim" level="INFO"/>
<logger name="org.opendaylight.controller.protocol_plugin.openflow.internal.TopologyServices" level="INFO"/>
<logger name="org.opendaylight.controller.protocol_plugin.openflow.internal.TopologyServiceShim" level="INFO"/>
+ <logger name="org.opendaylight.controller.protocol_plugin.openflow.core.internal.Controller" level="INFO"/>
+ <logger name="org.opendaylight.controller.protocol_plugin.openflow.core.internal.SwitchHandler" level="INFO"/>
+ <logger name="org.opendaylight.controller.protocol_plugin.openflow.core.internal.SwitchIOSecureService" level="INFO"/>
<!-- SAL -->
<logger name="org.opendaylight.controller.sal" level="INFO"/>
<logger name="org.opendaylight.controller.sal.implementation" level="INFO"/>
org.apache.felix.dm,
org.slf4j,
org.eclipse.osgi.framework.console,
- org.osgi.framework
+ org.osgi.framework,
+ javax.net.ssl
</Import-Package>
<Export-Package>
org.opendaylight.controller.protocol_plugin.openflow.internal
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.protocol_plugin.openflow.core;
+
+import java.util.List;
+
+import org.openflow.protocol.OFMessage;
+
+/**
+ * This interface defines low level routines to read/write messages on an open
+ * socket channel. If secure communication is desired, these methods also perform
+ * encryption and decryption of the network data.
+ */
+public interface IMessageReadWrite {
+ /**
+ * Sends the OF message out over the socket channel. For secure
+ * communication, the data will be encrypted.
+ *
+ * @param msg OF message to be sent
+ * @throws Exception
+ */
+ public void asyncSend(OFMessage msg) throws Exception;
+
+ /**
+ * Resumes sending the remaining messages in the outgoing buffer
+ * @throws Exception
+ */
+ public void resumeSend() throws Exception;
+
+ /**
+ * Reads the incoming network data from the socket and retrieves the OF
+ * messages. For secure communication, the data will be decrypted first.
+ *
+ * @return list of OF messages
+ * @throws Exception
+ */
+ public List<OFMessage> readMessages() throws Exception;
+}
public Date getConnectedDate();
/**
- * Sends the message. A unique XID is generated automatically and inserted into the message.
- * @param msg TheOF message to be sent
+ * This method puts the message in an outgoing priority queue with normal
+ * priority. It will be served after high priority messages. The method
+ * should be used for non-critical messages such as statistics request,
+ * discovery packets, etc. An unique XID is generated automatically and
+ * inserted into the message.
+ *
+ * @param msg The OF message to be sent
* @return The XID used
*/
public Integer asyncSend(OFMessage msg);
/**
- * Sends the message with the specified XID.
+ * This method puts the message in an outgoing priority queue with normal
+ * priority. It will be served after high priority messages. The method
+ * should be used for non-critical messages such as statistics request,
+ * discovery packets, etc. The specified XID is inserted into the message.
+ *
* @param msg The OF message to be Sent
* @param xid The XID to be used in the message
* @return The XID used
*/
public Integer asyncSend(OFMessage msg, int xid);
+ /**
+ * This method puts the message in an outgoing priority queue with high
+ * priority. It will be served first before normal priority messages. The
+ * method should be used for critical messages such as hello, echo reply
+ * etc. An unique XID is generated automatically and inserted into the
+ * message.
+ *
+ * @param msg The OF message to be sent
+ * @return The XID used
+ */
+ public Integer asyncFastSend(OFMessage msg);
+
+ /**
+ * This method puts the message in an outgoing priority queue with high
+ * priority. It will be served first before normal priority messages. The
+ * method should be used for critical messages such as hello, echo reply
+ * etc. The specified XID is inserted into the message.
+ *
+ * @param msg The OF message to be sent
+ * @return The XID used
+ */
+ public Integer asyncFastSend(OFMessage msg, int xid);
+
/**
* Sends the OF message followed by a Barrier Request with a unique XID which is automatically generated,
* and waits for a result from the switch.
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.protocol_plugin.openflow.core.internal;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.util.List;
+
+import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageReadWrite;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.factory.BasicFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements methods to read/write messages over an established
+ * socket channel. The data exchange is in clear text format.
+ */
+public class MessageReadWriteService implements IMessageReadWrite {
+ private static final Logger logger = LoggerFactory
+ .getLogger(MessageReadWriteService.class);
+ private static final int bufferSize = 1024 * 1024;
+
+ private Selector selector;
+ private SelectionKey clientSelectionKey;
+ private SocketChannel socket;
+ private ByteBuffer inBuffer;
+ private ByteBuffer outBuffer;
+ private BasicFactory factory;
+
+ public MessageReadWriteService(SocketChannel socket, Selector selector) throws ClosedChannelException {
+ this.socket = socket;
+ this.selector = selector;
+ this.factory = new BasicFactory();
+ this.inBuffer = ByteBuffer.allocateDirect(bufferSize);
+ this.outBuffer = ByteBuffer.allocateDirect(bufferSize);
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ);
+ }
+
+ /**
+ * Sends the OF message out over the socket channel.
+ *
+ * @param msg OF message to be sent
+ * @throws Exception
+ */
+ @Override
+ public void asyncSend(OFMessage msg) throws IOException {
+ synchronized (outBuffer) {
+ int msgLen = msg.getLengthU();
+ if (outBuffer.remaining() < msgLen) {
+ // increase the buffer size so that it can contain this message
+ ByteBuffer newBuffer = ByteBuffer.allocateDirect(outBuffer
+ .capacity()
+ + msgLen);
+ outBuffer.flip();
+ newBuffer.put(outBuffer);
+ outBuffer = newBuffer;
+ }
+ msg.writeTo(outBuffer);
+
+ if (!socket.isOpen()) {
+ return;
+ }
+
+ outBuffer.flip();
+ socket.write(outBuffer);
+ outBuffer.compact();
+ if (outBuffer.position() > 0) {
+ this.clientSelectionKey = this.socket.register(
+ this.selector, SelectionKey.OP_WRITE, this);
+ }
+ logger.trace("Message sent: {}", msg.toString());
+ }
+ }
+
+ /**
+ * Resumes sending the remaining messages in the outgoing buffer
+ * @throws Exception
+ */
+ @Override
+ public void resumeSend() throws IOException {
+ synchronized (outBuffer) {
+ if (!socket.isOpen()) {
+ return;
+ }
+
+ outBuffer.flip();
+ socket.write(outBuffer);
+ outBuffer.compact();
+ if (outBuffer.position() > 0) {
+ this.clientSelectionKey = this.socket.register(
+ this.selector, SelectionKey.OP_WRITE, this);
+ } else {
+ this.clientSelectionKey = this.socket.register(
+ this.selector, SelectionKey.OP_READ, this);
+ }
+ }
+ }
+
+ /**
+ * Reads the incoming network data from the socket and retrieves the OF
+ * messages.
+ *
+ * @return list of OF messages
+ * @throws Exception
+ */
+ @Override
+ public List<OFMessage> readMessages() throws IOException {
+ if (!socket.isOpen()) {
+ return null;
+ }
+
+ List<OFMessage> msgs = null;
+ int bytesRead = -1;
+ bytesRead = socket.read(inBuffer);
+ if (bytesRead < 0) {
+ throw new AsynchronousCloseException();
+ }
+
+ inBuffer.flip();
+ msgs = factory.parseMessages(inBuffer);
+ if (inBuffer.hasRemaining()) {
+ inBuffer.compact();
+ } else {
+ inBuffer.clear();
+ }
+ return msgs;
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.protocol_plugin.openflow.core.internal;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
+import org.openflow.protocol.OFMessage;
+
+/**
+ * This class describes an OpenFlow message with priority
+ */
+class PriorityMessage {
+ OFMessage msg;
+ int priority;
+
+ public PriorityMessage(OFMessage msg, int priority) {
+ this.msg = msg;
+ this.priority = priority;
+ }
+
+ public OFMessage getMsg() {
+ return msg;
+ }
+
+ public void setMsg(OFMessage msg) {
+ this.msg = msg;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ @Override
+ public int hashCode() {
+ return HashCodeBuilder.reflectionHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return EqualsBuilder.reflectionEquals(this, obj);
+ }
+
+ @Override
+ public String toString() {
+ return "PriorityMessage[" + ReflectionToStringBuilder.toString(this) + "]";
+ }
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.protocol_plugin.openflow.core.internal;
+
+import java.io.FileInputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.util.List;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageReadWrite;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.factory.BasicFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class implements methods to read/write messages over an established
+ * socket channel. The data exchange is encrypted/decrypted by SSLEngine.
+ */
+public class SecureMessageReadWriteService implements IMessageReadWrite {
+ private static final Logger logger = LoggerFactory
+ .getLogger(SecureMessageReadWriteService.class);
+
+ private Selector selector;
+ private SelectionKey clientSelectionKey;
+ private SocketChannel socket;
+ private BasicFactory factory;
+
+ private SSLEngine sslEngine;
+ private SSLEngineResult sslEngineResult; // results from sslEngine last operation
+ private ByteBuffer myAppData; // clear text message to be sent
+ private ByteBuffer myNetData; // encrypted message to be sent
+ private ByteBuffer peerAppData; // clear text message received from the switch
+ private ByteBuffer peerNetData; // encrypted message from the switch
+
+ public SecureMessageReadWriteService(SocketChannel socket, Selector selector) throws Exception {
+ this.socket = socket;
+ this.selector = selector;
+ this.factory = new BasicFactory();
+
+ createSecureChannel(socket);
+ createBuffers(sslEngine);
+ }
+
+ /**
+ * Bring up secure channel using SSL Engine
+ *
+ * @param socket TCP socket channel
+ * @throws Exception
+ */
+ private void createSecureChannel(SocketChannel socket) throws Exception {
+ String keyStoreFile = System.getProperty("controllerKeyStore");
+ String keyStorePassword = System.getProperty("controllerKeyStorePassword");
+ String trustStoreFile = System.getProperty("controllerTrustStore");;
+ String trustStorePassword = System.getProperty("controllerTrustStorePassword");;
+
+ KeyStore ks = KeyStore.getInstance("JKS");
+ KeyStore ts = KeyStore.getInstance("JKS");
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+ ks.load(new FileInputStream(keyStoreFile), keyStorePassword.toCharArray());
+ ts.load(new FileInputStream(trustStoreFile), trustStorePassword.toCharArray());
+ kmf.init(ks, keyStorePassword.toCharArray());
+ tmf.init(ts);
+
+ SecureRandom random = new SecureRandom();
+ random.nextInt();
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), random);
+ sslEngine = sslContext.createSSLEngine();
+ sslEngine.setUseClientMode(false);
+ sslEngine.setNeedClientAuth(true);
+
+ // Do initial handshake
+ doHandshake(socket, sslEngine);
+
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ);
+ }
+
+ /**
+ * Sends the OF message out over the socket channel. The message is
+ * encrypted by SSL Engine.
+ *
+ * @param msg OF message to be sent
+ * @throws Exception
+ */
+ @Override
+ public void asyncSend(OFMessage msg) throws Exception {
+ synchronized (myAppData) {
+ int msgLen = msg.getLengthU();
+ if (myAppData.remaining() < msgLen) {
+ // increase the buffer size so that it can contain this message
+ ByteBuffer newBuffer = ByteBuffer.allocateDirect(myAppData
+ .capacity()
+ + msgLen);
+ myAppData.flip();
+ newBuffer.put(myAppData);
+ myAppData = newBuffer;
+ }
+ msg.writeTo(myAppData);
+ myAppData.flip();
+ sslEngineResult = sslEngine.wrap(myAppData, myNetData);
+ logger.trace("asyncSend sslEngine wrap: {}", sslEngineResult);
+ runDelegatedTasks(sslEngineResult, sslEngine);
+
+ if (!socket.isOpen()) {
+ return;
+ }
+
+ myNetData.flip();
+ socket.write(myNetData);
+ if (myNetData.hasRemaining()) {
+ myNetData.compact();
+ } else {
+ myNetData.clear();
+ }
+
+ if (myAppData.hasRemaining()) {
+ myAppData.compact();
+ this.clientSelectionKey = this.socket.register(
+ this.selector, SelectionKey.OP_WRITE, this);
+ } else {
+ myAppData.clear();
+ this.clientSelectionKey = this.socket.register(
+ this.selector, SelectionKey.OP_READ, this);
+ }
+
+ logger.trace("Message sent: {}", msg.toString());
+ }
+ }
+
+ /**
+ * Resumes sending the remaining messages in the outgoing buffer
+ * @throws Exception
+ */
+ @Override
+ public void resumeSend() throws Exception {
+ synchronized (myAppData) {
+ myAppData.flip();
+ sslEngineResult = sslEngine.wrap(myAppData, myNetData);
+ logger.trace("resumeSend sslEngine wrap: {}", sslEngineResult);
+ runDelegatedTasks(sslEngineResult, sslEngine);
+
+ if (!socket.isOpen()) {
+ return;
+ }
+
+ myNetData.flip();
+ socket.write(myNetData);
+ if (myNetData.hasRemaining()) {
+ myNetData.compact();
+ } else {
+ myNetData.clear();
+ }
+
+ if (myAppData.hasRemaining()) {
+ myAppData.compact();
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_WRITE, this);
+ } else {
+ myAppData.clear();
+ this.clientSelectionKey = this.socket.register(this.selector,
+ SelectionKey.OP_READ, this);
+ }
+ }
+ }
+
+ /**
+ * Reads the incoming network data from the socket, decryptes them and then
+ * retrieves the OF messages.
+ *
+ * @return list of OF messages
+ * @throws Exception
+ */
+ @Override
+ public List<OFMessage> readMessages() throws Exception {
+ if (!socket.isOpen()) {
+ return null;
+ }
+
+ List<OFMessage> msgs = null;
+ int bytesRead = -1;
+ int countDown = 50;
+
+ bytesRead = socket.read(peerNetData);
+ if (bytesRead < 0) {
+ throw new AsynchronousCloseException();
+ }
+
+ do {
+ peerNetData.flip();
+ sslEngineResult = sslEngine.unwrap(peerNetData, peerAppData);
+ if (peerNetData.hasRemaining()) {
+ peerNetData.compact();
+ } else {
+ peerNetData.clear();
+ }
+ logger.trace("sslEngine unwrap result: {}", sslEngineResult);
+ runDelegatedTasks(sslEngineResult, sslEngine);
+ } while ((sslEngineResult.getStatus() == SSLEngineResult.Status.OK) &&
+ peerNetData.hasRemaining() && (--countDown > 0));
+
+ if (countDown == 0) {
+ logger.trace("countDown reaches 0. peerNetData pos {} lim {}", peerNetData.position(), peerNetData.limit());
+ }
+
+ peerAppData.flip();
+ msgs = factory.parseMessages(peerAppData);
+ if (peerAppData.hasRemaining()) {
+ peerAppData.compact();
+ } else {
+ peerAppData.clear();
+ }
+
+ this.clientSelectionKey = this.socket.register(
+ this.selector, SelectionKey.OP_READ, this);
+
+ return msgs;
+ }
+
+ /**
+ * If the result indicates that we have outstanding tasks to do,
+ * go ahead and run them in this thread.
+ */
+ private void runDelegatedTasks(SSLEngineResult result,
+ SSLEngine engine) throws Exception {
+
+ if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+ Runnable runnable;
+ while ((runnable = engine.getDelegatedTask()) != null) {
+ logger.debug("\trunning delegated task...");
+ runnable.run();
+ }
+ HandshakeStatus hsStatus = engine.getHandshakeStatus();
+ if (hsStatus == HandshakeStatus.NEED_TASK) {
+ throw new Exception(
+ "handshake shouldn't need additional tasks");
+ }
+ logger.debug("\tnew HandshakeStatus: {}", hsStatus);
+ }
+ }
+
+ private void doHandshake(SocketChannel socket, SSLEngine engine) throws Exception {
+ SSLSession session = engine.getSession();
+ ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
+ ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
+ ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
+ ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
+
+ // Begin handshake
+ engine.beginHandshake();
+ SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
+
+ // Process handshaking message
+ while (hs != SSLEngineResult.HandshakeStatus.FINISHED &&
+ hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
+ switch (hs) {
+ case NEED_UNWRAP:
+ // Receive handshaking data from peer
+ if (socket.read(peerNetData) < 0) {
+ throw new AsynchronousCloseException();
+ }
+
+ // Process incoming handshaking data
+ peerNetData.flip();
+ SSLEngineResult res = engine.unwrap(peerNetData, peerAppData);
+ peerNetData.compact();
+ hs = res.getHandshakeStatus();
+
+ // Check status
+ switch (res.getStatus()) {
+ case OK :
+ // Handle OK status
+ break;
+ }
+ break;
+
+ case NEED_WRAP :
+ // Empty the local network packet buffer.
+ myNetData.clear();
+
+ // Generate handshaking data
+ res = engine.wrap(myAppData, myNetData);
+ hs = res.getHandshakeStatus();
+
+ // Check status
+ switch (res.getStatus()) {
+ case OK :
+ myNetData.flip();
+
+ // Send the handshaking data to peer
+ while (myNetData.hasRemaining()) {
+ if (socket.write(myNetData) < 0) {
+ throw new AsynchronousCloseException();
+ }
+ }
+ break;
+ }
+ break;
+
+ case NEED_TASK :
+ // Handle blocking tasks
+ Runnable runnable;
+ while ((runnable = engine.getDelegatedTask()) != null) {
+ logger.debug("\trunning delegated task...");
+ runnable.run();
+ }
+ hs = engine.getHandshakeStatus();
+ if (hs == HandshakeStatus.NEED_TASK) {
+ throw new Exception(
+ "handshake shouldn't need additional tasks");
+ }
+ logger.debug("\tnew HandshakeStatus: {}", hs);
+ break;
+ }
+ }
+ }
+
+ private void createBuffers(SSLEngine engine) {
+ SSLSession session = engine.getSession();
+ this.myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
+ this.peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
+ this.myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
+ this.peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
+ }
+}
package org.opendaylight.controller.protocol_plugin.openflow.core.internal;
-import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
+import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageReadWrite;
import org.openflow.protocol.OFBarrierReply;
import org.openflow.protocol.OFEchoReply;
import org.openflow.protocol.OFError;
private static final int SWITCH_LIVENESS_TIMER = 5000;
private static final int SWITCH_LIVENESS_TIMEOUT = 2 * SWITCH_LIVENESS_TIMER + 500;
private int MESSAGE_RESPONSE_TIMER = 2000;
- private static final int bufferSize = 1024 * 1024;
private String instanceName;
private ISwitch thisISwitch;
private Byte tables;
private Integer actions;
private Selector selector;
- private SelectionKey clientSelectionKey;
private SocketChannel socket;
- private ByteBuffer inBuffer;
- private ByteBuffer outBuffer;
private BasicFactory factory;
private AtomicInteger xid;
private SwitchState state;
private ExecutorService executor;
private ConcurrentHashMap<Integer, Callable<Object>> messageWaitingDone;
private boolean running;
+ private IMessageReadWrite msgReadWriteService;
private Thread switchHandlerThread;
private Integer responseTimerValue;
-
+ private PriorityBlockingQueue<PriorityMessage> transmitQ;
+ private Thread transmitThread;
+
private enum SwitchState {
NON_OPERATIONAL(0), WAIT_FEATURES_REPLY(1), WAIT_CONFIG_REPLY(2), OPERATIONAL(
3);
this.periodicTimer = null;
this.executor = Executors.newFixedThreadPool(4);
this.messageWaitingDone = new ConcurrentHashMap<Integer, Callable<Object>>();
- this.inBuffer = ByteBuffer.allocateDirect(bufferSize);
- this.outBuffer = ByteBuffer.allocateDirect(bufferSize);
this.responseTimerValue = MESSAGE_RESPONSE_TIMER;
String rTimer = System.getProperty("of.messageResponseTimer");
if (rTimer != null) {
- try {
- responseTimerValue = Integer.decode(rTimer);
- } catch (NumberFormatException e) {
- logger.warn("Invalid of.messageResponseTimer:" + rTimer + ", use default("
- + MESSAGE_RESPONSE_TIMER+ ")");
- }
+ try {
+ responseTimerValue = Integer.decode(rTimer);
+ } catch (NumberFormatException e) {
+ logger.warn("Invalid of.messageResponseTimer: {} use default({})",
+ rTimer, MESSAGE_RESPONSE_TIMER);
+ }
}
- }
+ }
public void start() {
try {
- this.selector = SelectorProvider.provider().openSelector();
- this.socket.configureBlocking(false);
- this.socket.socket().setTcpNoDelay(true);
- this.clientSelectionKey = this.socket.register(this.selector,
- SelectionKey.OP_READ);
+ startTransmitThread();
+ setupCommChannel();
+ sendFirstHello();
startHandlerThread();
} catch (Exception e) {
reportError(e);
- return;
}
}
private void startHandlerThread() {
- OFMessage msg = factory.getMessage(OFType.HELLO);
- asyncSend(msg);
switchHandlerThread = new Thread(new Runnable() {
@Override
public void run() {
running = true;
while (running) {
try {
- // wait for an incoming connection
+ // wait for an incoming connection
selector.select(0);
Iterator<SelectionKey> selectedKeys = selector
.selectedKeys().iterator();
running = false;
selector.wakeup();
cancelSwitchTimer();
- this.clientSelectionKey.cancel();
+ this.selector.close();
this.socket.close();
executor.shutdown();
} catch (Exception e) {
return this.xid.incrementAndGet();
}
+ /**
+ * This method puts the message in an outgoing priority queue with normal
+ * priority. It will be served after high priority messages. The method
+ * should be used for non-critical messages such as statistics request,
+ * discovery packets, etc. An unique XID is generated automatically and
+ * inserted into the message.
+ *
+ * @param msg The OF message to be sent
+ * @return The XID used
+ */
@Override
public Integer asyncSend(OFMessage msg) {
- return asyncSend(msg, getNextXid());
+ return asyncSend(msg, getNextXid());
+ }
+
+ /**
+ * This method puts the message in an outgoing priority queue with normal
+ * priority. It will be served after high priority messages. The method
+ * should be used for non-critical messages such as statistics request,
+ * discovery packets, etc. The specified XID is inserted into the message.
+ *
+ * @param msg The OF message to be Sent
+ * @param xid The XID to be used in the message
+ * @return The XID used
+ */
+ @Override
+ public Integer asyncSend(OFMessage msg, int xid) {
+ msg.setXid(xid);
+ transmitQ.add(new PriorityMessage(msg, 0));
+ return xid;
}
+ /**
+ * This method puts the message in an outgoing priority queue with high
+ * priority. It will be served first before normal priority messages. The
+ * method should be used for critical messages such as hello, echo reply
+ * etc. An unique XID is generated automatically and inserted into the
+ * message.
+ *
+ * @param msg The OF message to be sent
+ * @return The XID used
+ */
@Override
- public Integer asyncSend(OFMessage msg, int xid) {
- synchronized (outBuffer) {
- /*
- if ((msg.getType() != OFType.ECHO_REQUEST) &&
- (msg.getType() != OFType.ECHO_REPLY)) {
- logger.debug("sending " + msg.getType().toString() + " to " + toString());
- }
- */
- msg.setXid(xid);
- int msgLen = msg.getLengthU();
- if (outBuffer.remaining() < msgLen) {
- // increase the buffer size so that it can contain this message
- ByteBuffer newBuffer = ByteBuffer.allocateDirect(outBuffer
- .capacity()
- + msgLen);
- outBuffer.flip();
- newBuffer.put(outBuffer);
- outBuffer = newBuffer;
- }
- msg.writeTo(outBuffer);
- outBuffer.flip();
- try {
- socket.write(outBuffer);
- outBuffer.compact();
- if (outBuffer.position() > 0) {
- this.clientSelectionKey = this.socket.register(
- this.selector, SelectionKey.OP_WRITE, this);
- }
- logger.trace("Message sent: " + msg.toString());
- } catch (Exception e) {
- reportError(e);
- }
- }
+ public Integer asyncFastSend(OFMessage msg) {
+ return asyncFastSend(msg, getNextXid());
+ }
+
+ /**
+ * This method puts the message in an outgoing priority queue with high
+ * priority. It will be served first before normal priority messages. The
+ * method should be used for critical messages such as hello, echo reply
+ * etc. The specified XID is inserted into the message.
+ *
+ * @param msg The OF message to be sent
+ * @return The XID used
+ */
+ @Override
+ public Integer asyncFastSend(OFMessage msg, int xid) {
+ msg.setXid(xid);
+ transmitQ.add(new PriorityMessage(msg, 1));
return xid;
}
- public void resumeSend() {
- synchronized (outBuffer) {
- try {
- outBuffer.flip();
- socket.write(outBuffer);
- outBuffer.compact();
- if (outBuffer.position() > 0) {
- this.clientSelectionKey = this.socket.register(
- this.selector, SelectionKey.OP_WRITE, this);
- } else {
- this.clientSelectionKey = this.socket.register(
- this.selector, SelectionKey.OP_READ, this);
- }
- } catch (Exception e) {
- reportError(e);
- }
- }
+ public void resumeSend() {
+ try {
+ msgReadWriteService.resumeSend();
+ } catch (Exception e) {
+ reportError(e);
+ }
}
public void handleMessages() {
- List<OFMessage> msgs = readMessages();
+ List<OFMessage> msgs = null;
+
+ try {
+ msgs = msgReadWriteService.readMessages();
+ } catch (Exception e) {
+ reportError(e);
+ }
+
if (msgs == null) {
- logger.debug(toString() + " is down");
+ logger.debug("{} is down", toString());
// the connection is down, inform core
reportSwitchStateChange(false);
return;
}
for (OFMessage msg : msgs) {
- logger.trace("Message received: " + msg.toString());
+ logger.trace("Message received: {}", msg.toString());
/*
if ((msg.getType() != OFType.ECHO_REQUEST) &&
(msg.getType() != OFType.ECHO_REPLY)) {
// send feature request
OFMessage featureRequest = factory
.getMessage(OFType.FEATURES_REQUEST);
- asyncSend(featureRequest);
+ asyncFastSend(featureRequest);
// delete all pre-existing flows
OFMatch match = new OFMatch().setWildcards(OFMatch.OFPFW_ALL);
OFFlowMod flowMod = (OFFlowMod) factory
flowMod.setMatch(match).setCommand(OFFlowMod.OFPFC_DELETE)
.setOutPort(OFPort.OFPP_NONE).setLength(
(short) OFFlowMod.MINIMUM_LENGTH);
- asyncSend(flowMod);
+ asyncFastSend(flowMod);
this.state = SwitchState.WAIT_FEATURES_REPLY;
startSwitchTimer();
break;
case ECHO_REQUEST:
OFEchoReply echoReply = (OFEchoReply) factory
.getMessage(OFType.ECHO_REPLY);
- asyncSend(echoReply);
+ asyncFastSend(echoReply);
break;
case ECHO_REPLY:
this.probeSent = false;
}
- private List<OFMessage> readMessages() {
- List<OFMessage> msgs = null;
- int bytesRead;
- try {
- bytesRead = socket.read(inBuffer);
- } catch (Exception e) {
- reportError(e);
- return null;
- }
- if (bytesRead == -1) {
- return null;
- }
- inBuffer.flip();
- msgs = factory.parseMessages(inBuffer);
- if (inBuffer.hasRemaining()) {
- inBuffer.compact();
- } else {
- inBuffer.clear();
- }
- return msgs;
- }
-
private void startSwitchTimer() {
this.periodicTimer = new Timer();
this.periodicTimer.scheduleAtFixedRate(new TimerTask() {
if ((now - lastMsgReceivedTimeStamp) > SWITCH_LIVENESS_TIMEOUT) {
if (probeSent) {
// switch failed to respond to our probe, consider it down
- logger.warn(toString()
- + " is idle for too long, disconnect");
+ logger.warn("{} is idle for too long, disconnect", toString());
reportSwitchStateChange(false);
} else {
// send a probe to see if the switch is still alive
probeSent = true;
OFMessage echo = factory
.getMessage(OFType.ECHO_REQUEST);
- asyncSend(echo);
+ asyncFastSend(echo);
}
} else {
if (state == SwitchState.WAIT_FEATURES_REPLY) {
// send another features request
OFMessage request = factory
.getMessage(OFType.FEATURES_REQUEST);
- asyncSend(request);
+ asyncFastSend(request);
} else {
if (state == SwitchState.WAIT_CONFIG_REPLY) {
// send another config request
.getMessage(OFType.SET_CONFIG);
config.setMissSendLength((short) 0xffff)
.setLengthU(OFSetConfig.MINIMUM_LENGTH);
- asyncSend(config);
+ asyncFastSend(config);
OFMessage getConfig = factory
.getMessage(OFType.GET_CONFIG_REQUEST);
- asyncSend(getConfig);
+ asyncFastSend(getConfig);
}
}
}
}
private void reportError(Exception e) {
- //logger.error(toString() + " caught Error " + e.toString());
- // notify core of this error event
+ logger.debug("Caught exception ", e);
+ // notify core of this error event and disconnect the switch
((Controller) core).takeSwitchEventError(this);
}
.getMessage(OFType.SET_CONFIG);
config.setMissSendLength((short) 0xffff).setLengthU(
OFSetConfig.MINIMUM_LENGTH);
- asyncSend(config);
+ asyncFastSend(config);
// send config request to make sure the switch can handle the set config
OFMessage getConfig = factory.getMessage(OFType.GET_CONFIG_REQUEST);
- asyncSend(getConfig);
+ asyncFastSend(getConfig);
this.state = SwitchState.WAIT_CONFIG_REPLY;
// inform core that a new switch is now operational
reportSwitchStateChange(true);
.get(MESSAGE_RESPONSE_TIMER, TimeUnit.MILLISECONDS);
return result;
} catch (Exception e) {
- logger.warn("Timeout while waiting for " + req.getType()
- + " replies");
+ logger.warn("Timeout while waiting for {} replies", req.getType());
result = null; // to indicate timeout has occurred
return result;
}
} else {
// if result is not null, this means the switch can't handle this message
// the result if OFError already
- logger.debug("Send " + msg.getType().toString()
- + " failed --> " + ((OFError) result).toString());
+ logger.debug("Send {} failed --> {}",
+ msg.getType().toString(), ((OFError) result).toString());
}
return result;
} catch (Exception e) {
- logger.warn("Timeout while waiting for " + msg.getType().toString()
- + " reply");
+ logger.warn("Timeout while waiting for {} reply", msg.getType().toString());
// convert the result into a Boolean with value false
status = false;
result = status;
worker.wakeup();
}
}
-
+
@Override
public Map<Short, OFPhysicalPort> getPhysicalPorts() {
return this.physicalPorts;
}
return result;
}
+
+ /*
+ * Transmit thread polls the message out of the priority queue and invokes
+ * messaging service to transmit it over the socket channel
+ */
+ class PriorityMessageTransmit implements Runnable {
+ public void run() {
+ while (true) {
+ try {
+ if (!transmitQ.isEmpty()) {
+ PriorityMessage pmsg = transmitQ.poll();
+ msgReadWriteService.asyncSend(pmsg.msg);
+ logger.trace("Message sent: {}", pmsg.toString());
+ }
+ Thread.sleep(10);
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+ }
+ }
+
+ /*
+ * Setup and start the transmit thread
+ */
+ private void startTransmitThread() {
+ this.transmitQ = new PriorityBlockingQueue<PriorityMessage>(11,
+ new Comparator<PriorityMessage>() {
+ public int compare(PriorityMessage p1, PriorityMessage p2) {
+ return p2.priority - p1.priority;
+ }
+ });
+ this.transmitThread = new Thread(new PriorityMessageTransmit());
+ this.transmitThread.start();
+ }
+
+ /*
+ * Setup communication services
+ */
+ private void setupCommChannel() throws Exception {
+ this.selector = SelectorProvider.provider().openSelector();
+ this.socket.configureBlocking(false);
+ this.socket.socket().setTcpNoDelay(true);
+ this.msgReadWriteService = getMessageReadWriteService();
+ }
+
+ private void sendFirstHello() {
+ try {
+ OFMessage msg = factory.getMessage(OFType.HELLO);
+ asyncFastSend(msg);
+ } catch (Exception e) {
+ reportError(e);
+ }
+ }
+
+ private IMessageReadWrite getMessageReadWriteService() throws Exception {
+ String str = System.getProperty("secureChannelEnabled");
+ return ((str != null) && (str.equalsIgnoreCase("true"))) ?
+ new SecureMessageReadWriteService(socket, selector) :
+ new MessageReadWriteService(socket, selector);
+ }
}
// provider of service exists
c.add(createContainerServiceDependency(containerName).setService(
IListenRoutingUpdates.class).setCallbacks(
- "setLIstenRoutingUpdates", "unsetLIstenRoutingUpdates")
+ "setListenRoutingUpdates", "unsetListenRoutingUpdates")
.setRequired(false));
c.add(createContainerServiceDependency(containerName).setService(
}
}
- public void unsetRoutingUpdates(IListenRoutingUpdates i) {
+ public void unsetListenRoutingUpdates(IListenRoutingUpdates i) {
if (this.routingAware == null) {
return;
}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
- activate="factoryStartUp" deactivate="factoryShutDown"
- factory="routing.dijkstra_implementation.factory" name="routing.dijkstra_implementation.ComponentFactory">
- <implementation class="org.opendaylight.controller.routing.dijkstra_implementation.internal.DijkstraImplementation"/>
- <service>
- <provide interface="org.osgi.service.component.ComponentFactory"/>
- </service>
-</scr:component>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
- activate="startUp"
- deactivate="shutDown"
- name="routing.dijkstra_implementation.Component">
- <implementation class="org.opendaylight.controller.routing.dijkstra_implementation.internal.DijkstraImplementation"/>
-
- <service>
- <provide interface="org.opendaylight.controller.sal.routing.IRouting"/>
- <provide interface="org.opendaylight.controller.topologymanager.ITopologyManagerAware"/>
- </service>
- <reference name="IListenRoutingUpdates"
- bind="setListenRoutingUpdates"
- unbind="unsetRoutingUpdates"
- cardinality="0..n"
- policy="dynamic"
- interface="org.opendaylight.controller.sal.routing.IListenRoutingUpdates"/>
-</scr:component>
<module>binding-generator-util</module>\r
<module>binding-generator-impl</module>\r
<module>binding-java-api-generator</module>\r
+ <module>yang-to-sources</module>\r
+ <module>yang-to-sources-plugin</module>\r
+ <module>yang-to-sources-plugin-it</module>\r
</modules>\r
<dependencies>\r
\r
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>yang-to-sources-plugin-it</artifactId>
+
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven.shared</groupId>
+ <artifactId>maven-verifier</artifactId>
+ <version>1.4</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin.it;
+
+import org.apache.maven.it.VerificationException;
+import org.apache.maven.it.Verifier;
+import org.junit.Test;
+
+public class CombineTest {
+
+ @Test
+ public void testCorrect() throws VerificationException {
+ Verifier v = YangToSourcesPluginTest.setUp("Correct_combined/", false);
+ YangToResourcesPluginTest.verifyCorrectLog(v);
+ YangToSourcesPluginTest.verifyCorrectLog(v);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin.it;
+
+import org.apache.maven.it.VerificationException;
+import org.apache.maven.it.Verifier;
+import org.junit.Test;
+
+public class YangToResourcesPluginTest {
+
+ @Test
+ public void testCorrect() throws VerificationException {
+ Verifier v = YangToSourcesPluginTest.setUp("Correct_resources/", false);
+ verifyCorrectLog(v);
+ }
+
+ static void verifyCorrectLog(Verifier v) throws VerificationException {
+ v.verifyErrorFreeLog();
+ v.verifyTextInLog("[INFO] yang-to-resources: Resource provider instantiated from org.opendaylight.controller.yang2sources.spi.ResourceProviderTestImpl");
+ v.verifyTextInLog("[INFO] yang-to-resources: Resource provider org.opendaylight.controller.yang2sources.spi.ResourceProviderTestImpl call successful");
+ }
+
+ @Test
+ public void testNoGenerators() throws VerificationException {
+ Verifier v = YangToSourcesPluginTest.setUp("NoGenerators_resources/",
+ false);
+ v.verifyErrorFreeLog();
+ v.verifyTextInLog("[WARNING] yang-to-resources: No resource provider classes provided");
+ }
+
+ @Test
+ public void testUnknownGenerator() throws VerificationException {
+ Verifier v = YangToSourcesPluginTest.setUp(
+ "UnknownGenerator_resources/", true);
+ v.verifyTextInLog("[ERROR] yang-to-resources: Unable to provide resources with unknown resource provider");
+ v.verifyTextInLog("java.lang.ClassNotFoundException: unknown");
+ v.verifyTextInLog("[INFO] yang-to-resources: Resource provider instantiated from org.opendaylight.controller.yang2sources.spi.ResourceProviderTestImpl");
+ v.verifyTextInLog("[INFO] yang-to-resources: Resource provider org.opendaylight.controller.yang2sources.spi.ResourceProviderTestImpl call successful");
+ v.verifyTextInLog("[ERROR] yang-to-resources: One or more code resource provider failed, including failed list(resourceProviderClass=exception) {unknown=java.lang.ClassNotFoundException}");
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin.it;
+
+import static org.junit.Assert.*;
+import static org.junit.matchers.JUnitMatchers.*;
+
+import java.io.File;
+
+import org.apache.maven.it.VerificationException;
+import org.apache.maven.it.Verifier;
+import org.junit.Test;
+
+public class YangToSourcesPluginTest {
+
+ @Test
+ public void testYangRootNotExist() {
+ try {
+ setUp("YangRootNotExist/", false);
+ } catch (VerificationException e) {
+ assertVerificationException(e,
+ "[ERROR] yang-to-sources: Unable to parse yang files from unknown");
+ assertVerificationException(
+ e,
+ "Caused by: org.apache.maven.plugin.MojoExecutionException: yang-to-sources: Unable to parse yang files from unknown");
+ return;
+ }
+
+ fail("Verification exception should have been thrown");
+ }
+
+ @Test
+ public void testCorrect() throws VerificationException {
+ Verifier v = setUp("Correct/", false);
+ verifyCorrectLog(v);
+ }
+
+ static void verifyCorrectLog(Verifier v) throws VerificationException {
+ v.verifyErrorFreeLog();
+ v.verifyTextInLog("[INFO] yang-to-sources: yang files parsed from");
+ v.verifyTextInLog("[INFO] yang-to-sources: Code generator instantiated from org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl");
+ v.verifyTextInLog("[INFO] yang-to-sources: Sources generated by org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl: null");
+ }
+
+ @Test
+ public void testNoGenerators() throws VerificationException {
+ Verifier v = setUp("NoGenerators/", false);
+ v.verifyErrorFreeLog();
+ v.verifyTextInLog("[WARNING] yang-to-sources: No code generators provided");
+ }
+
+ @Test
+ public void testUnknownGenerator() throws VerificationException {
+ Verifier v = setUp("UnknownGenerator/", true);
+ v.verifyTextInLog("[ERROR] yang-to-sources: Unable to generate sources with unknown generator");
+ v.verifyTextInLog("java.lang.ClassNotFoundException: unknown");
+ v.verifyTextInLog("[INFO] yang-to-sources: Code generator instantiated from org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl");
+ v.verifyTextInLog("[INFO] yang-to-sources: Sources generated by org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl: null");
+ v.verifyTextInLog("[ERROR] yang-to-sources: One or more code generators failed, including failed list(generatorClass=exception) {unknown=java.lang.ClassNotFoundException}");
+ }
+
+ @Test
+ public void testNoYangFiles() throws VerificationException {
+ Verifier v = setUp("NoYangFiles/", false);
+ v.verifyTextInLog("[WARNING] yang-to-sources: No yang file found in ");
+ v.verifyTextInLog("[INFO] yang-to-sources: yang files parsed from []");
+ v.verifyTextInLog("[INFO] yang-to-sources: Code generator instantiated from org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl");
+ v.verifyTextInLog("[INFO] yang-to-sources: Sources generated by org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl: null");
+ }
+
+ static void assertVerificationException(VerificationException e,
+ String string) {
+ assertThat(e.getMessage(), containsString(string));
+ }
+
+ static Verifier setUp(String project, boolean ignoreF)
+ throws VerificationException {
+ Verifier verifier = new Verifier(new File("src/test/resources/"
+ + project).getAbsolutePath());
+ if (ignoreF)
+ verifier.addCliOption("-fn");
+ verifier.executeGoal("generate-resources");
+ return verifier;
+ }
+
+}
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>${basedir}</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ outDir/
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources</artifactId>
+ <version>1.0</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+module types1 {
+ yang-version 1;
+ namespace "urn:simple.container.demo";
+ prefix "t1";
+
+ import types2 {
+ prefix "data";
+ revision-date 2013-02-27;
+ }
+
+ organization "Cisco";
+ contact "WILL-BE-DEFINED-LATER";
+
+ revision "2013-02-27" {
+ reference " WILL BE DEFINED LATER";
+ }
+
+ container interfaces {
+ list ifEntry {
+ key "ifIndex";
+
+ leaf ifIndex {
+ type uint32;
+ units minutes;
+ }
+
+ leaf ifMtu {
+ type int32;
+ }
+ }
+ }
+
+ leaf testleaf {
+ type data:my-base-int32-type {
+ range "min..max";
+ }
+ }
+
+ leaf test-string-leaf {
+ type data:my-string-type-ext;
+ }
+
+ leaf test-int-leaf {
+ type data:my-int-type-ext;
+ }
+
+ leaf test-decimal-leaf {
+ type data:my-decimal-type {
+ fraction-digits 4;
+ }
+ }
+
+ leaf test-decimal-leaf2 {
+ type data:my-decimal-type-ext;
+ }
+
+}
--- /dev/null
+module types2 {
+ yang-version 1;
+ namespace "urn:simple.types.data.demo";
+ prefix "t2";
+
+ import types1 {
+ prefix "if";
+ revision-date 2013-02-27;
+ }
+
+ organization "Cisco";
+ contact "WILL-BE-DEFINED-LATER";
+ description "This is types-data test description";
+
+ revision "2013-02-27" {
+ reference " WILL BE DEFINED LATER";
+ }
+
+ typedef my-base-int32-type {
+ type int32 {
+ range "2..20";
+ }
+ }
+
+ typedef my-type1 {
+ type my-base-int32-type {
+ range "11..max";
+ }
+ }
+
+ typedef my-string-type {
+ type string {
+ pattern "[a-k]*";
+ }
+ }
+
+ typedef my-string-type2 {
+ type my-string-type {
+ pattern "[b-u]*";
+ }
+ }
+
+ typedef my-string-type-ext {
+ type my-string-type2 {
+ pattern "[e-z]*";
+ }
+ }
+
+ typedef my-int-type {
+ type int32 {
+ range "10..20";
+ }
+ }
+
+ typedef my-int-type2 {
+ type my-int-type {
+ range "12..18";
+ }
+ }
+
+ typedef my-int-type-ext {
+ type my-int-type2 {
+ range "14..16";
+ }
+ }
+
+ typedef my-decimal-type {
+ type decimal64 {
+ fraction-digits 6;
+ }
+ }
+
+ typedef my-decimal-type-ext {
+ type decimal64 {
+ fraction-digits 5;
+ }
+ }
+
+ augment "/if:interfaces/if:ifEntry" {
+ when "if:ifType='ds0'";
+ leaf ds0ChannelNumber {
+ type string;
+ }
+ }
+
+ leaf if-name {
+ type leafref {
+ path "/interface/name";
+ }
+ }
+
+ leaf name {
+ type string;
+ }
+
+ leaf nested-type-leaf {
+ type my-type1;
+ }
+
+}
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ <goal>generate-resources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>${basedir}/../Correct</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ outDir/src
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <resourceProviders>
+ <provider>
+ <resourceProviderClass>
+ org.opendaylight.controller.yang2sources.spi.ResourceProviderTestImpl
+ </resourceProviderClass>
+ <outputBaseDir>
+ outDir/resources
+ </outputBaseDir>
+ </provider>
+ </resourceProviders>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources</artifactId>
+ <version>1.0</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-resources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>${basedir}/../Correct</yangFilesRootDir>
+ <resourceProviders>
+ <provider>
+ <resourceProviderClass>
+ org.opendaylight.controller.yang2sources.spi.ResourceProviderTestImpl
+ </resourceProviderClass>
+ <outputBaseDir>
+ outDir/
+ </outputBaseDir>
+ </provider>
+ </resourceProviders>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources</artifactId>
+ <version>1.0</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>${basedir}/../Correct</yangFilesRootDir>
+ <codeGenerators>
+ </codeGenerators>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-resources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>${basedir}/../Correct</yangFilesRootDir>
+ <resourceProviders>
+ </resourceProviders>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>${basedir}</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ /outDir/
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources</artifactId>
+ <version>1.0</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>${basedir}/../Correct</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ /outDir/
+ </outputBaseDir>
+ </generator>
+ <generator>
+ <codeGeneratorClass>
+ unknown
+ </codeGeneratorClass>
+ <outputBaseDir>
+ /outDir/
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources</artifactId>
+ <version>1.0</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-resources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>${basedir}/../Correct</yangFilesRootDir>
+ <resourceProviders>
+ <provider>
+ <resourceProviderClass>
+ org.opendaylight.controller.yang2sources.spi.ResourceProviderTestImpl
+ </resourceProviderClass>
+ <outputBaseDir>
+ outDir/
+ </outputBaseDir>
+ </provider>
+ <provider>
+ <resourceProviderClass>
+ unknown
+ </resourceProviderClass>
+ <outputBaseDir>
+ outDir/
+ </outputBaseDir>
+ </provider>
+ </resourceProviders>
+ </configuration>
+ </execution>
+ </executions>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources</artifactId>
+ <version>1.0</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>test</artifactId>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <yangFilesRootDir>unknown</yangFilesRootDir>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>
+ org.opendaylight.controller.yang2sources.spi.CodeGeneratorTestImpl
+ </codeGeneratorClass>
+ <outputBaseDir>
+ /outDir/
+ </outputBaseDir>
+ </generator>
+ </codeGenerators>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+
+ <artifactId>yang-to-sources-plugin</artifactId>
+ <packaging>maven-plugin</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>3.0.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven.plugin-tools</groupId>
+ <artifactId>maven-plugin-annotations</artifactId>
+ <version>3.2</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-model-parser-impl</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-to-sources</artifactId>
+ <version>1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>yang-to-sources</artifactId>
+ <version>1.0</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.8.4</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-plugin-plugin</artifactId>
+ <version>3.2</version>
+ <configuration>
+ <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
+ </configuration>
+ <executions>
+ <execution>
+ <id>mojo-descriptor</id>
+ <goals>
+ <goal>descriptor</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin;
+
+import java.io.File;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Complex configuration arguments
+ */
+public abstract class ConfigArg {
+
+ protected File outputBaseDir;
+
+ public ConfigArg(File outputBaseDir) {
+ this.outputBaseDir = outputBaseDir;
+ }
+
+ public ConfigArg() {
+ }
+
+ public File getOutputBaseDir() {
+ return outputBaseDir;
+ }
+
+ public abstract void check();
+
+ public static final class ResourceProviderArg extends ConfigArg {
+ private String resourceProviderClass;
+
+ public ResourceProviderArg() {
+ }
+
+ public ResourceProviderArg(String resourceProviderClass,
+ File outputBaseDir) {
+ super(outputBaseDir);
+ this.resourceProviderClass = resourceProviderClass;
+ }
+
+ @Override
+ public void check() {
+ Preconditions
+ .checkNotNull(resourceProviderClass,
+ "resourceProviderClass for ResourceProvider cannot be null");
+ Preconditions.checkNotNull(outputBaseDir,
+ "outputBaseDir for ResourceProvider cannot be null, "
+ + resourceProviderClass);
+ }
+
+ public String getResourceProviderClass() {
+ return resourceProviderClass;
+ }
+ }
+
+ /**
+ * Transfer object for code generator class and output directory.
+ */
+ public static final class CodeGeneratorArg extends ConfigArg {
+ private String codeGeneratorClass;
+
+ public CodeGeneratorArg() {
+ }
+
+ public CodeGeneratorArg(String codeGeneratorClass, File outputBaseDir) {
+ super(outputBaseDir);
+ this.codeGeneratorClass = codeGeneratorClass;
+ }
+
+ @Override
+ public void check() {
+ Preconditions.checkNotNull(codeGeneratorClass,
+ "codeGeneratorClass for CodeGenerator cannot be null");
+ Preconditions.checkNotNull(outputBaseDir,
+ "outputBaseDir for CodeGenerator cannot be null, "
+ + codeGeneratorClass);
+ }
+
+ public String getCodeGeneratorClass() {
+ return codeGeneratorClass;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Maps;
+
+final class Util {
+
+ static final String YANG_SUFFIX = "yang";
+
+ // Cache for listed directories and found yang files. Typically yang files
+ // are utilized twice. First: code is generated during generate-sources
+ // phase Second: yang files are copied as resources during
+ // generate-resources phase. This cache ensures that yang files are listed
+ // only once.
+ private static Map<String, Collection<File>> cache = Maps
+ .newHashMapWithExpectedSize(10);
+
+ /**
+ * List files recursively and return as array of String paths. Use cache of
+ * size 1.
+ */
+ static Collection<File> listFiles(String rootDir) {
+
+ if (cache.get(rootDir) != null)
+ return cache.get(rootDir);
+
+ Collection<File> yangFiles = FileUtils.listFiles(new File(rootDir),
+ new String[] { YANG_SUFFIX }, true);
+
+ toCache(rootDir, yangFiles);
+ return yangFiles;
+ }
+
+ static String[] listFilesAsArrayOfPaths(String rootDir) {
+ String[] filesArray = new String[] {};
+ Collection<File> yangFiles = listFiles(rootDir);
+
+ // If collection is empty, return empty array [] rather then [null]
+ // array, that is created by default
+ return yangFiles.isEmpty() ? filesArray : Collections2.transform(
+ yangFiles, new Function<File, String>() {
+
+ @Override
+ public String apply(File input) {
+ return input.getPath();
+ }
+ }).toArray(filesArray);
+ }
+
+ private static void toCache(final String rootDir,
+ final Collection<File> yangFiles) {
+ cache.put(rootDir, yangFiles);
+ }
+
+ /**
+ * Instantiate object from fully qualified class name
+ */
+ static <T> T getInstance(String codeGeneratorClass, Class<T> baseType)
+ throws ClassNotFoundException, InstantiationException,
+ IllegalAccessException {
+ return baseType.cast(resolveClass(codeGeneratorClass, baseType)
+ .newInstance());
+ }
+
+ private static Class<?> resolveClass(String codeGeneratorClass,
+ Class<?> baseType) throws ClassNotFoundException {
+ Class<?> clazz = Class.forName(codeGeneratorClass);
+
+ if (!isImplemented(baseType, clazz))
+ throw new IllegalArgumentException("Code generator " + clazz
+ + " has to implement " + baseType);
+ return clazz;
+ }
+
+ private static boolean isImplemented(Class<?> expectedIface,
+ Class<?> byClazz) {
+ for (Class<?> iface : byClazz.getInterfaces()) {
+ if (iface.equals(expectedIface))
+ return true;
+ }
+ return false;
+ }
+
+ static String message(String message, String logPrefix, Object... args) {
+ String innerMessage = String.format(message, args);
+ return String.format("%s %s", logPrefix, innerMessage);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.opendaylight.controller.yang2sources.plugin.ConfigArg.ResourceProviderArg;
+import org.opendaylight.controller.yang2sources.spi.ResourceGenerator;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+
+@Mojo(name = "generate-resources", defaultPhase = LifecyclePhase.GENERATE_RESOURCES)
+public final class YangToResourcesMojo extends AbstractMojo {
+
+ private static final String LOG_PREFIX = "yang-to-resources:";
+
+ @Parameter(required = true)
+ private ResourceProviderArg[] resourceProviders;
+
+ @Parameter(required = true)
+ private String yangFilesRootDir;
+
+ @VisibleForTesting
+ YangToResourcesMojo(ResourceProviderArg[] resourceProviderArgs,
+ String yangFilesRootDir) {
+ super();
+ this.resourceProviders = resourceProviderArgs;
+ this.yangFilesRootDir = yangFilesRootDir;
+ }
+
+ public YangToResourcesMojo() {
+ super();
+ }
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+
+ if (resourceProviders.length == 0) {
+ getLog().warn(
+ Util.message("No resource provider classes provided",
+ LOG_PREFIX));
+ return;
+ }
+
+ Map<String, String> thrown = Maps.newHashMap();
+ Collection<File> yangFiles = Util.listFiles(yangFilesRootDir);
+
+ for (ResourceProviderArg resourceProvider : resourceProviders) {
+ try {
+
+ provideResourcesWithOneProvider(yangFiles, resourceProvider);
+
+ } catch (Exception e) {
+ // try other generators, exception will be thrown after
+ getLog().error(
+ Util.message(
+ "Unable to provide resources with %s resource provider",
+ LOG_PREFIX,
+ resourceProvider.getResourceProviderClass()), e);
+ thrown.put(resourceProvider.getResourceProviderClass(), e
+ .getClass().getCanonicalName());
+ }
+ }
+
+ if (!thrown.isEmpty()) {
+ String message = Util
+ .message(
+ "One or more code resource provider failed, including failed list(resourceProviderClass=exception) %s",
+ LOG_PREFIX, thrown.toString());
+ getLog().error(message);
+ throw new MojoFailureException(message);
+ }
+ }
+
+ /**
+ * Instantiate provider from class and call required method
+ */
+ private void provideResourcesWithOneProvider(Collection<File> yangFiles,
+ ResourceProviderArg resourceProvider)
+ throws ClassNotFoundException, InstantiationException,
+ IllegalAccessException {
+
+ resourceProvider.check();
+
+ ResourceGenerator g = Util.getInstance(
+ resourceProvider.getResourceProviderClass(),
+ ResourceGenerator.class);
+ getLog().info(
+ Util.message("Resource provider instantiated from %s",
+ LOG_PREFIX, resourceProvider.getResourceProviderClass()));
+
+ g.generateResourceFiles(yangFiles, resourceProvider.getOutputBaseDir());
+ getLog().info(
+ Util.message("Resource provider %s call successful",
+ LOG_PREFIX, resourceProvider.getResourceProviderClass()));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.opendaylight.controller.yang.model.api.Module;
+import org.opendaylight.controller.yang.model.api.SchemaContext;
+import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
+import org.opendaylight.controller.yang.model.parser.impl.YangModelParserImpl;
+import org.opendaylight.controller.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.controller.yang2sources.spi.CodeGenerator;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Maps;
+
+@Mojo(name = "generate-sources", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
+public final class YangToSourcesMojo extends AbstractMojo {
+
+ private static final String LOG_PREFIX = "yang-to-sources:";
+
+ @Parameter(required = true)
+ private CodeGeneratorArg[] codeGenerators;
+
+ @Parameter(required = true)
+ private String yangFilesRootDir;
+
+ private final YangModelParser parser;
+
+ @VisibleForTesting
+ YangToSourcesMojo(CodeGeneratorArg[] codeGeneratorArgs,
+ YangModelParser parser, String yangFilesRootDir) {
+ super();
+ this.codeGenerators = codeGeneratorArgs;
+ this.yangFilesRootDir = yangFilesRootDir;
+ this.parser = parser;
+ }
+
+ public YangToSourcesMojo() {
+ super();
+ parser = new YangModelParserImpl();
+ }
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ SchemaContext context = processYang();
+ generateSources(context);
+ }
+
+ /**
+ * Generate {@link SchemaContext} with {@link YangModelParserImpl}
+ */
+ private SchemaContext processYang() throws MojoExecutionException {
+ try {
+ String[] yangFiles = Util.listFilesAsArrayOfPaths(yangFilesRootDir);
+
+ if (yangFiles.length == 0)
+ getLog().warn(
+ Util.message("No %s file found in %s", LOG_PREFIX,
+ Util.YANG_SUFFIX, yangFilesRootDir));
+ // TODO only warning or throw exception ?
+
+ Set<Module> parsedYang = parser.parseYangModels(yangFiles);
+ SchemaContext resolveSchemaContext = parser
+ .resolveSchemaContext(parsedYang);
+ getLog().info(
+ Util.message("%s files parsed from %s", LOG_PREFIX,
+ Util.YANG_SUFFIX, Arrays.toString(yangFiles)));
+ return resolveSchemaContext;
+
+ // MojoExecutionException is thrown since execution cannot continue
+ } catch (Exception e) {
+ String message = Util.message("Unable to parse %s files from %s",
+ LOG_PREFIX, Util.YANG_SUFFIX, yangFilesRootDir);
+ getLog().error(message, e);
+ throw new MojoExecutionException(message, e);
+ }
+ }
+
+ /**
+ * Call generate on every generator from plugin configuration
+ */
+ private void generateSources(SchemaContext context)
+ throws MojoFailureException {
+ if (codeGenerators.length == 0) {
+ getLog().warn(
+ Util.message("No code generators provided", LOG_PREFIX));
+ return;
+ }
+
+ Map<String, String> thrown = Maps.newHashMap();
+
+ for (CodeGeneratorArg codeGenerator : codeGenerators) {
+ try {
+
+ generateSourcesWithOneGenerator(context, codeGenerator);
+
+ } catch (Exception e) {
+ // try other generators, exception will be thrown after
+ getLog().error(
+ Util.message(
+ "Unable to generate sources with %s generator",
+ LOG_PREFIX,
+ codeGenerator.getCodeGeneratorClass()), e);
+ thrown.put(codeGenerator.getCodeGeneratorClass(), e.getClass()
+ .getCanonicalName());
+ }
+ }
+
+ if (!thrown.isEmpty()) {
+ String message = Util
+ .message(
+ "One or more code generators failed, including failed list(generatorClass=exception) %s",
+ LOG_PREFIX, thrown.toString());
+ getLog().error(message);
+ throw new MojoFailureException(message);
+ }
+ }
+
+ /**
+ * Instantiate generator from class and call required method
+ */
+ private void generateSourcesWithOneGenerator(SchemaContext context,
+ CodeGeneratorArg codeGenerator) throws ClassNotFoundException,
+ InstantiationException, IllegalAccessException {
+
+ codeGenerator.check();
+
+ CodeGenerator g = Util.getInstance(
+ codeGenerator.getCodeGeneratorClass(), CodeGenerator.class);
+ getLog().info(
+ Util.message("Code generator instantiated from %s", LOG_PREFIX,
+ codeGenerator.getCodeGeneratorClass()));
+
+ Collection<File> generated = g.generateSources(context,
+ codeGenerator.getOutputBaseDir());
+ getLog().info(
+ Util.message("Sources generated by %s: %s", LOG_PREFIX,
+ codeGenerator.getCodeGeneratorClass(), generated));
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin;
+
+import static org.hamcrest.core.Is.*;
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.yang2sources.plugin.ConfigArg.ResourceProviderArg;
+import org.opendaylight.controller.yang2sources.spi.ResourceGenerator;
+
+public class GenerateResourcesTest {
+
+ private String yang;
+ private YangToResourcesMojo mojo;
+ private File outDir;
+
+ @Before
+ public void setUp() {
+ yang = new File(getClass().getResource("/mock.yang").getFile())
+ .getParent();
+ outDir = new File("outputDir");
+ mojo = new YangToResourcesMojo(
+ new ResourceProviderArg[] {
+ new ResourceProviderArg(ProviderMock.class.getName(),
+ outDir),
+ new ResourceProviderArg(ProviderMock2.class.getName(),
+ outDir) }, yang);
+ }
+
+ @Test
+ public void test() throws Exception {
+ mojo.execute();
+ assertThat(ProviderMock.called, is(1));
+ assertThat(ProviderMock2.called, is(1));
+ assertThat(ProviderMock2.baseDir, is(outDir));
+ assertThat(ProviderMock.baseDir, is(outDir));
+ }
+
+ public static class ProviderMock implements ResourceGenerator {
+
+ private static int called = 0;
+ private static File baseDir;
+
+ @Override
+ public void generateResourceFiles(Collection<File> resources,
+ File outputDir) {
+ called++;
+ baseDir = outputDir;
+ }
+ }
+
+ public static class ProviderMock2 implements ResourceGenerator {
+
+ private static int called = 0;
+ private static File baseDir;
+
+ @Override
+ public void generateResourceFiles(Collection<File> resources,
+ File outputDir) {
+ called++;
+ baseDir = outputDir;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin;
+
+import static org.hamcrest.core.Is.*;
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.yang.model.api.SchemaContext;
+import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
+import org.opendaylight.controller.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
+import org.opendaylight.controller.yang2sources.spi.CodeGenerator;
+
+import com.google.common.collect.Lists;
+
+public class GenerateSourcesTest {
+
+ @Mock
+ private YangModelParser parser;
+ private String yang;
+ private YangToSourcesMojo mojo;
+ private File outDir;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ yang = new File(getClass().getResource("/mock.yang").getFile())
+ .getParent();
+ outDir = new File("outputDir");
+ mojo = new YangToSourcesMojo(
+ new CodeGeneratorArg[] { new CodeGeneratorArg(
+ GeneratorMock.class.getName(), outDir) }, parser, yang);
+ }
+
+ @Test
+ public void test() throws Exception {
+ mojo.execute();
+ verify(parser, times(1)).parseYangModels((String[]) anyVararg());
+ assertThat(GeneratorMock.called, is(1));
+ assertThat(GeneratorMock.outputDir, is(outDir));
+ }
+
+ public static class GeneratorMock implements CodeGenerator {
+
+ private static int called = 0;
+ private static File outputDir;
+
+ @Override
+ public Collection<File> generateSources(SchemaContext context,
+ File baseDir) {
+ called++;
+ outputDir = baseDir;
+ return Lists.newArrayList();
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.plugin;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.junit.Test;
+
+public class UtilTest {
+
+ @Test
+ public void testCache() {
+ String yang = new File(getClass().getResource("/mock.yang").getFile())
+ .getParent();
+ Collection<File> files = Util.listFiles(yang);
+ Collection<File> files2 = Util.listFiles(yang);
+ assertTrue(files == files2);
+ }
+
+}
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>binding-generator</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <version>1.0</version>
+ </parent>
+ <artifactId>yang-to-sources</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-model-api</artifactId>
+ <version>1.0</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.4</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.spi;
-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-package org.opendaylight.controller.sal.core.impl;\r
-\r
-import org.opendaylight.controller.sal.core.api.BrokerService;\r
-\r
-abstract public class BrokerServiceImpl implements BrokerService {\r
-\r
- ConsumerSessionImpl session;\r
-}\r
+import java.io.File;
+import java.util.Collection;
+
+import org.opendaylight.controller.yang.model.api.SchemaContext;
+
+public interface CodeGenerator {
+
+ Collection<File> generateSources(SchemaContext context, File outputBaseDir);
+}
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.spi;
-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
+import java.io.File;
+import java.util.Collection;
-package org.opendaylight.controller.sal.core.impl.data;
\ No newline at end of file
+public interface ResourceGenerator {
+
+ void generateResourceFiles(Collection<File> resources, File outputBaseDir);
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.spi;
+
+import java.io.File;
+import java.util.Collection;
+
+import org.opendaylight.controller.yang.model.api.SchemaContext;
+
+public class CodeGeneratorTestImpl implements CodeGenerator {
+
+ @Override
+ public Collection<File> generateSources(SchemaContext context,
+ File outputBaseDir) {
+ // no-op
+ return null;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.yang2sources.spi;
+
+import java.io.File;
+import java.util.Collection;
+
+public class ResourceProviderTestImpl implements ResourceGenerator {
+
+ @Override
+ public void generateResourceFiles(Collection<File> resources,
+ File outputBaseDir) {
+ // no-op
+ }
+
+}
<packaging>pom</packaging>\r
\r
<modules>\r
+ <module>sal-common</module>\r
+ <module>sal-common-util</module>\r
<module>sal-core-api</module>\r
<module>sal-data-api</module>\r
<module>sal-binding-api</module>\r
<module>sal-binding-spi</module>\r
<module>sal-binding-broker-impl</module>\r
<module>sal-schema-repository-api</module>\r
- <module>sal-common</module>\r
<module>sal-core-spi</module>\r
- <module>../yang</module>\r
<module>sal-broker-impl</module>\r
<module>sal-core-demo</module>\r
</modules>\r
<artifactId>slf4j-api</artifactId>\r
<version>1.7.2</version>\r
</dependency>\r
+ <dependency>\r
+ <groupId>junit</groupId>\r
+ <artifactId>junit</artifactId>\r
+ <version>4.10</version>\r
+ </dependency>\r
</dependencies>\r
</dependencyManagement>\r
\r
<dependency>\r
<groupId>junit</groupId>\r
<artifactId>junit</artifactId>\r
- <version>4.10</version>\r
<scope>test</scope>\r
<optional>true</optional>\r
</dependency>\r
* @param store\r
* @param refresher\r
*/\r
- void removeRefresher(DataStoreIdentifier store, DataRefresher refresher);\r
-\r
- /**\r
- * Trigger for refreshing of the data exposed by the {@link Provider}\r
- * \r
-\r
- * \r
- */\r
- public interface DataRefresher extends\r
- BindingAwareProvider.ProviderFunctionality {\r
-\r
- /**\r
- * Fired when some component explicitly requested the data refresh.\r
- * \r
- * The provider which exposed the {@link DataRefresher} should republish\r
- * its provided data by editing the data in all affected data stores.\r
- */\r
- void refreshData();\r
- }\r
+ void removeRefresher(DataStoreIdentifier store, DataRefresher refresher);\r
\r
}\r
*/\r
package org.opendaylight.controller.sal.binding.api;\r
\r
-import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;\r
-\r
-public interface DataRefresher extends ProviderFunctionality {\r
+/**\r
+ * Trigger for refreshing of the data exposed by the {@link Provider}\r
+ * \r
+ * \r
+ * \r
+ */\r
+public interface DataRefresher extends\r
+ BindingAwareProvider.ProviderFunctionality {\r
\r
+ /**\r
+ * Fired when some component explicitly requested the data refresh.\r
+ * \r
+ * The provider which exposed the {@link DataRefresher} should republish its\r
+ * provided data by editing the data in all affected data stores.\r
+ */\r
void refreshData();\r
-}\r
+}
\ No newline at end of file
\r
\r
<dependencies>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-common-util</artifactId>\r
+ <version>1.0-SNAPSHOT</version>\r
+ </dependency>\r
<dependency>\r
<groupId>org.opendaylight.controller</groupId>\r
<artifactId>sal-binding-api</artifactId>\r
*/
package org.opendaylight.controller.sal.binding.impl;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
import org.opendaylight.controller.sal.binding.api.BindingAwareConsumer;
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.binding.spi.Mapper;
+import org.opendaylight.controller.sal.binding.spi.MappingProvider;
+import org.opendaylight.controller.sal.binding.spi.RpcMapper;
+import org.opendaylight.controller.sal.binding.spi.RpcMapper.RpcProxyInvocationHandler;
import org.opendaylight.controller.sal.binding.spi.SALBindingModule;
+import org.opendaylight.controller.sal.common.util.Rpcs;
+import org.opendaylight.controller.sal.core.api.Provider;
+import org.opendaylight.controller.sal.core.api.RpcImplementation;
+import org.opendaylight.controller.yang.binding.DataObject;
import org.opendaylight.controller.yang.binding.RpcService;
+import org.opendaylight.controller.yang.common.QName;
+import org.opendaylight.controller.yang.common.RpcResult;
+import org.opendaylight.controller.yang.data.api.CompositeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private Set<SALBindingModule> modules = new HashSet<SALBindingModule>();
private Map<Class<? extends BindingAwareService>, SALBindingModule> salServiceProviders = new HashMap<Class<? extends BindingAwareService>, SALBindingModule>();
+ private MappingProvider mapping;
+ private BIFacade biFacade = new BIFacade();
+ private org.opendaylight.controller.sal.core.api.Broker.ProviderSession biSession;
+ private ExecutorService executor;
+
+ Map<Class<? extends RpcService>, RpcService> rpcImpls = Collections
+ .synchronizedMap(new HashMap<Class<? extends RpcService>, RpcService>());
+
+ private RpcProxyInvocationHandler rpcProxyHandler = new RpcProxyInvocationHandler() {
+
+ @Override
+ public Future<RpcResult<? extends DataObject>> invokeRpc(
+ RpcService proxy, QName rpc, DataObject input) {
+ return rpcProxyInvoked(proxy, rpc, input);
+ }
+ };
@Override
public ConsumerSession registerConsumer(BindingAwareConsumer consumer) {
consumer.onSessionInitialized(session);
sessions.add(session);
-
return session;
-
}
@Override
}
+ private <T extends RpcService> T newRpcProxyForSession(Class<T> service) {
+
+ RpcMapper<T> mapper = mapping.rpcMapperForClass(service);
+ if (mapper == null) {
+ log.error("Mapper for " + service + "is unavailable.");
+ return null;
+ }
+ T proxy = mapper.getConsumerProxy(rpcProxyHandler);
+
+ return proxy;
+ }
+
+ private Future<RpcResult<? extends DataObject>> rpcProxyInvoked(
+ RpcService rpcProxy, QName rpcType, DataObject inputData) {
+ if (rpcProxy == null) {
+ throw new IllegalArgumentException("Proxy must not be null");
+ }
+ if (rpcType == null) {
+ throw new IllegalArgumentException(
+ "rpcType (QName) should not be null");
+ }
+ Future<RpcResult<? extends DataObject>> ret = null;
+
+ // Real invocation starts here
+ RpcMapper<? extends RpcService> mapper = mapping
+ .rpcMapperForProxy(rpcProxy);
+ RpcService impl = rpcImpls.get(mapper.getServiceClass());
+
+ if (impl == null) {
+ // RPC is probably remote
+ CompositeNode inputNode = null;
+ Mapper<? extends DataObject> inputMapper = mapper.getInputMapper();
+ if (inputMapper != null) {
+ inputNode = inputMapper.domFromObject(inputData);
+ }
+ Future<RpcResult<CompositeNode>> biResult = biSession.rpc(rpcType,
+ inputNode);
+ ret = new TranslatedFuture(biResult, mapper);
+
+ } else {
+ // RPC is local
+ Callable<RpcResult<? extends DataObject>> invocation = localRpcCallableFor(
+ impl, mapper, rpcType, inputData);
+ ret = executor.submit(invocation);
+ }
+ return ret;
+ }
+
+ private Callable<RpcResult<? extends DataObject>> localRpcCallableFor(
+ final RpcService impl,
+ final RpcMapper<? extends RpcService> mapper, final QName rpcType,
+ final DataObject inputData) {
+
+ return new Callable<RpcResult<? extends DataObject>>() {
+
+ @Override
+ public RpcResult<? extends DataObject> call() throws Exception {
+ return mapper.invokeRpcImplementation(rpcType, impl, inputData);
+ }
+ };
+ }
+
+ // Binding Independent invocation of Binding Aware RPC
+ private RpcResult<CompositeNode> invokeLocalRpc(QName rpc,
+ CompositeNode inputNode) {
+ RpcMapper<? extends RpcService> mapper = mapping.rpcMapperForData(rpc,
+ inputNode);
+
+ DataObject inputTO = mapper.getInputMapper().objectFromDom(inputNode);
+
+ RpcService impl = rpcImpls.get(mapper.getServiceClass());
+ if (impl == null) {
+ log.warn("Implementation for rpc: " + rpc + "not available.");
+ }
+ RpcResult<? extends DataObject> result = mapper
+ .invokeRpcImplementation(rpc, impl, inputTO);
+ DataObject outputTO = result.getResult();
+
+ CompositeNode outputNode = null;
+ if (outputTO != null) {
+ outputNode = mapper.getOutputMapper().domFromObject(outputTO);
+ }
+ return Rpcs.getRpcResult(result.isSuccessful(), outputNode,
+ result.getErrors());
+ }
+
private class ConsumerSessionImpl implements
BindingAwareBroker.ConsumerSession {
private final BindingAwareConsumer consumer;
- private Map<Class<? extends BindingAwareService>, BindingAwareService> sessionSalServices = new HashMap<Class<? extends BindingAwareService>, BindingAwareService>();
+ private Map<Class<? extends BindingAwareService>, BindingAwareService> sessionSalServices = Collections
+ .synchronizedMap(new HashMap<Class<? extends BindingAwareService>, BindingAwareService>());
+
+ private Map<Class<? extends RpcService>, RpcService> sessionRpcProxies = Collections
+ .synchronizedMap(new HashMap<Class<? extends RpcService>, RpcService>());
public ConsumerSessionImpl(BindingAwareConsumer cons) {
this.consumer = cons;
}
@Override
- public <T extends RpcService> T getRpcService(Class<T> module) {
- // TODO Implement this method
- throw new UnsupportedOperationException("Not implemented");
+ public <T extends RpcService> T getRpcService(Class<T> service) {
+ RpcService current = sessionRpcProxies.get(service);
+ if (current != null) {
+ if (service.isInstance(current)) {
+ @SuppressWarnings("unchecked")
+ T ret = (T) current;
+ return ret;
+ } else {
+ log.error("Proxy for rpc service " + service.getName()
+ + " does not implement the service interface");
+ throw new IllegalStateException("Service implementation "
+ + current.getClass().getName()
+ + "does not implement " + service.getName());
+ }
+ } else {
+ T ret = BindingBrokerImpl.this.newRpcProxyForSession(service);
+ if (ret != null) {
+ sessionRpcProxies.put(service, ret);
+ }
+ return ret;
+ }
}
public BindingAwareConsumer getConsumer() {
@Override
public void addRpcImplementation(RpcService implementation) {
+ if (implementation == null) {
+ throw new IllegalArgumentException(
+ "Implementation should not be null");
+ }
// TODO Implement this method
throw new UnsupportedOperationException("Not implemented");
}
@Override
public void removeRpcImplementation(RpcService implementation) {
+ if (implementation == null) {
+ throw new IllegalArgumentException(
+ "Implementation should not be null");
+ }
// TODO Implement this method
throw new UnsupportedOperationException("Not implemented");
}
}
+ private class BIFacade implements Provider,RpcImplementation {
+
+ @Override
+ public Set<QName> getSupportedRpcs() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public RpcResult<CompositeNode> invokeRpc(QName rpc, CompositeNode input) {
+ if (rpc == null) {
+ throw new IllegalArgumentException(
+ "Rpc type should not be null");
+ }
+
+ return BindingBrokerImpl.this.invokeLocalRpc(rpc, input);
+ }
+
+ @Override
+ public void onSessionInitiated(
+ org.opendaylight.controller.sal.core.api.Broker.ProviderSession session) {
+
+ BindingBrokerImpl.this.biSession = session;
+ for (SALBindingModule module : modules) {
+ try {
+ module.onBISessionAvailable(biSession);
+ } catch(Exception e) {
+ log.error("Module " +module +" throwed unexpected exception",e);
+ }
+ }
+ }
+
+ @Override
+ public Collection<ProviderFunctionality> getProviderFunctionality() {
+ return Collections.emptySet();
+ }
+
+ }
+
+ private static class TranslatedFuture implements
+ Future<RpcResult<? extends DataObject>> {
+ private final Future<RpcResult<CompositeNode>> realFuture;
+ private final RpcMapper<?> mapper;
+
+ public TranslatedFuture(Future<RpcResult<CompositeNode>> future,
+ RpcMapper<?> mapper) {
+ realFuture = future;
+ this.mapper = mapper;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return realFuture.cancel(mayInterruptIfRunning);
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return realFuture.isCancelled();
+ }
+
+ @Override
+ public boolean isDone() {
+ return realFuture.isDone();
+ }
+
+ @Override
+ public RpcResult<? extends DataObject> get()
+ throws InterruptedException, ExecutionException {
+ RpcResult<CompositeNode> val = realFuture.get();
+ return tranlate(val);
+ }
+
+ @Override
+ public RpcResult<? extends DataObject> get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException,
+ TimeoutException {
+ RpcResult<CompositeNode> val = realFuture.get(timeout, unit);
+ return tranlate(val);
+ }
+
+ private RpcResult<? extends DataObject> tranlate(
+ RpcResult<CompositeNode> result) {
+ CompositeNode outputNode = result.getResult();
+ DataObject outputTO = null;
+ if (outputNode != null) {
+ Mapper<?> outputMapper = mapper.getOutputMapper();
+ outputTO = outputMapper.objectFromDom(outputNode);
+ }
+ return Rpcs.getRpcResult(result.isSuccessful(), outputTO,
+ result.getErrors());
+ }
+
+ }
}
--- /dev/null
+package org.opendaylight.controller.sal.binding.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerSession;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality;
+import org.opendaylight.controller.sal.binding.api.BindingAwareService;
+import org.opendaylight.controller.sal.binding.api.DataBrokerService;
+import org.opendaylight.controller.sal.binding.api.DataCommitHandler;
+import org.opendaylight.controller.sal.binding.api.DataProviderService;
+import org.opendaylight.controller.sal.binding.api.DataValidator;
+import org.opendaylight.controller.sal.binding.spi.MappingProvider;
+import org.opendaylight.controller.sal.binding.spi.SALBindingModule;
+import org.opendaylight.controller.sal.common.DataStoreIdentifier;
+import org.opendaylight.controller.sal.binding.api.DataRefresher;
+import org.opendaylight.controller.yang.binding.DataRoot;
+import org.opendaylight.controller.yang.common.RpcResult;
+import org.opendaylight.controller.yang.data.api.CompositeNode;
+
+public class DataModule implements SALBindingModule {
+
+ private BindingAwareBroker broker;
+ private org.opendaylight.controller.sal.core.api.Broker.ProviderSession biSession;
+ private MappingProvider mappingProvider;
+ private final BIFacade biFacade = new BIFacade();
+ private org.opendaylight.controller.sal.core.api.data.DataProviderService biDataService;
+
+ @Override
+ public void setBroker(BindingAwareBroker broker) {
+ this.broker = broker;
+ }
+
+ @Override
+ public void onBISessionAvailable(
+ org.opendaylight.controller.sal.core.api.Broker.ProviderSession session) {
+ this.biSession = session;
+ this.biDataService = session
+ .getService(org.opendaylight.controller.sal.core.api.data.DataProviderService.class);
+ // biDataService.addRefresher(store, refresher)
+
+ }
+
+ @Override
+ public void setMappingProvider(MappingProvider provider) {
+ this.mappingProvider = provider;
+
+ }
+
+ @Override
+ public Set<Class<? extends BindingAwareService>> getProvidedServices() {
+ Set<Class<? extends BindingAwareService>> ret = new HashSet<Class<? extends BindingAwareService>>();
+ ret.add(DataBrokerService.class);
+ ret.add(DataProviderService.class);
+ return ret;
+ }
+
+ @Override
+ public <T extends BindingAwareService> T getServiceForSession(
+ Class<T> service, ConsumerSession session) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private class DataBrokerSession implements DataBrokerService {
+
+ @Override
+ public <T extends DataRoot> T getData(DataStoreIdentifier store,
+ Class<T> rootType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T extends DataRoot> T getData(DataStoreIdentifier store,
+ T filter) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T extends DataRoot> T getCandidateData(
+ DataStoreIdentifier store, Class<T> rootType) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public <T extends DataRoot> T getCandidateData(
+ DataStoreIdentifier store, T filter) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public RpcResult<DataRoot> editCandidateData(DataStoreIdentifier store,
+ DataRoot changeSet) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Future<RpcResult<Void>> commit(DataStoreIdentifier store) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
+
+ private class DataProviderSession extends DataBrokerSession implements
+ DataProviderService {
+
+ @Override
+ public void addValidator(DataStoreIdentifier store,
+ DataValidator validator) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void removeValidator(DataStoreIdentifier store,
+ DataValidator validator) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void addCommitHandler(DataStoreIdentifier store,
+ DataCommitHandler provider) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void removeCommitHandler(DataStoreIdentifier store,
+ DataCommitHandler provider) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void addRefresher(DataStoreIdentifier store,
+ DataRefresher refresher) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void removeRefresher(DataStoreIdentifier store,
+ DataRefresher refresher) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ private class BIFacade
+ implements
+ org.opendaylight.controller.sal.core.api.data.DataCommitHandler,
+ org.opendaylight.controller.sal.core.api.data.DataValidator,
+ org.opendaylight.controller.sal.core.api.data.DataProviderService.DataRefresher {
+
+ @Override
+ public RpcResult<Void> validate(CompositeNode toValidate) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Set<DataStoreIdentifier> getSupportedDataStores() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public RpcResult<CommitTransaction> requestCommit(
+ DataStoreIdentifier store) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void refreshData() {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+}
package org.opendaylight.controller.sal.binding.spi;
import org.opendaylight.controller.yang.binding.DataObject;
+import org.opendaylight.controller.yang.binding.RpcService;
import org.opendaylight.controller.yang.common.QName;
+import org.opendaylight.controller.yang.data.api.CompositeNode;
public interface MappingProvider {
- <T extends DataObject> Mapper<T> getMapper(Class<T> type);
- Mapper<DataObject> getMapper(QName name);
+ <T extends DataObject> Mapper<T> mapperForClass(Class<T> type);
+ Mapper<DataObject> mapperForQName(QName name);
+
+ /**
+ * Returns {@link RpcMapper} associated to class
+ *
+ * @param type Class for which RpcMapper should provide mapping
+ * @return
+ */
+ <T extends RpcService> RpcMapper<T> rpcMapperForClass(Class<T> type);
+
+ /**
+ * Returns {@link RpcMapper} associated to the {@link RpcService} proxy.
+ *
+ * @param proxy
+ * @return
+ */
+ RpcMapper<? extends RpcService> rpcMapperForProxy(RpcService proxy);
+
+ /**
+ *
+ *
+ * @param rpc
+ * @param inputNode
+ * @return
+ */
+ RpcMapper<? extends RpcService> rpcMapperForData(QName rpc,
+ CompositeNode inputNode);
<T extends MappingExtension> MappingExtensionFactory<T> getExtensionFactory(Class<T> cls);
T forClass(Class<?> obj);
}
+
+
}
--- /dev/null
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. 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.controller.sal.binding.spi;
+
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.yang.binding.DataObject;
+import org.opendaylight.controller.yang.binding.RpcService;
+import org.opendaylight.controller.yang.common.QName;
+import org.opendaylight.controller.yang.common.RpcResult;
+
+public interface RpcMapper<T extends RpcService> {
+
+ Set<QName> getRpcQNames();
+
+ /**
+ * Returns a class object representing subinterface
+ * to whom, this mapper is assigned.
+ *
+ * @return
+ */
+ Class<T> getServiceClass();
+
+ /**
+ * Returns a Binding Mapper for Rpc Input Data
+ * @return
+ */
+ Mapper<?> getInputMapper();
+ /**
+ * Returns a Binding Mapper for Rpc Output Data
+ *
+ * @return
+ */
+ Mapper<?> getOutputMapper();
+
+ /**
+ * Returns a consumer proxy, which is responsible
+ * for invoking the rpc functionality of {@link BindingAwareBroker} implementation.
+ *
+ * @return Proxy of {@link RpcService} assigned to this mapper.
+ */
+ T getConsumerProxy(RpcProxyInvocationHandler handler);
+
+ /**
+ * Invokes the method of RpcService representing the supplied rpc.
+ *
+ * @param rpc QName of Rpc
+ * @param impl Implementation of RpcService on which the method should be invoked
+ * @param baInput Input Data to RPC method
+ * @return Result of RPC invocation.
+ */
+ RpcResult<? extends DataObject> invokeRpcImplementation(QName rpc,
+ RpcService impl, DataObject baInput);
+
+ public interface RpcProxyInvocationHandler {
+
+ Future<RpcResult<? extends DataObject>> invokeRpc(RpcService proxy, QName rpc, DataObject input);
+ }
+}
<artifactId>sal-core-api</artifactId>\r
<version>1.0-SNAPSHOT</version>\r
</dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-common-util</artifactId>\r
+ <version>1.0-SNAPSHOT</version>\r
+ </dependency>\r
<dependency>\r
<groupId>org.opendaylight.controller</groupId>\r
<artifactId>sal-core-spi</artifactId>\r
-
/*\r
* Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
*\r
*/\r
package org.opendaylight.controller.sal.core.impl;\r
\r
+import java.util.Collection;\r
+import java.util.Collections;\r
import java.util.HashMap;\r
import java.util.HashSet;\r
import java.util.Map;\r
import java.util.Set;\r
+import java.util.concurrent.Callable;\r
+import java.util.concurrent.ExecutorService;\r
import java.util.concurrent.Future;\r
-\r
import org.opendaylight.controller.sal.core.api.Broker;\r
import org.opendaylight.controller.sal.core.api.BrokerService;\r
import org.opendaylight.controller.sal.core.api.Consumer;\r
import org.opendaylight.controller.sal.core.api.Provider;\r
+import org.opendaylight.controller.sal.core.api.RpcImplementation;\r
import org.opendaylight.controller.sal.core.spi.BrokerModule;\r
-import org.opendaylight.controller.yang.common.QName;
-import org.opendaylight.controller.yang.common.RpcResult;
-import org.opendaylight.controller.yang.data.api.CompositeNode;
+import org.opendaylight.controller.yang.common.QName;\r
+import org.opendaylight.controller.yang.common.RpcResult;\r
+import org.opendaylight.controller.yang.data.api.CompositeNode;\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
\r
-\r
public class BrokerImpl implements Broker {\r
private static Logger log = LoggerFactory.getLogger(BrokerImpl.class);\r
\r
- private Set<ConsumerSessionImpl> sessions = new HashSet<ConsumerSessionImpl>();\r
- private Set<ProviderSessionImpl> providerSessions = new HashSet<ProviderSessionImpl>();\r
- // private ExecutorService executor;\r
- private Set<BrokerModule> modules = new HashSet<BrokerModule>();\r
+ // Broker Generic Context\r
+ private Set<ConsumerSessionImpl> sessions = Collections\r
+ .synchronizedSet(new HashSet<ConsumerSessionImpl>());\r
+ private Set<ProviderSessionImpl> providerSessions = Collections\r
+ .synchronizedSet(new HashSet<ProviderSessionImpl>());\r
+ private Set<BrokerModule> modules = Collections\r
+ .synchronizedSet(new HashSet<BrokerModule>());\r
+ private Map<Class<? extends BrokerService>, BrokerModule> serviceProviders = Collections\r
+ .synchronizedMap(new HashMap<Class<? extends BrokerService>, BrokerModule>());\r
+\r
+ // RPC Context\r
+ private Map<QName, RpcImplementation> rpcImpls = Collections\r
+ .synchronizedMap(new HashMap<QName, RpcImplementation>());\r
\r
- private Map<Class<? extends BrokerService>, BrokerModule> serviceProviders = new HashMap<Class<? extends BrokerService>, BrokerModule>();\r
+ // Implementation specific\r
+ private ExecutorService executor;\r
\r
@Override\r
public ConsumerSession registerConsumer(Consumer consumer) {\r
checkPredicates(consumer);\r
log.info("Registering consumer " + consumer);\r
-\r
ConsumerSessionImpl session = newSessionFor(consumer);\r
consumer.onSessionInitiated(session);\r
-\r
sessions.add(session);\r
-\r
return session;\r
-\r
}\r
\r
@Override\r
\r
ProviderSessionImpl session = newSessionFor(provider);\r
provider.onSessionInitiated(session);\r
-\r
providerSessions.add(session);\r
return session;\r
}\r
\r
+ public void addModule(BrokerModule module) {\r
+ log.info("Registering broker module " + module);\r
+ if (modules.contains(module)) {\r
+ log.error("Module already registered");\r
+ throw new IllegalArgumentException("Module already exists.");\r
+ }\r
+ \r
+ Set<Class<? extends BrokerService>> provServices = module\r
+ .getProvidedServices();\r
+ for (Class<? extends BrokerService> serviceType : provServices) {\r
+ log.info(" Registering session service implementation: "\r
+ + serviceType.getCanonicalName());\r
+ serviceProviders.put(serviceType, module);\r
+ }\r
+ }\r
+\r
public <T extends BrokerService> T serviceFor(Class<T> service,\r
ConsumerSessionImpl session) {\r
BrokerModule prov = serviceProviders.get(service);\r
return prov.getServiceForSession(service, session);\r
}\r
\r
- public Future<RpcResult<CompositeNode>> invokeRpc(QName rpc,\r
+ // RPC Functionality\r
+ \r
+ private void addRpcImplementation(QName rpcType,\r
+ RpcImplementation implementation) {\r
+ synchronized (rpcImpls) {\r
+ if (rpcImpls.get(rpcType) != null) {\r
+ throw new IllegalStateException("Implementation for rpc "\r
+ + rpcType + " is already registered.");\r
+ }\r
+ rpcImpls.put(rpcType, implementation);\r
+ }\r
+ // TODO Add notification for availability of Rpc Implementation\r
+ }\r
+\r
+ private void removeRpcImplementation(QName rpcType,\r
+ RpcImplementation implToRemove) {\r
+ synchronized (rpcImpls) {\r
+ if (implToRemove == rpcImpls.get(rpcType)) {\r
+ rpcImpls.remove(rpcType);\r
+ }\r
+ }\r
+ // TODO Add notification for removal of Rpc Implementation\r
+ }\r
+\r
+ private Future<RpcResult<CompositeNode>> invokeRpc(QName rpc,\r
CompositeNode input) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
+ RpcImplementation impl = rpcImpls.get(rpc);\r
+ // if()\r
+\r
+ Callable<RpcResult<CompositeNode>> call = callableFor(impl,\r
+ rpc, input);\r
+ Future<RpcResult<CompositeNode>> result = executor.submit(call);\r
+\r
+ return result;\r
}\r
+ \r
+ // Validation\r
\r
private void checkPredicates(Provider prov) {\r
if (prov == null)\r
}\r
}\r
\r
+ // Private Factory methods\r
+ \r
private ConsumerSessionImpl newSessionFor(Consumer cons) {\r
- return new ConsumerSessionImpl(this, cons);\r
+ return new ConsumerSessionImpl(cons);\r
}\r
\r
private ProviderSessionImpl newSessionFor(Provider provider) {\r
- return new ProviderSessionImpl(this, provider);\r
+ return new ProviderSessionImpl(provider);\r
}\r
\r
- public void addModule(BrokerModule module) {\r
- log.info("Registering broker module " + module);\r
- if (modules.contains(module)) {\r
- log.error("Module already registered");\r
- throw new IllegalArgumentException("Module already exists.");\r
+ private void consumerSessionClosed(ConsumerSessionImpl consumerSessionImpl) {\r
+ sessions.remove(consumerSessionImpl);\r
+ providerSessions.remove(consumerSessionImpl);\r
+ }\r
+\r
+ private static Callable<RpcResult<CompositeNode>> callableFor(\r
+ final RpcImplementation implemenation, final QName rpc,\r
+ final CompositeNode input) {\r
+\r
+ return new Callable<RpcResult<CompositeNode>>() {\r
+\r
+ @Override\r
+ public RpcResult<CompositeNode> call() throws Exception {\r
+ return implemenation.invokeRpc(rpc, input);\r
+ }\r
+ };\r
+ }\r
+ \r
+ private class ConsumerSessionImpl implements ConsumerSession {\r
+\r
+ private final Consumer consumer;\r
+\r
+ private Map<Class<? extends BrokerService>, BrokerService> instantiatedServices = Collections\r
+ .synchronizedMap(new HashMap<Class<? extends BrokerService>, BrokerService>());\r
+ private boolean closed = false;\r
+\r
+ public Consumer getConsumer() {\r
+ return consumer;\r
}\r
\r
- Set<Class<? extends BrokerService>> provServices = module\r
- .getProvidedServices();\r
- for (Class<? extends BrokerService> serviceType : provServices) {\r
- log.info(" Registering session service implementation: "\r
- + serviceType.getCanonicalName());\r
- serviceProviders.put(serviceType, module);\r
+ public ConsumerSessionImpl(Consumer consumer) {\r
+ this.consumer = consumer;\r
+ }\r
+\r
+ @Override\r
+ public Future<RpcResult<CompositeNode>> rpc(QName rpc,\r
+ CompositeNode input) {\r
+ return BrokerImpl.this.invokeRpc(rpc, input);\r
+ }\r
+\r
+ @Override\r
+ public <T extends BrokerService> T getService(Class<T> service) {\r
+ BrokerService potential = instantiatedServices.get(service);\r
+ if (potential != null) {\r
+ @SuppressWarnings("unchecked")\r
+ T ret = (T) potential;\r
+ return ret;\r
+ }\r
+ T ret = BrokerImpl.this.serviceFor(service, this);\r
+ if (ret != null) {\r
+ instantiatedServices.put(service, ret);\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ @Override\r
+ public void close() {\r
+ Collection<BrokerService> toStop = instantiatedServices.values();\r
+ this.closed = true;\r
+ for (BrokerService brokerService : toStop) {\r
+ brokerService.closeSession();\r
+ }\r
+ BrokerImpl.this.consumerSessionClosed(this);\r
+ }\r
+\r
+ @Override\r
+ public boolean isClosed() {\r
+ return closed;\r
}\r
- }\r
\r
- public void consumerSessionClosed(ConsumerSessionImpl consumerSessionImpl) {\r
- sessions.remove(consumerSessionImpl);\r
- providerSessions.remove(consumerSessionImpl);\r
}\r
\r
+ private class ProviderSessionImpl extends ConsumerSessionImpl implements\r
+ ProviderSession {\r
+\r
+ private Provider provider;\r
+ private Map<QName, RpcImplementation> sessionRpcImpls = Collections.synchronizedMap(new HashMap<QName, RpcImplementation>());\r
+\r
+ public ProviderSessionImpl(Provider provider) {\r
+ super(null);\r
+ this.provider = provider;\r
+ }\r
+\r
+ @Override\r
+ public void addRpcImplementation(QName rpcType,\r
+ RpcImplementation implementation)\r
+ throws IllegalArgumentException {\r
+ if (rpcType == null) {\r
+ throw new IllegalArgumentException("rpcType must not be null");\r
+ }\r
+ if (implementation == null) {\r
+ throw new IllegalArgumentException(\r
+ "Implementation must not be null");\r
+ }\r
+ BrokerImpl.this.addRpcImplementation(rpcType, implementation);\r
+ sessionRpcImpls.put(rpcType, implementation);\r
+ }\r
+\r
+ @Override\r
+ public void removeRpcImplementation(QName rpcType,\r
+ RpcImplementation implToRemove) throws IllegalArgumentException {\r
+ RpcImplementation localImpl = rpcImpls.get(rpcType);\r
+ if (localImpl != implToRemove) {\r
+ throw new IllegalStateException(\r
+ "Implementation was not registered in this session");\r
+ }\r
+\r
+ BrokerImpl.this.removeRpcImplementation(rpcType, implToRemove);\r
+ sessionRpcImpls.remove(rpcType);\r
+ }\r
+\r
+ public Provider getProvider() {\r
+ return this.provider;\r
+ }\r
+\r
+ }\r
}\r
-
+++ /dev/null
-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-package org.opendaylight.controller.sal.core.impl;\r
-\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.concurrent.Future;\r
-\r
-import org.opendaylight.controller.sal.core.api.BrokerService;\r
-import org.opendaylight.controller.sal.core.api.Consumer;\r
-import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
-import org.opendaylight.controller.yang.common.QName;\r
-import org.opendaylight.controller.yang.common.RpcResult;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-\r
-\r
-public class ConsumerSessionImpl implements ConsumerSession {\r
-\r
- private final BrokerImpl broker;\r
- private final Consumer consumer;\r
-\r
- private Map<Class<? extends BrokerService>, BrokerService> instantiatedServices = new HashMap<Class<? extends BrokerService>, BrokerService>();\r
- private boolean closed = false;\r
-\r
- public Consumer getConsumer() {\r
- return consumer;\r
- }\r
-\r
- public ConsumerSessionImpl(BrokerImpl broker, Consumer consumer) {\r
- this.broker = broker;\r
- this.consumer = consumer;\r
- }\r
-\r
- @Override\r
- public Future<RpcResult<CompositeNode>> rpc(QName rpc, CompositeNode input) {\r
- return broker.invokeRpc(rpc, input);\r
- }\r
-\r
- @Override\r
- public <T extends BrokerService> T getService(Class<T> service) {\r
- BrokerService potential = instantiatedServices.get(service);\r
- if (potential != null) {\r
- @SuppressWarnings("unchecked")\r
- T ret = (T) potential;\r
- return ret;\r
- }\r
- T ret = this.broker.serviceFor(service, this);\r
- if (ret != null) {\r
- instantiatedServices.put(service, ret);\r
- }\r
- return ret;\r
- }\r
-\r
- @Override\r
- public void close() {\r
- Collection<BrokerService> toStop = instantiatedServices.values();\r
- this.closed = true;\r
- for (BrokerService brokerService : toStop) {\r
- brokerService.closeSession();\r
- }\r
- broker.consumerSessionClosed(this);\r
- }\r
-\r
- @Override\r
- public boolean isClosed() {\r
- return closed;\r
- }\r
-\r
-}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
* and is available at http://www.eclipse.org/legal/epl-v10.html\r
*/\r
-package org.opendaylight.controller.sal.core.impl.data;\r
+package org.opendaylight.controller.sal.core.impl;\r
\r
import java.util.ArrayList;\r
import java.util.Collections;\r
import java.util.List;\r
import java.util.Map;\r
import java.util.Set;\r
+import java.util.concurrent.ExecutorService;\r
import java.util.concurrent.Future;\r
\r
import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
+import org.opendaylight.controller.sal.common.util.Rpcs;\r
import org.opendaylight.controller.sal.core.api.BrokerService;\r
import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;\r
import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;\r
import org.opendaylight.controller.sal.core.api.data.DataValidator;\r
import org.opendaylight.controller.sal.core.api.data.DataCommitHandler.CommitTransaction;\r
import org.opendaylight.controller.sal.core.api.data.DataProviderService.DataRefresher;\r
-import org.opendaylight.controller.sal.core.impl.RpcUtils;\r
import org.opendaylight.controller.sal.core.spi.BrokerModule;\r
import org.opendaylight.controller.yang.common.RpcError;\r
import org.opendaylight.controller.yang.common.RpcResult;\r
import org.slf4j.Logger;\r
import org.slf4j.LoggerFactory;\r
\r
+import com.google.common.collect.ImmutableSet;\r
\r
public class DataBrokerModule implements BrokerModule {\r
\r
private static final Logger log = LoggerFactory\r
.getLogger(DataBrokerModule.class);\r
\r
+ private static final Set<Class<? extends ProviderFunctionality>> SUPPORTED_PROVIDER_FUNCTIONALITY = ImmutableSet\r
+ .of((Class<? extends ProviderFunctionality>) DataValidator.class,\r
+ DataRefresher.class, DataCommitHandler.class);\r
+\r
+ private static final Set<Class<? extends BrokerService>> PROVIDED_SESSION_SERVICES = ImmutableSet\r
+ .of((Class<? extends BrokerService>) DataBrokerService.class,\r
+ DataProviderService.class);\r
+\r
private Map<DataStoreIdentifier, StoreContext> storeContext;\r
\r
+ private ExecutorService executor;\r
+ \r
private SequentialCommitHandlerCoordinator coordinator = new SequentialCommitHandlerCoordinator();\r
\r
@Override\r
public Set<Class<? extends BrokerService>> getProvidedServices() {\r
- // FIXME: Refactor\r
- Set<Class<? extends BrokerService>> ret = new HashSet<Class<? extends BrokerService>>();\r
- ret.add(DataBrokerService.class);\r
- ret.add(DataProviderService.class);\r
- return ret;\r
+ return PROVIDED_SESSION_SERVICES;\r
}\r
\r
@Override\r
public Set<Class<? extends ProviderFunctionality>> getSupportedProviderFunctionality() {\r
- // FIXME Refactor\r
- Set<Class<? extends ProviderFunctionality>> ret = new HashSet<Class<? extends ProviderFunctionality>>();\r
- ret.add(DataValidator.class);\r
- ret.add(DataCommitHandler.class);\r
- ret.add(DataRefresher.class);\r
- return ret;\r
+ return SUPPORTED_PROVIDER_FUNCTIONALITY;\r
+ }\r
+\r
+ @Override\r
+ public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {\r
+ return Collections.emptySet();\r
}\r
\r
@Override\r
}\r
\r
private DataProviderService newDataProviderService(ConsumerSession session) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
+ return new DataProviderSession();\r
}\r
\r
private DataBrokerService newDataConsumerService(ConsumerSession session) {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
+ return new DataConsumerSession();\r
}\r
\r
- @Override\r
- public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
+ private StoreContext context(DataStoreIdentifier store) {\r
+ return storeContext.get(store);\r
}\r
\r
private static class StoreContext {\r
- private Set<DataCommitHandler> commitHandlers = new HashSet<DataCommitHandler>();\r
- private Set<DataValidator> validators = new HashSet<DataValidator>();\r
- private Set<DataRefresher> refreshers = new HashSet<DataRefresher>();\r
+ private Set<DataCommitHandler> commitHandlers = Collections\r
+ .synchronizedSet(new HashSet<DataCommitHandler>());\r
+ private Set<DataValidator> validators = Collections\r
+ .synchronizedSet(new HashSet<DataValidator>());\r
+ private Set<DataRefresher> refreshers = Collections\r
+ .synchronizedSet(new HashSet<DataRefresher>());\r
}\r
\r
private class DataConsumerSession implements DataBrokerService {\r
throw new UnsupportedOperationException("Not implemented");\r
}\r
\r
- }\r
+ @Override\r
+ public Set<DataStoreIdentifier> getDataStores() {\r
+ // TODO Auto-generated method stub\r
+ return null;\r
+ }\r
\r
- private StoreContext context(DataStoreIdentifier store) {\r
- return storeContext.get(store);\r
}\r
\r
private class DataProviderSession extends DataConsumerSession implements\r
}\r
CommitTransaction transaction = new SequentialCommitTransaction(\r
store, transactions);\r
- return RpcUtils.getRpcResult(successful, transaction, errors);\r
+ return Rpcs.getRpcResult(successful, transaction, errors);\r
}\r
\r
@Override\r
break;\r
}\r
\r
- return RpcUtils.getRpcResult(successful, null, errors);\r
+ return Rpcs.getRpcResult(successful, null, errors);\r
}\r
\r
@Override\r
break;\r
}\r
\r
- return RpcUtils.getRpcResult(successful, null, errors);\r
+ return Rpcs.getRpcResult(successful, null, errors);\r
}\r
\r
@Override\r
break;\r
}\r
\r
- return RpcUtils.getRpcResult(successful, null, errors);\r
+ return Rpcs.getRpcResult(successful, null, errors);\r
}\r
\r
@Override\r
}\r
}\r
}\r
-
import org.opendaylight.controller.sal.core.api.notify.NotificationListener;\r
import org.opendaylight.controller.sal.core.api.notify.NotificationProviderService;\r
import org.opendaylight.controller.sal.core.api.notify.NotificationService;\r
-import org.opendaylight.controller.sal.core.impl.BrokerServiceImpl;\r
import org.opendaylight.controller.sal.core.spi.BrokerModule;\r
import org.opendaylight.controller.yang.common.QName;\r
import org.opendaylight.controller.yang.data.api.CompositeNode;\r
private Multimap<QName, NotificationListener> listeners = HashMultimap\r
.create();\r
\r
- private static final Set<Class<? extends BrokerService>> providedServices = ImmutableSet\r
+ private static final Set<Class<? extends BrokerService>> PROVIDED_SERVICE_TYPE = ImmutableSet\r
.of((Class<? extends BrokerService>) NotificationService.class,\r
NotificationProviderService.class);\r
\r
+ private static final Set<Class<? extends ConsumerFunctionality>> SUPPORTED_CONSUMER_FUNCTIONALITY = ImmutableSet\r
+ .of((Class<? extends ConsumerFunctionality>) NotificationListener.class,\r
+ NotificationListener.class); // Workaround: if we use the\r
+ // version of method with only\r
+ // one argument, the generics\r
+ // inference will not work\r
+\r
@Override\r
public Set<Class<? extends BrokerService>> getProvidedServices() {\r
- return providedServices;\r
+ return PROVIDED_SERVICE_TYPE;\r
}\r
\r
@Override\r
public Set<Class<? extends ConsumerFunctionality>> getSupportedConsumerFunctionality() {\r
- // FIXME Refactor\r
- Set<Class<? extends ConsumerFunctionality>> ret = new HashSet<Class<? extends ConsumerFunctionality>>();\r
- ret.add(NotificationListener.class);\r
- return ret;\r
+ return SUPPORTED_CONSUMER_FUNCTIONALITY;\r
}\r
\r
@Override\r
return new NotificationProviderSessionImpl();\r
}\r
\r
- private class NotificationConsumerSessionImpl extends BrokerServiceImpl\r
- implements NotificationService {\r
+ private class NotificationConsumerSessionImpl implements\r
+ NotificationService {\r
\r
private Multimap<QName, NotificationListener> consumerListeners = HashMultimap\r
.create();\r
+++ /dev/null
-
-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-package org.opendaylight.controller.sal.core.impl;\r
-\r
-import java.util.Set;\r
-\r
-import org.opendaylight.controller.sal.core.api.Provider;\r
-import org.opendaylight.controller.sal.core.api.RpcImplementation;\r
-import org.opendaylight.controller.sal.core.api.Broker.ProviderSession;\r
-import org.opendaylight.controller.yang.common.QName;
-\r
-\r
-public class ProviderSessionImpl extends ConsumerSessionImpl implements\r
- ProviderSession {\r
-\r
- private Provider provider;\r
-\r
- public ProviderSessionImpl(BrokerImpl broker, Provider provider) {\r
- super(broker, null);\r
- this.provider = provider;\r
- }\r
-\r
- @Override\r
- public void addRpcImplementation(QName rpcType,\r
- RpcImplementation implementation) throws IllegalArgumentException {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- @Override\r
- public void removeRpcImplementation(QName rpcType,\r
- RpcImplementation implementation) throws IllegalArgumentException {\r
- // TODO Implement this method\r
- throw new UnsupportedOperationException("Not implemented");\r
- }\r
-\r
- public Provider getProvider() {\r
- return this.provider;\r
- }\r
-\r
-}\r
-
+++ /dev/null
-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-
-package org.opendaylight.controller.sal.core.impl.notify;
\ No newline at end of file
+++ /dev/null
-package org.opendaylight.controller.sal.core.impl.rpc;\r
-\r
-public interface RpcModule {\r
-\r
-}\r
+++ /dev/null
-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-package org.opendaylight.controller.sal.core.impl.rpc;
\ No newline at end of file
--- /dev/null
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
+ <modelVersion>4.0.0</modelVersion>\r
+ <parent>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal</artifactId>\r
+ <version>1.0-SNAPSHOT</version>\r
+ </parent>\r
+ <artifactId>sal-common-util</artifactId>\r
+\r
+ <dependencies>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>yang-common</artifactId>\r
+ <version>1.0</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-common</artifactId>\r
+ <version>1.0-SNAPSHOT</version>\r
+ </dependency>\r
+ </dependencies>
+
+</project>
\ No newline at end of file
* terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
* and is available at http://www.eclipse.org/legal/epl-v10.html\r
*/\r
-package org.opendaylight.controller.sal.core.impl;\r
+package org.opendaylight.controller.sal.common.util;\r
\r
import java.util.ArrayList;\r
import java.util.Collection;\r
import java.util.Collections;\r
-import java.util.List;\r
-import java.util.concurrent.Callable;\r
-\r
-import org.opendaylight.controller.sal.core.api.RpcImplementation;\r
-import org.opendaylight.controller.yang.common.QName;\r
import org.opendaylight.controller.yang.common.RpcError;\r
import org.opendaylight.controller.yang.common.RpcResult;\r
-import org.opendaylight.controller.yang.data.api.CompositeNode;\r
-\r
-\r
-public class RpcUtils {\r
-\r
- Callable<RpcResult<CompositeNode>> callableFor(\r
- final RpcImplementation implemenation, final QName rpc,\r
- final CompositeNode input) {\r
-\r
- return new Callable<RpcResult<CompositeNode>>() {\r
-\r
- @Override\r
- public RpcResult<CompositeNode> call() throws Exception {\r
- return implemenation.invokeRpc(rpc, input);\r
- }\r
- };\r
- }\r
\r
+public class Rpcs {\r
public static <T> RpcResult<T> getRpcResult(boolean successful, T result,\r
- List<RpcError> errors) {\r
+ Collection<RpcError> errors) {\r
RpcResult<T> ret = new RpcResultTO<T>(successful, result, errors);\r
return ret;\r
}\r
private final T result;\r
private final boolean successful;\r
\r
- public RpcResultTO(boolean successful, T result, List<RpcError> errors) {\r
+ public RpcResultTO(boolean successful, T result,\r
+ Collection<RpcError> errors) {\r
this.successful = successful;\r
this.result = result;\r
this.errors = Collections.unmodifiableList(new ArrayList<RpcError>(\r
\r
}\r
}\r
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
- <modelVersion>4.0.0</modelVersion>\r
- <parent>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </parent>\r
- <artifactId>sal-core-api</artifactId>\r
- \r
- <dependencies>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>sal-common</artifactId>\r
- <version>1.0-SNAPSHOT</version>\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>yang-data-api</artifactId>\r
- <version>1.0</version>\r
- </dependency>\r
- <dependency>\r
- <groupId>org.opendaylight.controller</groupId>\r
- <artifactId>yang-model-api</artifactId>\r
- <version>1.0</version>\r
- </dependency>\r
- </dependencies>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">\r
+ <modelVersion>4.0.0</modelVersion>\r
+ <parent>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal</artifactId>\r
+ <version>1.0-SNAPSHOT</version>\r
+ </parent>\r
+ <artifactId>sal-core-api</artifactId>\r
+\r
+ <dependencies>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>sal-common</artifactId>\r
+ <version>1.0-SNAPSHOT</version>\r
+ </dependency>\r
+\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>yang-data-api</artifactId>\r
+ <version>1.0</version>\r
+ </dependency>\r
+ <dependency>\r
+ <groupId>org.opendaylight.controller</groupId>\r
+ <artifactId>yang-model-api</artifactId>\r
+ <version>1.0</version>\r
+ </dependency>\r
+ </dependencies>
</project>
\ No newline at end of file
*/\r
package org.opendaylight.controller.sal.core.api.data;\r
\r
+import java.util.Set;\r
import java.util.concurrent.Future;\r
\r
import org.opendaylight.controller.sal.common.DataStoreIdentifier;\r
*/\r
public interface DataBrokerService extends BrokerService {\r
\r
+ \r
+ Set<DataStoreIdentifier> getDataStores();\r
+ \r
/**\r
* Returns a data from specified Data Store.\r
* \r
--- /dev/null
+/*\r
+ * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
+ *\r
+ * This program and the accompanying materials are made available under the\r
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
+ * and is available at http://www.eclipse.org/legal/epl-v10.html\r
+ */\r
+package org.opendaylight.controller.sal.core.api.model;\r
+\r
+import org.opendaylight.controller.sal.core.api.BrokerService;\r
+import org.opendaylight.controller.yang.model.api.Module;\r
+import org.opendaylight.controller.yang.model.api.SchemaContext;\r
+\r
+public interface SchemaService extends BrokerService {\r
+\r
+ /**\r
+ * Registers a YANG module to session and global context \r
+ * \r
+ * @param module\r
+ */\r
+ void addModule(Module module);\r
+ \r
+ /**\r
+ * Unregisters a YANG module from session context\r
+ * \r
+ * @param module\r
+ */\r
+ void removeModule(Module module);\r
+ \r
+ /**\r
+ * Returns session specific YANG schema context\r
+ * @return\r
+ */\r
+ SchemaContext getSessionContext();\r
+ \r
+ /**\r
+ * Returns global schema context\r
+ * \r
+ * @return\r
+ */\r
+ SchemaContext getGlobalContext();\r
+}\r
import java.io.InputStreamReader;\r
\r
import org.opendaylight.controller.sal.core.impl.BrokerImpl;\r
-import org.opendaylight.controller.sal.core.impl.notify.NotificationModule;\r
+import org.opendaylight.controller.sal.core.impl.NotificationModule;\r
\r
\r
public class SALDemo {\r
enable: true,
type: 'Native',
onMouseEnter: function(node, eventInfo, e) {
- if (node.id != undefined) // if node
+ // if node
+ if (node.id != undefined) {
one.topology.graph.canvas.getElement().style.cursor = 'move';
- else if (eventInfo.edge != undefined && eventInfo.edge.nodeTo.data["$type"] == "swtch" && eventInfo.edge.nodeFrom.data["$type"] == "swtch")
+ } else if (eventInfo.edge != undefined &&
+ eventInfo.edge.nodeTo.data["$type"] == "swtch" &&
+ eventInfo.edge.nodeFrom.data["$type"] == "swtch") {
one.topology.graph.canvas.getElement().style.cursor = 'pointer';
+ }
},
onMouseLeave: function(node, eventInfo, e) {
one.topology.graph.canvas.getElement().style.cursor = '';
$.post('/controller/web/topology/node/' + did, data);
},
onClick: function(node, eventInfo, e) {
- return false;
+ if(one.f.topology === undefined) {
+ return false;
+ } else {
+ one.f.topology.Events.onClick(node, eventInfo);
+ }
}
},
iterations: 200,
style.textAlign = "center";
}
});
+
one.topology.graph.loadJSON(json);
// compute positions incrementally and animate.
one.topology.graph.computeIncremental({
node.setPos(new $jit.Complex(x, y), "end");
}
}
- console.log('done');
+ console.log('done');
one.topology.graph.animate({
modes: ['linear'],
transition: $jit.Trans.Elastic.easeOut,
/** INIT */
$.getJSON(one.global.remoteAddress+"controller/web/topology/visual.json", function(data) {
one.topology.init(data);
-});
\ No newline at end of file
+});