Merge "Convert yanglib to new web API"
[netconf.git] / restconf / sal-rest-docgen / src / main / java / org / opendaylight / netconf / sal / rest / doc / mountpoints / MountPointSwagger.java
1 /*
2  * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.sal.rest.doc.mountpoints;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import java.util.Collections;
13 import java.util.HashMap;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.Objects;
19 import java.util.TreeMap;
20 import java.util.concurrent.atomic.AtomicLong;
21 import javax.ws.rs.core.UriInfo;
22 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
23 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
24 import org.opendaylight.controller.sal.core.api.model.SchemaService;
25 import org.opendaylight.controller.sal.core.api.mount.MountProvisionListener;
26 import org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator;
27 import org.opendaylight.netconf.sal.rest.doc.swagger.Api;
28 import org.opendaylight.netconf.sal.rest.doc.swagger.ApiDeclaration;
29 import org.opendaylight.netconf.sal.rest.doc.swagger.Operation;
30 import org.opendaylight.netconf.sal.rest.doc.swagger.Resource;
31 import org.opendaylight.netconf.sal.rest.doc.swagger.ResourceList;
32 import org.opendaylight.yangtools.concepts.ListenerRegistration;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
35 import org.opendaylight.yangtools.yang.model.api.Module;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37
38 public class MountPointSwagger implements MountProvisionListener, AutoCloseable {
39
40     private static final String DATASTORES_REVISION = "-";
41     private static final String DATASTORES_LABEL = "Datastores";
42
43     private final SchemaService globalSchema;
44     private final DOMMountPointService mountService;
45     private final BaseYangSwaggerGenerator swaggerGenerator;
46     private final Map<YangInstanceIdentifier, Long> instanceIdToLongId =
47             new TreeMap<>((o1, o2) -> o1.toString().compareToIgnoreCase(o2.toString()));
48     private final Map<Long, YangInstanceIdentifier> longIdToInstanceId = new HashMap<>();
49
50     private final Object lock = new Object();
51
52     private final AtomicLong idKey = new AtomicLong(0);
53
54     private ListenerRegistration<MountProvisionListener> registration;
55
56     public MountPointSwagger(SchemaService globalSchema, DOMMountPointService mountService,
57             BaseYangSwaggerGenerator swaggerGenerator) {
58         this.globalSchema = Objects.requireNonNull(globalSchema);
59         this.mountService = Objects.requireNonNull(mountService);
60         this.swaggerGenerator = Objects.requireNonNull(swaggerGenerator);
61     }
62
63     public void init() {
64         registration = mountService.registerProvisionListener(this);
65     }
66
67     @Override
68     public void close() {
69         if (registration != null) {
70             registration.close();
71         }
72     }
73
74     public Map<String, Long> getInstanceIdentifiers() {
75         final Map<String, Long> urlToId = new HashMap<>();
76         synchronized (this.lock) {
77             final SchemaContext context = this.globalSchema.getGlobalContext();
78             for (final Entry<YangInstanceIdentifier, Long> entry : this.instanceIdToLongId.entrySet()) {
79                 final String modName = findModuleName(entry.getKey(), context);
80                 urlToId.put(swaggerGenerator.generateUrlPrefixFromInstanceID(entry.getKey(), modName),
81                         entry.getValue());
82             }
83         }
84         return urlToId;
85     }
86
87     private String findModuleName(final YangInstanceIdentifier id, final SchemaContext context) {
88         final PathArgument rootQName = id.getPathArguments().iterator().next();
89         for (final Module mod : context.getModules()) {
90             if (mod.getDataChildByName(rootQName.getNodeType()) != null) {
91                 return mod.getName();
92             }
93         }
94         return null;
95     }
96
97     private String getYangMountUrl(final YangInstanceIdentifier key) {
98         final String modName = findModuleName(key, this.globalSchema.getGlobalContext());
99         return swaggerGenerator.generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount";
100     }
101
102     public ResourceList getResourceList(final UriInfo uriInfo, final Long id) {
103         final YangInstanceIdentifier iid = getInstanceId(id);
104         if (iid == null) {
105             return null; // indicating not found.
106         }
107         final SchemaContext context = getSchemaContext(iid);
108         if (context == null) {
109             return swaggerGenerator.createResourceList();
110         }
111         final List<Resource> resources = new LinkedList<>();
112         final Resource dataStores = new Resource();
113         dataStores.setDescription("Provides methods for accessing the data stores.");
114         dataStores.setPath(swaggerGenerator.generatePath(uriInfo, DATASTORES_LABEL, DATASTORES_REVISION));
115         resources.add(dataStores);
116         final String urlPrefix = getYangMountUrl(iid);
117         final ResourceList list = swaggerGenerator.getResourceListing(uriInfo, context, urlPrefix);
118         resources.addAll(list.getApis());
119         list.setApis(resources);
120         return list;
121     }
122
123     private YangInstanceIdentifier getInstanceId(final Long id) {
124         final YangInstanceIdentifier instanceId;
125         synchronized (this.lock) {
126             instanceId = this.longIdToInstanceId.get(id);
127         }
128         return instanceId;
129     }
130
131     private SchemaContext getSchemaContext(final YangInstanceIdentifier id) {
132
133         if (id == null) {
134             return null;
135         }
136
137         Preconditions.checkState(mountService != null);
138         final Optional<DOMMountPoint> mountPoint = this.mountService.getMountPoint(id);
139         if (!mountPoint.isPresent()) {
140             return null;
141         }
142
143         final SchemaContext context = mountPoint.get().getSchemaContext();
144         if (context == null) {
145             return null;
146         }
147         return context;
148     }
149
150     public ApiDeclaration getMountPointApi(final UriInfo uriInfo, final Long id, final String module,
151             final String revision) {
152         final YangInstanceIdentifier iid = getInstanceId(id);
153         final SchemaContext context = getSchemaContext(iid);
154         final String urlPrefix = getYangMountUrl(iid);
155         if (context == null) {
156             return null;
157         }
158
159         if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) {
160             return generateDataStoreApiDoc(uriInfo, urlPrefix);
161         }
162         return swaggerGenerator.getApiDeclaration(module, revision, uriInfo, context, urlPrefix);
163     }
164
165     private ApiDeclaration generateDataStoreApiDoc(final UriInfo uriInfo, final String context) {
166         final List<Api> apis = new LinkedList<>();
167         apis.add(createGetApi("config", "Queries the config (startup) datastore on the mounted hosted.", context));
168         apis.add(createGetApi("operational", "Queries the operational (running) datastore on the mounted hosted.",
169                 context));
170         apis.add(createGetApi("operations", "Queries the available operations (RPC calls) on the mounted hosted.",
171                 context));
172
173         final ApiDeclaration declaration = swaggerGenerator.createApiDeclaration(
174                 swaggerGenerator.createBasePathFromUriInfo(uriInfo));
175         declaration.setApis(apis);
176         return declaration;
177
178     }
179
180     private Api createGetApi(final String datastore, final String note, final String context) {
181         final Operation getConfig = new Operation();
182         getConfig.setMethod("GET");
183         getConfig.setNickname("GET " + datastore);
184         getConfig.setNotes(note);
185
186         final Api api = new Api();
187         api.setPath(swaggerGenerator.getDataStorePath(datastore, context).concat(
188                 swaggerGenerator.getContent(datastore)));
189         api.setOperations(Collections.singletonList(getConfig));
190
191         return api;
192     }
193
194     @Override
195     public void onMountPointCreated(final YangInstanceIdentifier path) {
196         synchronized (this.lock) {
197             final Long idLong = this.idKey.incrementAndGet();
198             this.instanceIdToLongId.put(path, idLong);
199             this.longIdToInstanceId.put(idLong, path);
200         }
201     }
202
203     @Override
204     public void onMountPointRemoved(final YangInstanceIdentifier path) {
205         synchronized (this.lock) {
206             final Long id = this.instanceIdToLongId.remove(path);
207             this.longIdToInstanceId.remove(id);
208         }
209     }
210 }