Thomas Vachuska
Committed by Ray Milkey

Initial sketch of codecs and REST API approach.

FIxed typos and defects.
ONOS-81

Change-Id: I789444a181abea509c354966545c927e305710d1
Showing 28 changed files with 985 additions and 22 deletions
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.codec;
17 +
18 +import com.fasterxml.jackson.databind.ObjectMapper;
19 +
20 +/**
21 + * Context for codecs to use while encoding/decoding.
22 + */
23 +public interface CodecContext {
24 +
25 + /**
26 + * Returns the JSON object mapper.
27 + *
28 + * @return object mapper
29 + */
30 + ObjectMapper mapper();
31 +
32 + /**
33 + * Returns the JSON codec for the specified entity class.
34 + *
35 + * @param entityClass entity class
36 + * @param <T> entity type
37 + * @return JSON codec; null if no codec available for the class
38 + */
39 + <T> JsonCodec<T> codec(Class<T> entityClass);
40 +
41 + /**
42 + * Returns reference to the specified service implementation.
43 + *
44 + * @param serviceClass service class
45 + * @param <T> service type
46 + * @return JSON codec; null if no codec available for the class
47 + */
48 + <T> T get(Class<T> serviceClass);
49 +
50 +}
...@@ -33,9 +33,10 @@ public interface CodecService { ...@@ -33,9 +33,10 @@ public interface CodecService {
33 * Returns the JSON codec for the specified entity class. 33 * Returns the JSON codec for the specified entity class.
34 * 34 *
35 * @param entityClass entity class 35 * @param entityClass entity class
36 + * @param <T> entity type
36 * @return JSON codec; null if no codec available for the class 37 * @return JSON codec; null if no codec available for the class
37 */ 38 */
38 - JsonCodec getCodec(Class<?> entityClass); 39 + <T> JsonCodec<T> getCodec(Class<T> entityClass);
39 40
40 /** 41 /**
41 * Registers the specified JSON codec for the given entity class. 42 * Registers the specified JSON codec for the given entity class.
...@@ -43,7 +44,7 @@ public interface CodecService { ...@@ -43,7 +44,7 @@ public interface CodecService {
43 * @param entityClass entity class 44 * @param entityClass entity class
44 * @param codec JSON codec 45 * @param codec JSON codec
45 */ 46 */
46 - void registerCodec(Class<?> entityClass, JsonCodec codec); 47 + <T> void registerCodec(Class<T> entityClass, JsonCodec<T> codec);
47 48
48 /** 49 /**
49 * Unregisters the JSON codec for the specified entity class. 50 * Unregisters the JSON codec for the specified entity class.
......
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
16 package org.onlab.onos.codec; 16 package org.onlab.onos.codec;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 -import com.fasterxml.jackson.databind.ObjectMapper;
20 import com.fasterxml.jackson.databind.node.ArrayNode; 19 import com.fasterxml.jackson.databind.node.ArrayNode;
21 import com.fasterxml.jackson.databind.node.ObjectNode; 20 import com.fasterxml.jackson.databind.node.ObjectNode;
22 21
...@@ -32,36 +31,41 @@ public abstract class JsonCodec<T> { ...@@ -32,36 +31,41 @@ public abstract class JsonCodec<T> {
32 * Encodes the specified entity into JSON. 31 * Encodes the specified entity into JSON.
33 * 32 *
34 * @param entity entity to encode 33 * @param entity entity to encode
35 - * @param mapper object mapper 34 + * @param context encoding context
36 * @return JSON node 35 * @return JSON node
37 * @throws java.lang.UnsupportedOperationException if the codec does not 36 * @throws java.lang.UnsupportedOperationException if the codec does not
38 * support encode operations 37 * support encode operations
39 */ 38 */
40 - public abstract ObjectNode encode(T entity, ObjectMapper mapper); 39 + public ObjectNode encode(T entity, CodecContext context) {
40 + throw new UnsupportedOperationException("encode() not supported");
41 + }
41 42
42 /** 43 /**
43 * Decodes the specified entity from JSON. 44 * Decodes the specified entity from JSON.
44 * 45 *
45 * @param json JSON to decode 46 * @param json JSON to decode
47 + * @param context decoding context
46 * @return decoded entity 48 * @return decoded entity
47 * @throws java.lang.UnsupportedOperationException if the codec does not 49 * @throws java.lang.UnsupportedOperationException if the codec does not
48 * support decode operations 50 * support decode operations
49 */ 51 */
50 - public abstract T decode(ObjectNode json); 52 + public T decode(ObjectNode json, CodecContext context) {
53 + throw new UnsupportedOperationException("decode() not supported");
54 + }
51 55
52 /** 56 /**
53 * Encodes the collection of the specified entities. 57 * Encodes the collection of the specified entities.
54 * 58 *
55 * @param entities collection of entities to encode 59 * @param entities collection of entities to encode
56 - * @param mapper object mapper 60 + * @param context encoding context
57 * @return JSON array 61 * @return JSON array
58 * @throws java.lang.UnsupportedOperationException if the codec does not 62 * @throws java.lang.UnsupportedOperationException if the codec does not
59 * support encode operations 63 * support encode operations
60 */ 64 */
61 - public ArrayNode encode(Iterable<T> entities, ObjectMapper mapper) { 65 + public ArrayNode encode(Iterable<T> entities, CodecContext context) {
62 - ArrayNode result = mapper.createArrayNode(); 66 + ArrayNode result = context.mapper().createArrayNode();
63 for (T entity : entities) { 67 for (T entity : entities) {
64 - result.add(encode(entity, mapper)); 68 + result.add(encode(entity, context));
65 } 69 }
66 return result; 70 return result;
67 } 71 }
...@@ -70,14 +74,15 @@ public abstract class JsonCodec<T> { ...@@ -70,14 +74,15 @@ public abstract class JsonCodec<T> {
70 * Decodes the specified JSON array into a collection of entities. 74 * Decodes the specified JSON array into a collection of entities.
71 * 75 *
72 * @param json JSON array to decode 76 * @param json JSON array to decode
77 + * @param context decoding context
73 * @return collection of decoded entities 78 * @return collection of decoded entities
74 * @throws java.lang.UnsupportedOperationException if the codec does not 79 * @throws java.lang.UnsupportedOperationException if the codec does not
75 * support decode operations 80 * support decode operations
76 */ 81 */
77 - public List<T> decode(ArrayNode json) { 82 + public List<T> decode(ArrayNode json, CodecContext context) {
78 List<T> result = new ArrayList<>(); 83 List<T> result = new ArrayList<>();
79 for (JsonNode node : json) { 84 for (JsonNode node : json) {
80 - result.add(decode((ObjectNode) node)); 85 + result.add(decode((ObjectNode) node, context));
81 } 86 }
82 return result; 87 return result;
83 } 88 }
......
...@@ -92,6 +92,8 @@ public interface LinkService { ...@@ -92,6 +92,8 @@ public interface LinkService {
92 */ 92 */
93 Set<Link> getIngressLinks(ConnectPoint connectPoint); 93 Set<Link> getIngressLinks(ConnectPoint connectPoint);
94 94
95 + // FIXME: I don't think this makes sense; discuss and remove or adjust return
96 + // to be a Set<Link> or add Link.Type parameter
95 /** 97 /**
96 * Returns the infrastructure links between the specified source 98 * Returns the infrastructure links between the specified source
97 * and destination connection points. 99 * and destination connection points.
......
...@@ -58,12 +58,12 @@ public class JsonCodecTest { ...@@ -58,12 +58,12 @@ public class JsonCodecTest {
58 58
59 private static class FooCodec extends JsonCodec<Foo> { 59 private static class FooCodec extends JsonCodec<Foo> {
60 @Override 60 @Override
61 - public ObjectNode encode(Foo entity, ObjectMapper mapper) { 61 + public ObjectNode encode(Foo entity, CodecContext context) {
62 - return mapper.createObjectNode().put("name", entity.name); 62 + return context.mapper().createObjectNode().put("name", entity.name);
63 } 63 }
64 64
65 @Override 65 @Override
66 - public Foo decode(ObjectNode json) { 66 + public Foo decode(ObjectNode json, CodecContext context) {
67 return new Foo(json.get("name").asText()); 67 return new Foo(json.get("name").asText());
68 } 68 }
69 } 69 }
...@@ -74,9 +74,26 @@ public class JsonCodecTest { ...@@ -74,9 +74,26 @@ public class JsonCodecTest {
74 Foo f2 = new Foo("bar"); 74 Foo f2 = new Foo("bar");
75 FooCodec codec = new FooCodec(); 75 FooCodec codec = new FooCodec();
76 ImmutableList<Foo> entities = ImmutableList.of(f1, f2); 76 ImmutableList<Foo> entities = ImmutableList.of(f1, f2);
77 - ArrayNode json = codec.encode(entities, new ObjectMapper()); 77 + ArrayNode json = codec.encode(entities, new TestContext());
78 - List<Foo> foos = codec.decode(json); 78 + List<Foo> foos = codec.decode(json, new TestContext());
79 assertEquals("incorrect encode/decode", entities, foos); 79 assertEquals("incorrect encode/decode", entities, foos);
80 } 80 }
81 81
82 + private class TestContext implements CodecContext {
83 + private ObjectMapper mapper = new ObjectMapper();
84 + @Override
85 + public ObjectMapper mapper() {
86 + return mapper;
87 + }
88 +
89 + @Override
90 + public <T> JsonCodec<T> codec(Class<T> entityClass) {
91 + return null;
92 + }
93 +
94 + @Override
95 + public <T> T get(Class<T> serviceClass) {
96 + return null;
97 + }
98 + }
82 } 99 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -80,7 +80,7 @@ ...@@ -80,7 +80,7 @@
80 <group> 80 <group>
81 <title>GUI, REST &amp; Command-Line</title> 81 <title>GUI, REST &amp; Command-Line</title>
82 <packages> 82 <packages>
83 - org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli:org.onlab.onos.gui.*:org.onlab.onos.rest.*:org.onlab.onos.cli.* 83 + org.onlab.onos.gui:org.onlab.onos.rest:org.onlab.onos.cli:org.onlab.onos.gui.*:org.onlab.onos.rest.*:org.onlab.onos.cli.*:org.onlab.onos.codec.impl
84 </packages> 84 </packages>
85 </group> 85 </group>
86 <group> 86 <group>
......
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.api;
17 +
18 +/**
19 + * Represents condition where an item is not found or not available.
20 + */
21 +public class ItemNotFoundException extends RuntimeException {
22 +
23 + /**
24 + * Creates a new exception with no message.
25 + */
26 + public ItemNotFoundException() {
27 + }
28 +
29 + /**
30 + * Creates a new exception with the supplied message.
31 + * @param message error message
32 + */
33 + public ItemNotFoundException(String message) {
34 + super(message);
35 + }
36 +
37 + /**
38 + * Creates a new exception with the supplied message and cause.
39 + * @param message error message
40 + * @param cause cause of the error
41 + */
42 + public ItemNotFoundException(String message, Throwable cause) {
43 + super(message, cause);
44 + }
45 +
46 +}
...@@ -18,6 +18,8 @@ package org.onlab.rest; ...@@ -18,6 +18,8 @@ package org.onlab.rest;
18 import org.onlab.osgi.DefaultServiceDirectory; 18 import org.onlab.osgi.DefaultServiceDirectory;
19 import org.onlab.osgi.ServiceDirectory; 19 import org.onlab.osgi.ServiceDirectory;
20 20
21 +import javax.ws.rs.core.Response;
22 +
21 /** 23 /**
22 * Base abstraction of a JAX-RS resource. 24 * Base abstraction of a JAX-RS resource.
23 */ 25 */
...@@ -44,8 +46,12 @@ public abstract class BaseResource { ...@@ -44,8 +46,12 @@ public abstract class BaseResource {
44 * @param <T> type of service 46 * @param <T> type of service
45 * @return service implementation 47 * @return service implementation
46 */ 48 */
47 - protected static <T> T get(Class<T> service) { 49 + public <T> T get(Class<T> service) {
48 return services.get(service); 50 return services.get(service);
49 } 51 }
50 52
53 + protected static Response.ResponseBuilder ok(Object obj) {
54 + return Response.ok(obj);
55 + }
56 +
51 } 57 }
......
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onlab.onos.codec.CodecContext;
20 +import org.onlab.onos.codec.JsonCodec;
21 +import org.onlab.onos.net.Annotated;
22 +import org.onlab.onos.net.Annotations;
23 +
24 +/**
25 + * Base JSON codec for annotated entities.
26 + */
27 +public abstract class AnnotatedCodec<T extends Annotated> extends JsonCodec<T> {
28 +
29 + /**
30 + * Adds JSON encoding of the given item annotations to the specified node.
31 + *
32 + * @param node node to add annotations to
33 + * @param entity annotated entity
34 + * @param context encode context
35 + * @return the given node
36 + */
37 + protected ObjectNode annotate(ObjectNode node, T entity, CodecContext context) {
38 + if (!entity.annotations().keys().isEmpty()) {
39 + JsonCodec<Annotations> codec = context.codec(Annotations.class);
40 + node.set("annotations", codec.encode(entity.annotations(), context));
41 + }
42 + return node;
43 + }
44 +
45 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onlab.onos.codec.CodecContext;
20 +import org.onlab.onos.codec.JsonCodec;
21 +import org.onlab.onos.net.Annotations;
22 +
23 +/**
24 + * Annotations JSON codec.
25 + */
26 +public class AnnotationsCodec extends JsonCodec<Annotations> {
27 +
28 + @Override
29 + public ObjectNode encode(Annotations annotations, CodecContext context) {
30 + ObjectNode result = context.mapper().createObjectNode();
31 + for (String key : annotations.keys()) {
32 + result.put(key, annotations.value(key));
33 + }
34 + return result;
35 + }
36 +
37 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.codec.impl;
17 +
18 +import com.google.common.collect.ImmutableSet;
19 +import org.apache.felix.scr.annotations.Activate;
20 +import org.apache.felix.scr.annotations.Component;
21 +import org.apache.felix.scr.annotations.Deactivate;
22 +import org.apache.felix.scr.annotations.Service;
23 +import org.onlab.onos.codec.CodecService;
24 +import org.onlab.onos.codec.JsonCodec;
25 +import org.onlab.onos.net.Annotations;
26 +import org.onlab.onos.net.ConnectPoint;
27 +import org.onlab.onos.net.Device;
28 +import org.onlab.onos.net.Link;
29 +import org.onlab.onos.net.Port;
30 +import org.slf4j.Logger;
31 +import org.slf4j.LoggerFactory;
32 +
33 +import java.util.Map;
34 +import java.util.Set;
35 +import java.util.concurrent.ConcurrentHashMap;
36 +
37 +/**
38 + * Implementation of the JSON codec brokering service.
39 + */
40 +@Component(immediate = true)
41 +@Service
42 +public class CodecManager implements CodecService {
43 +
44 + private static Logger log = LoggerFactory.getLogger(CodecManager.class);
45 +
46 + private final Map<Class<?>, JsonCodec> codecs = new ConcurrentHashMap<>();
47 +
48 + @Activate
49 + public void activate() {
50 + codecs.clear();
51 + registerCodec(Annotations.class, new AnnotationsCodec());
52 + registerCodec(Device.class, new DeviceCodec());
53 + registerCodec(Port.class, new PortCodec());
54 + registerCodec(ConnectPoint.class, new ConnectPointCodec());
55 + registerCodec(Link.class, new LinkCodec());
56 + log.info("Started");
57 + }
58 +
59 + @Deactivate
60 + public void deativate() {
61 + codecs.clear();
62 + log.info("Stopped");
63 + }
64 +
65 + @Override
66 + public Set<Class<?>> getCodecs() {
67 + return ImmutableSet.copyOf(codecs.keySet());
68 + }
69 +
70 + @Override
71 + @SuppressWarnings("unchecked")
72 + public <T> JsonCodec<T> getCodec(Class<T> entityClass) {
73 + return codecs.get(entityClass);
74 + }
75 +
76 + @Override
77 + public <T> void registerCodec(Class<T> entityClass, JsonCodec<T> codec) {
78 + codecs.putIfAbsent(entityClass, codec);
79 + }
80 +
81 + @Override
82 + public void unregisterCodec(Class<?> entityClass) {
83 + codecs.remove(entityClass);
84 + }
85 +
86 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onlab.onos.codec.CodecContext;
20 +import org.onlab.onos.codec.JsonCodec;
21 +import org.onlab.onos.net.ConnectPoint;
22 +
23 +import static com.google.common.base.Preconditions.checkNotNull;
24 +
25 +/**
26 + * Connection point JSON codec.
27 + */
28 +public class ConnectPointCodec extends JsonCodec<ConnectPoint> {
29 +
30 + @Override
31 + public ObjectNode encode(ConnectPoint point, CodecContext context) {
32 + checkNotNull(point, "Connect point cannot be null");
33 + return context.mapper().createObjectNode()
34 + .put("device", point.deviceId().toString())
35 + .put("port", point.port().toString());
36 + }
37 +
38 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onlab.onos.codec.CodecContext;
20 +import org.onlab.onos.net.Device;
21 +import org.onlab.onos.net.device.DeviceService;
22 +
23 +import static com.google.common.base.Preconditions.checkNotNull;
24 +
25 +/**
26 + * Device JSON codec.
27 + */
28 +public class DeviceCodec extends AnnotatedCodec<Device> {
29 +
30 + @Override
31 + public ObjectNode encode(Device device, CodecContext context) {
32 + checkNotNull(device, "Device cannot be null");
33 + DeviceService service = context.get(DeviceService.class);
34 + ObjectNode result = context.mapper().createObjectNode()
35 + .put("id", device.id().toString())
36 + .put("available", service.isAvailable(device.id()))
37 + .put("role", service.getRole(device.id()).toString())
38 + .put("mfr", device.manufacturer())
39 + .put("hw", device.hwVersion())
40 + .put("sw", device.swVersion())
41 + .put("serial", device.serialNumber());
42 + return annotate(result, device, context);
43 + }
44 +
45 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onlab.onos.codec.CodecContext;
20 +import org.onlab.onos.codec.JsonCodec;
21 +import org.onlab.onos.net.ConnectPoint;
22 +import org.onlab.onos.net.Link;
23 +import org.onlab.onos.net.device.DeviceService;
24 +
25 +import static com.google.common.base.Preconditions.checkNotNull;
26 +
27 +/**
28 + * Link JSON codec.
29 + */
30 +public class LinkCodec extends AnnotatedCodec<Link> {
31 +
32 + @Override
33 + public ObjectNode encode(Link link, CodecContext context) {
34 + checkNotNull(link, "Link cannot be null");
35 + DeviceService service = context.get(DeviceService.class);
36 + JsonCodec<ConnectPoint> codec = context.codec(ConnectPoint.class);
37 + ObjectNode result = context.mapper().createObjectNode();
38 + result.set("src", codec.encode(link.src(), context));
39 + result.set("dst", codec.encode(link.dst(), context));
40 + return annotate(result, link, context);
41 + }
42 +
43 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.codec.impl;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onlab.onos.codec.CodecContext;
20 +import org.onlab.onos.net.Port;
21 +import org.onlab.onos.net.PortNumber;
22 +
23 +import static com.google.common.base.Preconditions.checkNotNull;
24 +
25 +/**
26 + * Device port JSON codec.
27 + */
28 +public class PortCodec extends AnnotatedCodec<Port> {
29 +
30 + @Override
31 + public ObjectNode encode(Port port, CodecContext context) {
32 + checkNotNull(port, "Port cannot be null");
33 + ObjectNode result = context.mapper().createObjectNode()
34 + .put("port", portName(port.number()))
35 + .put("isEnabled", port.isEnabled())
36 + .put("type", port.type().toString().toLowerCase())
37 + .put("portSpeed", port.portSpeed());
38 + return annotate(result, port, context);
39 + }
40 +
41 + private String portName(PortNumber port) {
42 + return port.equals(PortNumber.LOCAL) ? "local" : port.toString();
43 + }
44 +
45 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Implementations of the codec broker and built-in entity JSON codecs.
19 + */
20 +package org.onlab.onos.codec.impl;
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.rest;
17 +
18 +import com.fasterxml.jackson.databind.ObjectMapper;
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +import org.onlab.api.ItemNotFoundException;
21 +import org.onlab.onos.codec.CodecContext;
22 +import org.onlab.onos.codec.CodecService;
23 +import org.onlab.onos.codec.JsonCodec;
24 +import org.onlab.rest.BaseResource;
25 +
26 +/**
27 + * Abstract REST resource.
28 + */
29 +public class AbstractWebResource extends BaseResource implements CodecContext {
30 +
31 + @Override
32 + public ObjectMapper mapper() {
33 + return new ObjectMapper();
34 + }
35 +
36 + /**
37 + * Returns the JSON codec for the specified entity class.
38 + *
39 + * @param entityClass entity class
40 + * @param <T> entity type
41 + * @return JSON codec
42 + */
43 + public <T> JsonCodec<T> codec(Class<T> entityClass) {
44 + return get(CodecService.class).getCodec(entityClass);
45 + }
46 +
47 + /**
48 + * Returns JSON object wrapping the array encoding of the specified
49 + * collection of items.
50 + *
51 + * @param codecClass codec item class
52 + * @param field field holding the array
53 + * @param items collection of items to be encoded into array
54 + * @param <T> item type
55 + * @return JSON object
56 + */
57 + protected <T> ObjectNode encodeArray(Class<T> codecClass, String field,
58 + Iterable<T> items) {
59 + ObjectNode result = mapper().createObjectNode();
60 + result.set(field, codec(codecClass).encode(items, this));
61 + return result;
62 + }
63 +
64 + /**
65 + * Returns the specified item if that items is null; otherwise throws
66 + * not found exception.
67 + *
68 + * @param item item to check
69 + * @param message not found message
70 + * @param <T> item type
71 + * @return item if not null
72 + * @throws org.onlab.api.ItemNotFoundException if item is null
73 + */
74 + protected <T> T nullIsNotFound(T item, String message) {
75 + if (item == null) {
76 + throw new ItemNotFoundException(message);
77 + }
78 + return item;
79 + }
80 +
81 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.rest;
17 +
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 +import org.onlab.onos.net.Device;
20 +import org.onlab.onos.net.Port;
21 +import org.onlab.onos.net.device.DeviceService;
22 +
23 +import javax.ws.rs.GET;
24 +import javax.ws.rs.Path;
25 +import javax.ws.rs.PathParam;
26 +import javax.ws.rs.core.Response;
27 +import java.util.List;
28 +
29 +import static com.google.common.base.Preconditions.checkNotNull;
30 +import static org.onlab.onos.net.DeviceId.deviceId;
31 +
32 +/**
33 + * REST resource for interacting with the inventory of infrastructure devices.
34 + */
35 +@Path("devices")
36 +public class DevicesWebResource extends AbstractWebResource {
37 +
38 + public static final String DEVICE_NOT_FOUND = "Device is not found";
39 +
40 + @GET
41 + public Response getDevices() {
42 + Iterable<Device> devices = get(DeviceService.class).getDevices();
43 + return ok(encodeArray(Device.class, "devices", devices)).build();
44 + }
45 +
46 + @GET
47 + @Path("{id}")
48 + public Response getDevice(@PathParam("id") String id) {
49 + Device device = nullIsNotFound(get(DeviceService.class).getDevice(deviceId(id)),
50 + DEVICE_NOT_FOUND);
51 + return ok(codec(Device.class).encode(device, this)).build();
52 + }
53 +
54 + @GET
55 + @Path("{id}/ports")
56 + public Response getDevicePorts(@PathParam("id") String id) {
57 + DeviceService service = get(DeviceService.class);
58 + Device device = nullIsNotFound(service.getDevice(deviceId(id)), DEVICE_NOT_FOUND);
59 + List<Port> ports = checkNotNull(service.getPorts(deviceId(id)), "Ports could not be retrieved");
60 + ObjectNode result = codec(Device.class).encode(device, this);
61 + result.set("ports", codec(Port.class).encode(ports, this));
62 + return ok(result).build();
63 + }
64 +
65 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.rest;
17 +
18 +import com.fasterxml.jackson.databind.ObjectMapper;
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +
21 +import javax.ws.rs.Produces;
22 +import javax.ws.rs.core.MediaType;
23 +import javax.ws.rs.core.MultivaluedMap;
24 +import javax.ws.rs.ext.MessageBodyWriter;
25 +import java.io.IOException;
26 +import java.io.OutputStream;
27 +import java.lang.annotation.Annotation;
28 +import java.lang.reflect.Type;
29 +
30 +/**
31 + * JAX-RS Response message body writer.
32 + */
33 +@Produces("application/json")
34 +public class JsonBodyWriter implements MessageBodyWriter<ObjectNode> {
35 +
36 + private ObjectMapper mapper = new ObjectMapper();
37 +
38 + @Override
39 + public boolean isWriteable(Class<?> type, Type genericType,
40 + Annotation[] annotations, MediaType mediaType) {
41 + return type == ObjectNode.class;
42 + }
43 +
44 + @Override
45 + public long getSize(ObjectNode node, Class<?> type, Type genericType,
46 + Annotation[] annotations, MediaType mediaType) {
47 + return -1;
48 + }
49 +
50 + @Override
51 + public void writeTo(ObjectNode node, Class<?> type, Type genericType,
52 + Annotation[] annotations, MediaType mediaType,
53 + MultivaluedMap<String, Object> httpHeaders,
54 + OutputStream entityStream) throws IOException {
55 + mapper.writer().writeValue(entityStream, node);
56 + entityStream.flush();
57 + }
58 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.rest;
17 +
18 +import org.onlab.onos.net.ConnectPoint;
19 +import org.onlab.onos.net.DeviceId;
20 +import org.onlab.onos.net.Link;
21 +import org.onlab.onos.net.link.LinkService;
22 +
23 +import javax.ws.rs.GET;
24 +import javax.ws.rs.Path;
25 +import javax.ws.rs.QueryParam;
26 +import javax.ws.rs.core.Response;
27 +
28 +import static org.onlab.onos.net.DeviceId.deviceId;
29 +import static org.onlab.onos.net.PortNumber.portNumber;
30 +import static org.onlab.onos.rest.LinksWebResource.Direction.valueOf;
31 +
32 +/**
33 + * REST resource for interacting with the inventory of infrastructure links.
34 + */
35 +@Path("links")
36 +public class LinksWebResource extends AbstractWebResource {
37 +
38 + enum Direction { ALL, INGRESS, EGRESS }
39 +
40 + @GET
41 + public Response getLinks(@QueryParam("device") String deviceId,
42 + @QueryParam("port") String port,
43 + @QueryParam("direction") String direction) {
44 + LinkService service = get(LinkService.class);
45 + Iterable<Link> links;
46 +
47 + if (deviceId != null && port != null) {
48 + links = getConnectPointLinks(new ConnectPoint(deviceId(deviceId),
49 + portNumber(port)),
50 + direction, service);
51 + } else if (deviceId != null) {
52 + links = getDeviceLinks(deviceId(deviceId), direction, service);
53 + } else {
54 + links = service.getLinks();
55 + }
56 + return ok(encodeArray(Link.class, "links", links)).build();
57 + }
58 +
59 + private Iterable<Link> getConnectPointLinks(ConnectPoint point,
60 + String direction,
61 + LinkService service) {
62 + Direction dir = direction != null ?
63 + valueOf(direction.toUpperCase()) : Direction.ALL;
64 + switch (dir) {
65 + case INGRESS:
66 + return service.getIngressLinks(point);
67 + case EGRESS:
68 + return service.getEgressLinks(point);
69 + default:
70 + return service.getLinks(point);
71 + }
72 + }
73 +
74 + private Iterable<Link> getDeviceLinks(DeviceId deviceId,
75 + String direction,
76 + LinkService service) {
77 + Direction dir = direction != null ?
78 + valueOf(direction.toUpperCase()) : Direction.ALL;
79 + switch (dir) {
80 + case INGRESS:
81 + return service.getDeviceIngressLinks(deviceId);
82 + case EGRESS:
83 + return service.getDeviceEgressLinks(deviceId);
84 + default:
85 + return service.getDeviceLinks(deviceId);
86 + }
87 + }
88 +
89 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.rest.exceptions;
17 +
18 +import com.fasterxml.jackson.databind.ObjectMapper;
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +
21 +import javax.ws.rs.core.Response;
22 +import javax.ws.rs.ext.ExceptionMapper;
23 +
24 +/**
25 + * Base exception mapper implementation.
26 + */
27 +public abstract class AbstractMapper<E extends Throwable> implements ExceptionMapper<E> {
28 +
29 + /**
30 + * Returns the response status to be given when the exception occurs.
31 + *
32 + * @return response status
33 + */
34 + protected abstract Response.Status responseStatus();
35 +
36 + @Override
37 + public Response toResponse(E exception) {
38 + return response(responseStatus(), exception).build();
39 + }
40 +
41 + /**
42 + * Produces a response builder primed with the supplied status code
43 + * and JSON entity with the status code and exception message.
44 + *
45 + * @param status response status
46 + * @param exception exception to encode
47 + * @return response builder
48 + */
49 + protected Response.ResponseBuilder response(Response.Status status,
50 + Throwable exception) {
51 + ObjectMapper mapper = new ObjectMapper();
52 + ObjectNode result = mapper.createObjectNode()
53 + .put("code", status.getStatusCode())
54 + .put("message", exception.getMessage());
55 + return Response.status(status).entity(result.toString());
56 + }
57 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.rest.exceptions;
17 +
18 +import org.onlab.api.ItemNotFoundException;
19 +
20 +import javax.ws.rs.core.Response;
21 +
22 +/**
23 + * Mapper for service not found exceptions to the NOT_FOUND response code.
24 + */
25 +public class EntityNotFoundMapper extends AbstractMapper<ItemNotFoundException> {
26 + @Override
27 + protected Response.Status responseStatus() {
28 + return Response.Status.NOT_FOUND;
29 + }
30 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.rest.exceptions;
17 +
18 +import javax.ws.rs.core.Response;
19 +
20 +/**
21 + * Mapper for service not found exceptions to the NOT_FOUND response code.
22 + */
23 +public class ServerErrorMapper extends AbstractMapper<RuntimeException> {
24 + @Override
25 + protected Response.Status responseStatus() {
26 + return Response.Status.INTERNAL_SERVER_ERROR;
27 + }
28 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.onos.rest.exceptions;
17 +
18 +import org.onlab.osgi.ServiceNotFoundException;
19 +
20 +import javax.ws.rs.core.Response;
21 +
22 +/**
23 + * Mapper for service not found exceptions to the SERVICE_UNAVAILABLE response code.
24 + */
25 +public class ServiceNotFoundMapper extends AbstractMapper<ServiceNotFoundException> {
26 + @Override
27 + protected Response.Status responseStatus() {
28 + return Response.Status.SERVICE_UNAVAILABLE;
29 + }
30 +}
1 +/*
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Various exception mappers to map errors to proper response status codes.
19 + */
20 +package org.onlab.onos.rest.exceptions;
...\ No newline at end of file ...\ No newline at end of file
...@@ -24,8 +24,21 @@ ...@@ -24,8 +24,21 @@
24 <servlet-name>JAX-RS Service</servlet-name> 24 <servlet-name>JAX-RS Service</servlet-name>
25 <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> 25 <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
26 <init-param> 26 <init-param>
27 - <param-name>com.sun.jersey.config.property.packages</param-name> 27 + <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
28 - <param-value>org.onlab.onos.rest</param-value> 28 + <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
29 + </init-param>
30 + <init-param>
31 + <param-name>com.sun.jersey.config.property.classnames</param-name>
32 + <param-value>
33 + org.onlab.onos.rest.exceptions.EntityNotFoundMapper,
34 + org.onlab.onos.rest.exceptions.ServiceNotFoundMapper,
35 + org.onlab.onos.rest.exceptions.ServerErrorMapper,
36 + org.onlab.onos.rest.JsonBodyWriter,
37 +
38 + org.onlab.onos.rest.DevicesWebResource,
39 + org.onlab.onos.rest.LinksWebResource,
40 + org.onlab.onos.rest.ConfigResource
41 + </param-value>
29 </init-param> 42 </init-param>
30 <load-on-startup>1</load-on-startup> 43 <load-on-startup>1</load-on-startup>
31 </servlet> 44 </servlet>
......
...@@ -109,6 +109,10 @@ ...@@ -109,6 +109,10 @@
109 <plugins> 109 <plugins>
110 <plugin> 110 <plugin>
111 <groupId>org.apache.felix</groupId> 111 <groupId>org.apache.felix</groupId>
112 + <artifactId>maven-scr-plugin</artifactId>
113 + </plugin>
114 + <plugin>
115 + <groupId>org.apache.felix</groupId>
112 <artifactId>maven-bundle-plugin</artifactId> 116 <artifactId>maven-bundle-plugin</artifactId>
113 <extensions>true</extensions> 117 <extensions>true</extensions>
114 <configuration> 118 <configuration>
...@@ -120,13 +124,15 @@ ...@@ -120,13 +124,15 @@
120 <Import-Package> 124 <Import-Package>
121 org.slf4j, 125 org.slf4j,
122 org.osgi.framework, 126 org.osgi.framework,
123 - javax.ws.rs,javax.ws.rs.core, 127 + javax.ws.rs,javax.ws.rs.core,javax.ws.rs.ext,
124 com.sun.jersey.api.core, 128 com.sun.jersey.api.core,
125 com.sun.jersey.spi.container.servlet, 129 com.sun.jersey.spi.container.servlet,
126 com.sun.jersey.server.impl.container.servlet, 130 com.sun.jersey.server.impl.container.servlet,
127 com.fasterxml.jackson.databind, 131 com.fasterxml.jackson.databind,
128 com.fasterxml.jackson.databind.node, 132 com.fasterxml.jackson.databind.node,
129 com.google.common.base.*, 133 com.google.common.base.*,
134 + org.onlab.api.*,
135 + org.onlab.osgi.*,
130 org.onlab.packet.*, 136 org.onlab.packet.*,
131 org.onlab.rest.*, 137 org.onlab.rest.*,
132 org.onlab.onos.* 138 org.onlab.onos.*
......