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