- fix of getConfigurationDatastore returning null
[yangtools.git] / restconf / restconf-client-impl / src / main / java / org / opendaylight / yangtools / restconf / client / RestconfClientImpl.java
1 /*
2  * Copyright (c) 2013 Cisco 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.yangtools.restconf.client;
9
10 import com.google.common.base.Function;
11 import com.google.common.base.Preconditions;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import com.google.common.util.concurrent.ListeningExecutorService;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import com.sun.jersey.api.client.Client;
16 import com.sun.jersey.api.client.ClientResponse;
17 import com.sun.jersey.api.client.WebResource;
18 import com.sun.jersey.api.client.config.ClientConfig;
19 import com.sun.jersey.api.client.config.DefaultClientConfig;
20 import java.net.URI;
21 import java.net.URISyntaxException;
22 import java.net.URL;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.Executors;
28 import org.opendaylight.yangtools.restconf.client.api.RestconfClientContext;
29 import org.opendaylight.yangtools.restconf.client.api.data.ConfigurationDatastore;
30 import org.opendaylight.yangtools.restconf.client.api.data.OperationalDatastore;
31 import org.opendaylight.yangtools.restconf.client.api.dto.RestEventStreamInfo;
32 import org.opendaylight.yangtools.restconf.client.api.dto.RestModule;
33 import org.opendaylight.yangtools.restconf.client.api.event.EventStreamInfo;
34 import org.opendaylight.yangtools.restconf.client.api.event.ListenableEventStreamContext;
35 import org.opendaylight.yangtools.restconf.client.api.rpc.RpcServiceContext;
36 import org.opendaylight.yangtools.restconf.client.to.RestListenableEventStreamContext;
37 import org.opendaylight.yangtools.restconf.client.to.RestRpcServiceContext;
38 import org.opendaylight.yangtools.restconf.common.ResourceMediaTypes;
39 import org.opendaylight.yangtools.restconf.common.ResourceUri;
40 import org.opendaylight.yangtools.restconf.utils.RestconfUtils;
41 import org.opendaylight.yangtools.restconf.utils.XmlTools;
42 import org.opendaylight.yangtools.yang.binding.RpcService;
43 import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContextHolder;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 public class RestconfClientImpl implements RestconfClientContext, SchemaContextListener {
51
52     private final URI defaultUri;
53
54     private final Client restClient;
55
56     private final ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
57
58     private final Logger logger = LoggerFactory.getLogger(RestconfClientImpl.class.toString());
59
60     private final SchemaContextHolder schemaContextHolder;
61
62     private final BindingIndependentMappingService mappingService;
63
64     private OperationalDataStoreImpl operationalDatastoreAccessor;
65     private ConfigurationDataStoreImpl configurationDatastoreAccessor;
66
67
68     public RestconfClientImpl(URL url,BindingIndependentMappingService mappingService, SchemaContextHolder schemaContextHolder){
69         Preconditions.checkArgument(url != null,"Restconf endpoint URL must be supplied.");
70         Preconditions.checkArgument(mappingService != null, "Mapping service must not be null.");
71         Preconditions.checkNotNull(schemaContextHolder, "Schema Context Holder must not be null.");
72         ClientConfig config = new DefaultClientConfig();
73         restClient  = Client.create(config);
74         URI uri = null;
75         try {
76             uri = url.toURI();
77         } catch (URISyntaxException e) {
78             logger.trace("Error in URI syntax {}",e.getMessage(),e);
79         }
80         this.defaultUri = uri;
81         this.mappingService = mappingService;
82         this.schemaContextHolder = schemaContextHolder;
83     }
84
85     protected URI getDefaultUri() {
86         return defaultUri;
87     }
88
89     protected ListeningExecutorService getPool() {
90         return pool;
91     }
92
93     protected SchemaContextHolder getSchemaContextHolder() {
94         return schemaContextHolder;
95     }
96
97     protected BindingIndependentMappingService getMappingService() {
98         return mappingService;
99     }
100
101     @Override
102     public ListenableFuture<Set<Class<? extends RpcService>>> getRpcServices() {
103         ListenableFuture<Set<Class<? extends RpcService>>> future = pool.submit(new Callable<Set<Class<? extends RpcService>>>() {
104             @Override
105             public Set<Class<? extends RpcService>> call() throws Exception {
106                 WebResource resource = restClient.resource(defaultUri.toString() + ResourceUri.MODULES.getPath());
107                 final ClientResponse response = resource.accept(ResourceMediaTypes.XML.getMediaType())
108                         .get(ClientResponse.class);
109                 if (response.getStatus() != 200) {
110                     throw new RuntimeException("Failed : HTTP error code : "
111                             + response.getStatus());
112                 }
113
114                 return RestconfUtils.rpcServicesFromInputStream(response.getEntityInputStream(),mappingService,schemaContextHolder.getSchemaContext());
115             }
116         });
117         return future;
118     }
119
120     @Override
121     public <T extends RpcService> RpcServiceContext<T> getRpcServiceContext(Class<T> rpcService) {
122         RestRpcServiceContext restRpcServiceContext = new RestRpcServiceContext(rpcService,this.defaultUri);
123         return restRpcServiceContext;
124     }
125
126     @Override
127     public ListenableFuture<Set<EventStreamInfo>> getAvailableEventStreams() {
128         ListenableFuture<Set<org.opendaylight.yangtools.restconf.client.api.event.EventStreamInfo>> future = pool.submit(new Callable<Set<org.opendaylight.yangtools.restconf.client.api.event.EventStreamInfo>>() {
129             @Override
130             public Set<org.opendaylight.yangtools.restconf.client.api.event.EventStreamInfo> call() throws Exception {
131                 // when restconf will support discovery by /restconf/streams change ResourceUri.MODULES to ResourceUri.STREAMS
132                 WebResource resource = restClient.resource(defaultUri.toString() + ResourceUri.MODULES.getPath());
133                 final ClientResponse response = resource.accept(ResourceMediaTypes.XML.getMediaType())
134                         .get(ClientResponse.class);
135
136                 if (response.getStatus() != 200) {
137                     throw new RuntimeException("Failed : HTTP error code : "
138                             + response.getStatus());
139                 }
140                 List<RestModule> modules = XmlTools.getModulesFromInputStream(response.getEntityInputStream());
141                 // when restconf will support discovery by /restconf/streams use this  instead of next iteration
142                 //return XmlTools.evenStreamsFromInputStream(response.getEntityInputStream());
143                 Set<EventStreamInfo> evtStreamInfos = new HashSet<EventStreamInfo>();
144                 for (RestModule module:modules){
145                     RestEventStreamInfo esi = new RestEventStreamInfo();
146                     esi.setIdentifier(module.getName());
147                     esi.setDescription(module.getNamespace()+" "+module.getRevision());
148                     evtStreamInfos.add(esi);
149                 }
150                 return evtStreamInfos;
151             }
152         });
153         return future;
154     }
155
156     @Override
157     public ListenableEventStreamContext getEventStreamContext(EventStreamInfo info) {
158         RestListenableEventStreamContext listenableEventStream = new RestListenableEventStreamContext(defaultUri);
159         return listenableEventStream;
160     }
161
162     @Override
163     public ConfigurationDatastore getConfigurationDatastore() {
164         if (configurationDatastoreAccessor == null)
165             configurationDatastoreAccessor = new ConfigurationDataStoreImpl(this);
166         return configurationDatastoreAccessor;
167     }
168
169     @Override
170     public OperationalDatastore getOperationalDatastore() {
171         if(operationalDatastoreAccessor == null)
172             operationalDatastoreAccessor =  new OperationalDataStoreImpl(this);
173         return operationalDatastoreAccessor;
174     }
175
176     @Override
177     public void close() {
178         this.pool.shutdown();
179     }
180
181
182     @Override
183     public void onGlobalContextUpdated(SchemaContext context) {
184
185     }
186
187     protected Client getRestClient() {
188         return restClient;
189     }
190
191     public SchemaContext getSchemaContext() {
192         return this.schemaContextHolder.getSchemaContext();
193     }
194
195     protected <T> ListenableFuture<T> get(final String path, final Function<ClientResponse, T> processingFunction) {
196         return pool.submit(new GetAndTransformTask<T>(constructPath(path),processingFunction));
197     }
198
199     protected <T> ListenableFuture<T> get(final String path,final String mediaType, final Function<ClientResponse, T> processingFunction) {
200         return pool.submit(new GetAndTransformTask<T>(constructPath(path),mediaType,processingFunction));
201     }
202
203     protected String constructPath(String path) {
204         return getDefaultUri().toString() + path;
205     }
206
207     //, RestRestconfService {
208
209     private class GetAndTransformTask<T> implements Callable<T> {
210
211         private final Function<ClientResponse, T> transformation;
212         private final String path;
213         private final String acceptType;
214
215         public GetAndTransformTask(String path, Function<ClientResponse, T> processingFunction) {
216             this.path = path;
217             this.transformation = processingFunction;
218             this.acceptType = ResourceMediaTypes.XML.getMediaType();
219         }
220
221         public GetAndTransformTask(String path, String mediaType, Function<ClientResponse, T> processingFunction) {
222             this.path = path;
223             this.transformation = processingFunction;
224             this.acceptType = mediaType;
225         }
226
227         @Override
228         public T call() {
229
230             WebResource resource = restClient.resource(path);
231             ClientResponse response = resource.accept(acceptType).get(ClientResponse.class);
232
233             // Applies the specific transformation for supplied resource.
234             return transformation.apply(response);
235         }
236
237     }
238
239 }