Henry Yu
Committed by Brian O'Connor

RESTCONF Server outline

Change-Id: Id93a647b35b24c47f2828763a799b56a50113faf
Showing 22 changed files with 1290 additions and 0 deletions
......@@ -42,6 +42,7 @@
<module>snmp</module>
<module>bmv2</module>
<module>lisp</module>
<module>restconf</module>
</modules>
<dependencies>
......
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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>
<groupId>org.onosproject</groupId>
<artifactId>onos-protocols</artifactId>
<version>1.8.0-SNAPSHOT</version>
</parent>
<artifactId>onos-restconf</artifactId>
<packaging>pom</packaging>
<modules>
<module>server</module>
</modules>
</project>
BUNDLES = [
'//protocols/restconf/server/api:onos-protocols-restconf-server-api',
'//protocols/restconf/server/restconfmgr:onos-protocols-restconf-server-restconfmgr',
'//protocols/restconf/server/rpp:onos-protocols-restconf-server-rpp',
]
onos_app (
title = 'RESTCONF Server Module',
category = 'Utility',
url = 'http://onosproject.org',
included_bundles = BUNDLES,
)
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:jersey-client',
'//lib:javax.ws.rs-api',
'//lib:jersey-server',
]
osgi_jar_with_tests (
deps = COMPILE_DEPS,
)
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-restconf-server-api</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-serializers</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
</dependencies>
</project>
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf.server.api;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import static javax.ws.rs.core.Response.Status;
/**
* Exceptions raised during RESTCONF operations. This class extends
* WebApplicationException. The design intention is to create a place holder
* for RESTCONF specific errors and to be able to add more functions as the
* subsystem grows.
*/
public class RestconfException extends WebApplicationException {
// This is a randomly generated value. A WebApplicationException class is required to define it.
private static final long SERIAL_VERSION_UID = 3275970397584007046L;
/**
* Constructs a new RESTCONF server error exception. The caller raising this
* exception may pass in a HTTP error status code and an error message. The
* error code will be displayed to the RESTCONF client as part of the
* response from the RESTCONF server. The error message is a string which
* may be saved in a log file and may be later retrieved by the
* getMessage() method.
*
* @param message the detailed error message
* @param status HTTP error status
* @throws IllegalArgumentException in case the status code is null or is not from
* javax.ws.rs.core.Response.Status.Family
* status code family
*/
public RestconfException(String message, Status status) {
super(message, null, Response.status(status).build());
}
/**
* Constructs a new RESTCONF server error exception. The caller raising
* this exception may pass in the numerical value of a HTTP error
* status code, The error code will be displayed to the RESTCONF client
* as a response from the RESTCONF server.
*
* @param status HTTP error status
* @throws IllegalArgumentException in case the status code is not a valid
* HTTP status code or if it is not from the
* javax.ws.rs.core.Response.Status.Family
* status code family
*/
public RestconfException(int status) {
super((Throwable) null, Response.status(status).build());
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf.server.api;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.glassfish.jersey.server.ChunkedOutput;
/**
* Abstraction of RESTCONF Server functionality according to the
* RESTCONF RFC (no official RFC number yet).
*/
public interface RestconfService {
/**
* Processes a GET request against a data resource. The
* target data resource is identified by its URI.
*
* @param uri URI of the target data resource
* @return JSON representation of the data resource
* @throws RestconfException if the GET operation cannot be fulfilled due
* reasons such as the nonexistence of the target
* resource. The proper HTTP error status code is
* enclosed in the exception, so that the caller
* may return it to the RESTCONF client
*/
ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException;
/**
* Processes a POST request against a data resource. The location of
* the target resource is passed in as a URI. And the resource's
* content is passed in as a JSON ObjectNode.
*
* @param uri URI of the data resource to be created
* @param rootNode JSON representation of the data resource
* @throws RestconfException if the POST operation cannot be fulfilled due
* reasons such as wrong URI or syntax error
* in JSON payload. The proper HTTP error status
* code is enclosed in the exception
*/
void runPostOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException;
/**
* Processes a PUT request against a data resource. The location of
* the target resource is passed in as a URI. And the resource's
* content is passed in as a JSON ObjectNode.
*
* @param uri URI of the data resource to be created or updated
* @param rootNode JSON representation of the data resource
* @throws RestconfException if the PUT operation cannot be fulfilled due
* reasons such as wrong URI or syntax error
* in JSON payload. The proper HTTP error status
* code is enclosed in the exception
*/
void runPutOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException;
/**
* Processes the DELETE operation against a data resource. The target
* data resource is identified by its URI.
*
* @param uri URI of the data resource to be deleted
* @throws RestconfException if the DELETE operation cannot be fulfilled due
* reasons such as the nonexistence of the target
* resource. The proper HTTP error status code is
* enclosed in the exception
*/
void runDeleteOperationOnDataResource(String uri) throws RestconfException;
/**
* Retrieves the RESTCONF Root directory.
*
* @return the RESTCONF Root directory
*/
String getRestconfRootPath();
/**
* Handles an Event Stream subscription request. This function creates
* a worker thread to listen to events and writes to a ChunkedOutput,
* which is passed in from the caller. (The worker thread blocks if
* no events arrive.) The ChuckedOutput is a pipe to which this
* function acts as the writer and the caller the reader.
*
* @param streamId ID of the RESTCONF stream to subscribe
* @param output A string data stream
* @throws RestconfException if the Event Stream cannot be subscribed due to
* reasons such as the nonexistence of the target
* stream or unable to allocate any free worker
* thread to handle the request
*/
void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException;
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* RESTCONF Server Public Interface. All public interfaces/APIs that might be used by
* external applications should be packaged here.
*/
package org.onosproject.protocol.restconf.server.api;
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<app name="org.onosproject.protocols.restconfserver" origin="ON.Lab" version="${project.version}"
category="Utility" url="http://onosproject.org" title="RESTCONF Service Module App"
featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
features="${project.artifactId}" >
<description>${project.description}</description>
<artifact>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-restconf-server-restconfmanager/${project.version}</artifact>
<artifact>mvn:${project.groupId}/onos-restconf-server-rpp/${project.version}</artifact>
</app>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<feature>onos-api</feature>
<bundle>mvn:${project.groupId}/onos-restconf-server-api/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-restconf-server-restconfmanager/${project.version}</bundle>
<bundle>mvn:${project.groupId}/onos-restconf-server-rpp/${project.version}</bundle>
</feature>
</features>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-restconf-server-app</artifactId>
<packaging>pom</packaging>
<properties>
<onos.app.readme>RESTCONF Server Module main Application.</onos.app.readme>
</properties>
<description>RESTCONF Service Module main Application</description>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server-restconfmanager</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server-rpp</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-restconf-server</artifactId>
<packaging>pom</packaging>
<modules>
<module>api</module>
<module>restconfmgr</module>
<module>rpp</module>
<module>app</module>
</modules>
<description>RESTCONF Server Module</description>
</project>
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:jersey-client',
'//lib:jersey-server',
'//lib:javax.ws.rs-api',
'//utils/rest:onlab-rest',
'//core/store/serializers:onos-core-serializers',
'//protocols/restconf/server/api:onos-protocols-restconf-server-api',
]
osgi_jar_with_tests (
deps = COMPILE_DEPS,
)
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-restconf-server-restconfmanager</artifactId>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-rest</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-rest</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-misc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf.server.restconfmanager;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
import org.glassfish.jersey.server.ChunkedOutput;
import org.onosproject.event.ListenerTracker;
import org.onosproject.protocol.restconf.server.api.RestconfException;
import org.onosproject.protocol.restconf.server.api.RestconfService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/*
* Skeletal ONOS RESTCONF Server application. The RESTCONF Manager
* implements the main logic of the RESTCONF Server.
*
* The design of the RESTCONF subsystem contains 2 major bundles:
*
* 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a JAX-RS application.
* It acts as the frond-end of the the RESTCONF server. It handles
* HTTP requests that are sent to the RESTCONF Root Path. It then calls the RESTCONF Manager
* to process the requests.
*
* 2. RESTCONF Manager. This is the back-end. It provides the main logic of the RESTCONF server.
* It calls the YMS (YANG Management System) to operate on the YANG data objects.
*/
/**
* Implementation of the RestconfService interface. The class is designed
* as a Apache Flex component. Note that to avoid unnecessary
* activation, the @Component annotation's immediate parameter is set to false.
* So the component is not activated until a RESTCONF request is received by
* the RESTCONF Protocol Proxy (RPP) module, which consumes the service.
*/
@Component(immediate = false)
@Service
public class RestconfManager implements RestconfService {
private static final String RESTCONF_ROOT = "/onos/restconf";
private static final int THREAD_TERMINATION_TIMEOUT = 10;
private static final String EOL = String.format("%n");
private final int maxNumOfWorkerThreads = 5;
private final Logger log = LoggerFactory.getLogger(getClass());
//TODO: YMS service
//@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
//protected YmsService ymsService;
private ListenerTracker listeners;
private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList =
new ConcurrentHashMap<String, BlockingQueue<ObjectNode>>();
private ExecutorService workerThreadPool;
@Activate
protected void activate() {
workerThreadPool = Executors.newFixedThreadPool(maxNumOfWorkerThreads,
new ThreadFactoryBuilder()
.setNameFormat("restconf-worker")
.build());
listeners = new ListenerTracker();
//TODO: YMS notification
//listeners.addListener(ymsService, new InternalYangNotificationListener());
log.info("Started");
}
@Deactivate
protected void deactivate() {
listeners.removeListeners();
shutdownAndAwaitTermination(workerThreadPool);
log.info("Stopped");
}
@Override
public ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException {
//TODO: YMS integration
return null;
}
@Override
public void runPostOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException {
//TODO: YMS integration
}
@Override
public void runPutOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException {
//TODO: YMS integration
}
/**
* Process the delete operation on a data resource.
*
* @param uri URI of the data resource to be deleted.
*/
@Override
public void runDeleteOperationOnDataResource(String uri) throws RestconfException {
//TODO: YMS integration
}
@Override
public String getRestconfRootPath() {
return this.RESTCONF_ROOT;
}
/**
* Creates a worker thread to listen to events and write to chunkedOutput.
* The worker thread blocks if no events arrive.
*
* @param streamId ID of the RESTCONF stream to subscribe
* @param output A string data stream
* @throws RestconfException if the worker thread fails to create
*/
@Override
public void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException {
BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<ObjectNode>();
if (workerThreadPool instanceof ThreadPoolExecutor) {
if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >= maxNumOfWorkerThreads) {
throw new RestconfException("no more work threads left to handle event subscription",
Response.Status.INTERNAL_SERVER_ERROR);
}
} else {
throw new RestconfException("Server ERROR: workerThreadPool NOT instanceof ThreadPoolExecutor",
Response.Status.INTERNAL_SERVER_ERROR);
}
workerThreadPool.submit(new EventConsumer(output, eventQueue));
}
/**
* Shutdown a pool cleanly if possible.
*
* @param pool an executorService
*/
private void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, TimeUnit.SECONDS)) {
log.error("Pool did not terminate");
}
}
} catch (Exception ie) {
// (Re-)Cancel if current thread also interrupted
pool.shutdownNow();
// Preserve interrupt status
Thread.currentThread().interrupt();
}
}
private class EventConsumer implements Runnable {
private final String queueId;
private final ChunkedOutput<String> output;
private final BlockingQueue<ObjectNode> bqueue;
public EventConsumer(ChunkedOutput<String> output, BlockingQueue<ObjectNode> q) {
this.queueId = Thread.currentThread().getName();
this.output = output;
this.bqueue = q;
eventQueueList.put(queueId, bqueue);
}
@Override
public void run() {
try {
ObjectNode chunk;
while ((chunk = bqueue.take()) != null) {
output.write(chunk.toString().concat(EOL));
}
} catch (IOException e) {
log.debug("chunkedOuput is closed: {}", this.bqueue.toString());
/*
* Remove queue from the queue list, so that the event producer
* (i.e., listener) would stop working.
*/
eventQueueList.remove(this.queueId);
} catch (InterruptedException e) {
log.error("ERROR: EventConsumer: bqueue.take() has been interrupted.");
log.debug("EventConsumer Exception:", e);
} finally {
try {
output.close();
log.debug("EventConsumer thread terminated: {}", queueId);
} catch (IOException e) {
log.error("ERROR: EventConsumer: ", e);
}
}
}
}
/**
* The listener class acts as the event producer for the event queues. The
* queues are created by the event consumer threads and are removed when the
* threads terminate.
*/
//TODO: YMS notification
/*private class InternalYangNotificationListener implements YangNotificationListener {
@Override
public void event(YangNotificationEvent event) {
if (event.type() != YangNotificationEvent.Type.YANG_NOTIFICATION) {
// For now, we only handle YANG notification events.
return;
}
if (eventQueueList.isEmpty()) {
*//*
* There is no consumer waiting to consume, so don't have to
* produce this event.
*//*
return;
}
try {
*//*
* Put the event to every queue out there. Each queue is
* corresponding to an event stream session. The queue is
* removed when the session terminates.
*//*
for (Entry<String, BlockingQueue<ObjectNode>> entry : eventQueueList
.entrySet()) {
entry.getValue().put(event.subject().getData());
}
} catch (InterruptedException e) {
Log.error("ERROR", e);
throw new RestconfException("queue", Status.INTERNAL_SERVER_ERROR);
}
}
}*/
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* RESTCONF Service Manager implementation.
*/
package org.onosproject.protocol.restconf.server.restconfmanager;
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:jersey-client',
'//lib:jersey-server',
'//lib:javax.ws.rs-api',
'//utils/rest:onlab-rest',
'//protocols/restconf/server/api:onos-protocols-restconf-server-api',
'//protocols/restconf/server/restconfmgr:onos-protocols-restconf-server-restconfmgr',
]
osgi_jar_with_tests (
deps = COMPILE_DEPS,
)
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server</artifactId>
<version>1.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-restconf-server-rpp</artifactId>
<packaging>bundle</packaging>
<properties>
<web.context>/onos/restconf</web.context>
<api.version>1.0.0</api.version>
<api.title>YANG RESTCONF Protocol Stack</api.title>
<api.description>
RESTCONF Protocol Proxy
</api.description>
<api.package>org.onosproject.protocol.restconf.server.rpp</api.package>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-restconf-server-restconfmanager</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-rest</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-rest</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-app-dhcp-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-misc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
<version>1.9.8</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<_wab>src/main/webapp/</_wab>
<Include-Resource>
WEB-INF/classes/apidoc/swagger.json=target/swagger.json,
{maven-resources}
</Include-Resource>
<Bundle-SymbolicName>
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
*,org.glassfish.jersey.servlet
</Import-Package>
<Web-ContextPath>${web.context}</Web-ContextPath>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf.server.rpp;
import org.onlab.rest.AbstractWebApplication;
import java.util.Set;
/**
* RESTCONF Server front-end application.
*/
public class RestconfProtocolProxy extends AbstractWebApplication {
@Override
public Set<Class<?>> getClasses() {
return getClasses(RestconfWebResource.class);
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.protocol.restconf.server.rpp;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.glassfish.jersey.server.ChunkedOutput;
import org.onosproject.protocol.restconf.server.api.RestconfException;
import org.onosproject.protocol.restconf.server.api.RestconfService;
import org.onosproject.rest.AbstractWebResource;
import org.slf4j.Logger;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.io.InputStream;
import static org.slf4j.LoggerFactory.getLogger;
/*
* This class is the main implementation of the RESTCONF Protocol
* Proxy module. Currently it only handles some basic operations
* on data resource nodes. However, the design intention is to
* create a code structure that allows new methods/functionality
* to be easily added in future releases.
*/
/**
* Implementation of the RESTCONF Protocol Proxy module.
*/
@Path("/")
public class RestconfWebResource extends AbstractWebResource {
@Context
UriInfo uriInfo;
private final RestconfService service = get(RestconfService.class);
private final Logger log = getLogger(getClass());
/**
* Handles a RESTCONF GET operation against a target data resource. If the
* operation is successful, the JSON presentation of the resource plus HTTP
* status code "200 OK" is returned. Otherwise, HTTP error status code
* "400 Bad Request" is returned.
*
* @param uriString URI of the data resource.
* @return HTTP response
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("data/{identifier : .+}")
public Response handleGetRequest(@PathParam("identifier") String uriString) {
log.debug("handleGetRequest: {}", uriString);
try {
ObjectNode node = service.runGetOperationOnDataResource(uriString);
return ok(node).build();
} catch (RestconfException e) {
log.error("ERROR: handleGetRequest: {}", e.getMessage());
log.debug("Exception in handleGetRequest:", e);
return e.getResponse();
}
}
/**
* Handles the RESTCONF Event Notification Subscription request. If the
* subscription is successful, a ChunkedOutput stream is created and returned
* to the caller.
* <P></P>
* This function is not blocked on streaming the data (so that it can handle
* other incoming requests). Instead, a worker thread running in the background
* does the data streaming. If errors occur during streaming, the worker thread
* calls ChunkedOutput.close() to disconnect the session and terminates itself.
*
* @param streamId Event stream ID
* @return A string data stream over HTTP keep-alive session
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("streams/{streamId}")
public ChunkedOutput<String> handleNotificationRegistration(@PathParam("streamId") String streamId) {
final ChunkedOutput<String> output = new ChunkedOutput<String>(String.class);
try {
service.subscribeEventStream(streamId, output);
} catch (RestconfException e) {
log.error("ERROR: handleNotificationRegistration: {}", e.getMessage());
log.debug("Exception in handleNotificationRegistration:", e);
try {
output.close();
} catch (IOException ex) {
log.error("ERROR: handleNotificationRegistration:", ex);
}
}
return output;
}
/**
* Handles a RESTCONF POST operation against a data resource. If the
* operation is successful, HTTP status code "201 Created" is returned
* and there is no response message-body. If the data resource already
* exists, then the HTTP status code "409 Conflict" is returned.
*
* @param uriString URI of the data resource
* @param stream Input JSON object
* @return HTTP response
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("data/{identifier : .+}")
public Response handlePostRequest(@PathParam("identifier") String uriString, InputStream stream) {
log.debug("handlePostRequest: {}", uriString);
try {
ObjectNode rootNode = (ObjectNode) mapper().readTree(stream);
service.runPostOperationOnDataResource(uriString, rootNode);
return Response.created(uriInfo.getRequestUri()).build();
} catch (JsonProcessingException e) {
log.error("ERROR: handlePostRequest ", e);
return Response.status(Response.Status.BAD_REQUEST).build();
} catch (RestconfException e) {
log.error("ERROR: handlePostRequest: {}", e.getMessage());
log.debug("Exception in handlePostRequest:", e);
return e.getResponse();
} catch (IOException ex) {
log.error("ERROR: handlePostRequest ", ex);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
/**
* Handles a RESTCONF PUT operation against a data resource. If a new
* resource is successfully created, then the HTTP status code "201 Created"
* is returned. If an existing resource is modified, then the HTTP
* status code "204 No Content" is returned. If the input JSON payload
* contains errors, then "400 Bad Request" is returned. If an exception
* occurs during the operation, the status code enclosed in
* the RestconfException object, such as "500 Internal Server Error",
* is returned.
*
* @param uriString URI of the data resource.
* @param stream Input JSON object
* @return HTTP response
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("data/{identifier : .+}")
public Response handlePutRequest(@PathParam("identifier") String uriString, InputStream stream) {
log.debug("handlePutRequest: {}", uriString);
try {
ObjectNode rootNode = (ObjectNode) mapper().readTree(stream);
service.runPutOperationOnDataResource(uriString, rootNode);
return Response.created(uriInfo.getRequestUri()).build();
} catch (JsonProcessingException e) {
log.error("ERROR: handlePutRequest ", e);
return Response.status(Response.Status.BAD_REQUEST).build();
} catch (RestconfException e) {
log.error("ERROR: handlePutRequest: {}", e.getMessage());
log.debug("Exception in handlePutRequest:", e);
return e.getResponse();
} catch (IOException ex) {
log.error("ERROR: handlePutRequest ", ex);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
/**
* Handles the RESTCONF DELETION Operation against a data resource. If the
* resource is successfully deleted, the HTTP status code "204 No Content"
* is returned in the response. If an exception occurs, then the
* HTTP status code enclosed in the RestconfException object is
* returned.
*
* @param uriString URI of the data resource to be deleted.
* @return HTTP response
*/
@DELETE
@Produces(MediaType.APPLICATION_JSON)
@Path("data/{identifier : .+}")
public Response handleDeleteRequest(@PathParam("identifier") String uriString) {
log.debug("handleDeleteRequest: {}", uriString);
try {
service.runDeleteOperationOnDataResource(uriString);
return Response.ok().build();
} catch (RestconfException e) {
log.error("ERROR: handleDeleteRequest: {}", e.getMessage());
log.debug("Exception in handleDeleteRequest:", e);
return e.getResponse();
}
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* RESTCONF Protocol Proxy implementation.
*/
package org.onosproject.protocol.restconf.server.rpp;
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="ONOS" version="2.5">
<display-name>ONOS RESTCONF Protocol Proxy</display-name>
<servlet>
<servlet-name>RESTCONF Protocol Proxy</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.onosproject.protocol.restconf.server.rpp.RestconfProtocolProxy</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>RESTCONF Protocol Proxy</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>