blob: 385415f2927c6bd4cecd4e5c9d4bf6664950182c [file] [log] [blame]
Ray Milkeye8274232014-05-27 16:03:12 -07001package net.onrc.onos.api.rest;
2
3
4import net.floodlightcontroller.restserver.RestletRoutable;
Ray Milkeye8274232014-05-27 16:03:12 -07005import org.restlet.Application;
6import org.restlet.Component;
7import org.restlet.Context;
8import org.restlet.Request;
9import org.restlet.Response;
10import org.restlet.Restlet;
11import org.restlet.Server;
12import org.restlet.data.Protocol;
13import org.restlet.data.Reference;
14import org.restlet.data.Status;
15import org.restlet.ext.jackson.JacksonRepresentation;
16import org.restlet.representation.Representation;
17import org.restlet.routing.Filter;
18import org.restlet.routing.Router;
19import org.restlet.routing.Template;
20import org.restlet.service.StatusService;
21
22import java.util.List;
23
24/**
25 * A REST API server suitible for inclusion in unit tests. Unit tests can
26 * create a server on a given port, then specify the RestletRoutable classes
27 * that are to be tested. The lifecyle for the server is to create it
28 * and then start it during the @Before (setUp) portion of the test and to
29 * shut it down during the @After (tearDown) section.
30 */
31public class TestRestApiServer {
32
33 private List<RestletRoutable> restlets;
34 private RestApplication restApplication;
35 private Server server;
36 private Component component;
Ray Milkeye8274232014-05-27 16:03:12 -070037
38 /**
39 * The restlet engine requires an Application as a container.
40 */
41 private class RestApplication extends Application {
Ray Milkey531bb232014-06-03 09:08:15 -070042 private final Context context;
Ray Milkeye8274232014-05-27 16:03:12 -070043
44 /**
45 * Initialize the Application along with its Context.
46 */
47 public RestApplication() {
48 super();
49 context = new Context();
50 }
51
52 /**
53 * Add an attribute to the Context for the Application. This is most
54 * often used to specify attributes that allow modules to locate each
55 * other.
56 *
57 * @param name name of the attribute
58 * @param value value of the attribute
59 */
60 public void addAttribute(final String name, final Object value) {
61 context.getAttributes().put(name, value);
62 }
63
64 /**
65 * Sets up the Restlet for the APIs under test using a Router. Also, a
66 * filter is installed to deal with double slashes in URLs.
67 * This code is adapted from
68 * net.floodlightcontroller.restserver.RestApiServer
69 *
70 * @return Router object for the APIs under test.
71 */
72 @Override
73 public Restlet createInboundRoot() {
74 Router baseRouter = new Router(context);
75 baseRouter.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
76 for (RestletRoutable rr : restlets) {
77 baseRouter.attach(rr.basePath(), rr.getRestlet(context));
78 }
79
80 /**
81 * Filter out multiple slashes in URLs to make them a single slash.
82 */
83 Filter slashFilter = new Filter() {
84 @Override
85 protected int beforeHandle(Request request, Response response) {
86 Reference ref = request.getResourceRef();
87 String originalPath = ref.getPath();
88 if (originalPath.contains("//")) {
89 String newPath = originalPath.replaceAll("/+", "/");
90 ref.setPath(newPath);
91 }
92 return Filter.CONTINUE;
93 }
94
95 };
96 slashFilter.setNext(baseRouter);
97
98 return slashFilter;
99 }
100
Ray Milkeye8274232014-05-27 16:03:12 -0700101
Ray Milkey1c030302014-07-30 11:03:29 -0700102 /**
103 * Run the Application on an open port.
104 *
105 */
106 public void run() {
107
Ray Milkeye8274232014-05-27 16:03:12 -0700108 try {
Ray Milkey1c030302014-07-30 11:03:29 -0700109 setStatusService(new StatusService() {
110 @Override
111 public Representation getRepresentation(Status status,
112 Request request,
113 Response response) {
114 return new JacksonRepresentation<>(status);
115 }
116 });
117
118 // Start listening for REST requests
Ray Milkeye8274232014-05-27 16:03:12 -0700119 component = new Component();
Ray Milkey1c030302014-07-30 11:03:29 -0700120 server = component.getServers().add(Protocol.HTTP, 0);
Ray Milkeye8274232014-05-27 16:03:12 -0700121 component.getDefaultHost().attach(this);
122 component.start();
123 } catch (Exception e) {
Ray Milkey1c030302014-07-30 11:03:29 -0700124 // Web server did not start.
125 throw new IllegalStateException(e);
Ray Milkeye8274232014-05-27 16:03:12 -0700126 }
127 }
128 }
129
130 /**
131 * Start up the REST server. A list of the Restlets being tested is
132 * passed in. The usual use of this method is in the @Before (startUp)
133 * of a JUnit test.
134 *
135 * @param restletsUnderTest list of Restlets to run as part of the server.
Ray Milkeye8274232014-05-27 16:03:12 -0700136 */
Ray Milkey531bb232014-06-03 09:08:15 -0700137 public void startServer(final List<RestletRoutable> restletsUnderTest) {
Ray Milkeye8274232014-05-27 16:03:12 -0700138 restlets = restletsUnderTest;
139
140 restApplication = new RestApplication();
Ray Milkey1c030302014-07-30 11:03:29 -0700141 restApplication.run();
Ray Milkeye8274232014-05-27 16:03:12 -0700142
143 }
144
145 /**
146 * Stop the REST server. The container is stopped, and the server will
147 * no longer respond to requests. The usual use of this is in the @After
Ray Milkey531bb232014-06-03 09:08:15 -0700148 * (tearDown) part of the test.
Ray Milkeye8274232014-05-27 16:03:12 -0700149 */
Ray Milkey531bb232014-06-03 09:08:15 -0700150 public void stopServer() {
151 try {
152 restApplication.stop();
153 server.stop();
154 component.stop();
155 } catch (Exception ex) {
156 // Stopping the server failed, convert to unchecked exception to
157 // abort the calling test with a failure.
158 throw new IllegalStateException(ex);
159 }
Ray Milkeye8274232014-05-27 16:03:12 -0700160 }
161
162
163 /**
164 * Add an attribute to the Context for the Application. This is most
165 * often used to specify attributes that allow modules to locate each
166 * other.
167 *
168 * @param name name of the attribute
169 * @param value value of the attribute
170 */
171 public void addAttribute(final String name, final Object value) {
172 restApplication.addAttribute(name, value);
173 }
Ray Milkey1c030302014-07-30 11:03:29 -0700174
175 /**
176 * Gets the port number being used by the REST web server.
177 *
178 * @return port number
179 */
180 public int getRestPort() {
181 return server.getActualPort();
182 }
Ray Milkeye8274232014-05-27 16:03:12 -0700183}