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