Add OCP Plugin Developer Guide
[docs.git] / manuals / developer-guide / src / main / asciidoc / developing-app.adoc
1 == Developing Apps on the OpenDaylight controller ==\r
2 \r
3 // Content is adapted from the https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Startup_Project_Archetype\r
4 \r
5 This section provides information that is required to develop apps on the OpenDaylight controller.\r
6  \r
7 You can either develop apps within the controller using the model-driven SAL (MD-SAL) archetype or develop external apps and use the RESTCONF to communicate with the controller.\r
8 \r
9 === Overview ===\r
10 \r
11 This section enables you to get started with app development within the OpenDaylight controller. In this example, you perform the following steps to develop an app.\r
12 \r
13 . Create a local repository for the code using a simple build process. \r
14 . Start the OpenDaylight controller. \r
15 . Test a simple remote procedure call (RPC) which you have created based on the principle of 'hello world'.\r
16 \r
17 === Pre requisites ===\r
18 \r
19 This example requires the following.\r
20 \r
21 * A development environment with following set up and working correctly from the shell:\r
22 ** Maven 3.1.1 or later\r
23 ** Java 7- or Java 8-compliant JDK\r
24 ** An appropriate Maven settings.xml file. A simple way to get the default OpenDaylight settings.xml file is:\r
25 +\r
26  cp -n ~/.m2/settings.xml{,.orig} ; \wget -q -O - https://raw.githubusercontent.com/opendaylight/odlparent/stable/lithium/settings.xml > ~/.m2/settings.xml\r
27 \r
28 NOTE: If you are using Linux or Mac OS X as your development OS, your local repository is ~/.m2/repository. For other platforms the local repository location will vary.\r
29 \r
30 === Building an example module ===\r
31 \r
32 To develop an app perform the following steps.\r
33 \r
34 . Create an 'Example' project using Maven and an archetype called the 'opendaylight-startup-archetype'. If you are downloading this project for the first time, then it will take sometime to pull all the code from the remote repository.\r
35 +\r
36 [source,shell]\r
37 mvn archetype:generate -DarchetypeGroupId=org.opendaylight.controller -DarchetypeArtifactId=opendaylight-startup-archetype \\r
38 -DarchetypeRepository=https://nexus.opendaylight.org/content/repositories/public/ \\r
39 -DarchetypeCatalog=https://nexus.opendaylight.org/content/repositories/public/archetype-catalog.xml\r
40 +\r
41 . Update the properties values as follows. Ensure that the groupid and the artifactid is lower case.\r
42 +\r
43 [source,shell]\r
44 Define value for property 'groupId': : org.opendaylight.example\r
45 Define value for property 'artifactId': : example\r
46 Define value for property 'version':  1.0-SNAPSHOT: : 1.0.0-SNAPSHOT\r
47 Define value for property 'package':  org.opendaylight.example: : \r
48 Define value for property 'classPrefix':  ${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)}\r
49 Define value for property 'copyright': : Copyright (c) 2015 Yoyodyne, Inc. \r
50 +\r
51 . Accept the default value of classPrefix that is, `(${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)})`.\r
52 The classPrefix creates a Java Class Prefix by capitalizing the first character of the artifactId.\r
53 +\r
54 NOTE: In this scenario, the classPrefix used is "Example".\r
55     Create a top-level directory for the archetype.\r
56 +\r
57 [source,shell]\r
58 ${artifactId}/\r
59 example/\r
60 cd example/\r
61 api/\r
62 artifacts/\r
63 features/\r
64 impl/\r
65 karaf/\r
66 pom.xml\r
67 +\r
68 . Build the 'example' project.\r
69 +\r
70 NOTE: Depending on your development machine's specification this might take a little while. Ensure that you are in the project's root directory, example/, and then issue the build command, shown below.\r
71 +\r
72 [source,shell]\r
73 mvn clean install\r
74 +\r
75 . Start the 'example' project for the first time.\r
76 +\r
77 [source,shell]\r
78 cd karaf/target/assembly/bin\r
79 ls\r
80 ./karaf\r
81 +\r
82 . Wait for the karaf cli that appears as follows. Wait for OpenDaylight to fully load all the components. This can take a minute or two after the prompt appears. Check the CPU on your dev machine, specifically the Java process to see when it calms down. \r
83 +\r
84 [source,shell]\r
85 opendaylight-user@root>\r
86 +\r
87 . Verify if the “example” module is built and search  for the log entry which includes the entry 'ExampleProvider Session Initiated'.\r
88 +\r
89 [source,shell]\r
90 log:display | grep Example\r
91 +\r
92 . Shutdown the OpenDaylight through the console by using the following command.\r
93 +\r
94 [source,shell]\r
95 shutdown -f\r
96 \r
97 === Defining a Simple Hello World RPC ===\r
98 \r
99 . Run the maven archetype 'opendaylight-startup-archetype', and create the 'hello' project. + \r
100 +\r
101 [source,shell]\r
102 mvn archetype:generate -DarchetypeGroupId=org.opendaylight.controller -DarchetypeArtifactId=opendaylight-startup-archetype \\r
103 -DarchetypeRepository=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/ \\r
104 -DarchetypeCatalog=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml\r
105 +\r
106 . Update the Properties values as follows.\r
107 +\r
108 [source,shell]\r
109 Define value for property 'groupId': : org.opendaylight.hello\r
110 Define value for property 'artifactId': : hello\r
111 Define value for property 'version':  1.0-SNAPSHOT: : 1.0.0-SNAPSHOT\r
112 Define value for property 'package':  org.opendaylight.hello: : \r
113 Define value for property 'classPrefix':  ${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)}\r
114 Define value for property 'copyright': : Copyright(c) Yoyodyne, Inc.\r
115 +\r
116 . View the 'hello' project.\r
117 +\r
118 [source,shell]\r
119 cd hello/\r
120 ls -1\r
121 api\r
122 artifacts\r
123 features\r
124 impl\r
125 karaf\r
126 pom.xml\r
127 +\r
128 . Build 'hello' project by using the following command.\r
129 +\r
130 [source,shell]\r
131 mvn clean install\r
132 +\r
133 . Verify  that the project is functioning by executing karaf.\r
134 +\r
135 [source,shell]\r
136 cd karaf/target/assembly/bin\r
137 ./karaf\r
138 +\r
139 . The karaf cli appears as follows. + \r
140 NOTE: Remember to wait for OpenDaylight to load completely. Verify that the Java process CPU has stabilized.+  \r
141 +\r
142 [source,shell]\r
143 opendaylight-user@root>\r
144 +\r
145 . Verify that the 'hello' module is loaded by checking the log.\r
146 +\r
147 [source,shell]\r
148 log:display | grep Hello\r
149 +\r
150 . Shutdown karaf.\r
151 +\r
152 [source,shell]\r
153 shutdown -f\r
154 +\r
155 . Return to the top of the directory structure:\r
156 +\r
157 [source,shell]\r
158 cd ../../../../\r
159 +\r
160 . View the entry point to understand where the log line came from. The entry point is in the impl project:\r
161 +\r
162 [source,shell]\r
163 impl/src/main/java/org/opendaylight/hello/impl/HelloProvider.java\r
164 +\r
165 . Add any new things that you are doing in your implementation by using the HelloProvider.onSessionInitiate method. Its analogous to an Activator. \r
166 +\r
167 [source,java]\r
168 @Override\r
169     public void onSessionInitiated(ProviderContext session) {\r
170         LOG.info("HelloProvider Session Initiated");\r
171     }\r
172 \r
173 === Add a simple HelloWorld RPC API\r
174 \r
175 . Navigate to the file.\r
176 +\r
177 [source]\r
178 Edit\r
179 api/src/main/yang/hello.yang\r
180 +\r
181 . Edit this file as follows. In the following example, we are adding the code in a YANG module to define the 'hello-world' RPC:\r
182 +\r
183 [source,yang]\r
184 module hello {\r
185     yang-version 1;\r
186     namespace "urn:opendaylight:params:xml:ns:yang:hello";\r
187     prefix "hello";\r
188     revision "2015-01-05" {\r
189         description "Initial revision of hello model";\r
190     }\r
191     rpc hello-world {\r
192         input {\r
193             leaf name {\r
194                 type string;\r
195             }\r
196         }\r
197         output {\r
198             leaf greating {\r
199                 type string;\r
200             }\r
201         }\r
202     }\r
203 }\r
204 +\r
205 . Return to the hello/api directory and build your API as follows.      \r
206 +\r
207 [source,shell]\r
208 cd ../../../\r
209 mvn clean install\r
210 \r
211 === Implement the HelloWorld RPC API\r
212 \r
213 . Define the HelloService, which is invoked through the 'hello-world' API.\r
214 +\r
215 [source,shell]\r
216 cd ../impl/src/main/java/org/opendaylight/hello/impl/\r
217 +\r
218 . Create a new file called HelloWorldImpl.java and add in the code below.\r
219 +\r
220 [source,java]\r
221 package org.opendaylight.hello.impl;\r
222 import java.util.concurrent.Future;\r
223 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloService;\r
224 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldInput;\r
225 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldOutput;\r
226 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldOutputBuilder;\r
227 import org.opendaylight.yangtools.yang.common.RpcResult;\r
228 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;\r
229 public class HelloWorldImpl implements HelloService {\r
230     @Override\r
231     public Future<RpcResult<HelloWorldOutput>> helloWorld(HelloWorldInput input) {\r
232         HelloWorldOutputBuilder helloBuilder = new HelloWorldOutputBuilder();\r
233         helloBuilder.setGreating("Hello " + input.getName());\r
234         return RpcResultBuilder.success(helloBuilder.build()).buildFuture();\r
235     }\r
236 }\r
237 +\r
238 . The HelloProvider.java file is in the current directory. Register the RPC that you created in the 'hello.yang' file in the HelloProvider.java file. You can either edit the HelloProvider.java to match what is below or you can simple replace it with the code below.\r
239 +\r
240 [source,java]\r
241 -----\r
242 /*\r
243  * Copyright(c) Yoyodyne, Inc. and others.  All rights reserved.\r
244  *\r
245  * This program and the accompanying materials are made available under the\r
246  * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
247  * and is available at http://www.eclipse.org/legal/epl-v10.html\r
248  */\r
249 package org.opendaylight.hello.impl;\r
250 \r
251 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;\r
252 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;\r
253 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;\r
254 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloService;\r
255 import org.slf4j.Logger;\r
256 import org.slf4j.LoggerFactory;\r
257 \r
258 public class HelloProvider implements BindingAwareProvider, AutoCloseable {\r
259     private static final Logger LOG = LoggerFactory.getLogger(HelloProvider.class);\r
260     private RpcRegistration<HelloService> helloService;\r
261     @Override\r
262     public void onSessionInitiated(ProviderContext session) {\r
263         LOG.info("HelloProvider Session Initiated");\r
264         helloService = session.addRpcImplementation(HelloService.class, new HelloWorldImpl());\r
265     }\r
266     @Override\r
267     public void close() throws Exception {\r
268         LOG.info("HelloProvider Closed");\r
269         if (helloService != null) {\r
270             helloService.close();\r
271         }\r
272     }\r
273 }\r
274 -----\r
275 +\r
276 . Optionally, you can also build the Java classes which will register the new RPC. This is useful to test the edits you have made to HelloProvider.java and HelloWorldImpl.java.\r
277 +\r
278 [source,shell]\r
279 cd ../../../../../../../\r
280 mvn clean install\r
281 +\r
282 . Return to the top level directory\r
283 +\r
284 [source,shell]\r
285 cd ../\r
286 +\r
287 . Build the entire 'hello' again, which will pickup the changes you have made and build them into your project:\r
288 +\r
289 [source,shell]\r
290 mvn clean install\r
291 \r
292 === Execute the 'hello' project for the first time\r
293 \r
294 . Run karaf\r
295 +\r
296 [source,shell]\r
297 cd ../karaf/target/assembly/bin\r
298 ./karaf\r
299 +\r
300 . Wait for the project to load completely. Then view the log to see the loaded 'Hello' Module:\r
301 +\r
302 [source,shell]\r
303 log:display | grep Hello\r
304 \r
305 === Test the 'hello-world' RPC via REST\r
306 \r
307 There are a lot of ways to test your RPC. Following are some examples.\r
308 \r
309 . Using the API Explorer through HTTP\r
310 . Using a browser REST client\r
311 \r
312 ==== Using the API Explorer through HTTP \r
313 \r
314 . Navigate to http://localhost:8181/apidoc/explorer/index.html[apidoc UI] with your web browser. + \r
315 NOTE: In the URL mentioned above, Change 'localhost' to the IP/Host name to reflect your development machine's network address.\r
316 +\r
317 . Select \r
318 +\r
319 [source,shell]\r
320 hello(2015-01-05)\r
321 +\r
322 . Select \r
323 [source]\r
324 POST /operations/hello:hello-world\r
325 +\r
326 . Provide the required value.\r
327 [source,json]\r
328 {"hello:input": { "name":"Your Name"}}\r
329 +\r
330 . Click the button.\r
331 . Enter the username and password, by default the credentials are admin/admin.\r
332 . In the response body you should see.\r
333 +\r
334 [source,json]\r
335 {\r
336   "output": {\r
337     "greating": "Hello Your Name"\r
338   }\r
339 }\r
340 \r
341 ==== Using a browser REST client\r
342 \r
343 For example, use the following information in the Firefox plugin 'RESTClient' [https://github.com/chao/RESTClient} + \r
344 [source]\r
345 POST: http://192.168.1.43:8181/restconf/operations/hello:hello-world\r
346 \r
347 Header:\r
348 [source]\r
349 application/json\r
350 \r
351 Body:\r
352 [source,json]\r
353 {"input": {\r
354     "name": "Andrew"\r
355   }\r
356 }\r
357 \r
358 \r
359 === Troubleshooting ===\r
360 \r
361 If you get a response code 501 while attempting to POST /operations/hello:hello-world, check the file: HelloProvider.java and make sure the helloService member is being set.\r
362 By not invoking "session.addRpcImplementation()" the REST API will be unable to map /operations/hello:hello-world url to HelloWorldImpl.\r
363 \r
364 // == Developing Apps external to the controller ==\r