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