2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.restconf.client;
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;
21 import java.net.URISyntaxException;
23 import java.util.HashSet;
24 import java.util.List;
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;
50 public class RestconfClientImpl implements RestconfClientContext, SchemaContextListener {
52 private final URI defaultUri;
54 private final Client restClient;
56 private final ListeningExecutorService pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
58 private final Logger logger = LoggerFactory.getLogger(RestconfClientImpl.class.toString());
60 private final SchemaContextHolder schemaContextHolder;
62 private final BindingIndependentMappingService mappingService;
64 private OperationalDataStoreImpl operationalDatastoreAccessor;
65 private ConfigurationDataStoreImpl configurationDatastoreAccessor;
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);
77 } catch (URISyntaxException e) {
78 logger.trace("Error in URI syntax {}",e.getMessage(),e);
80 this.defaultUri = uri;
81 this.mappingService = mappingService;
82 this.schemaContextHolder = schemaContextHolder;
85 protected URI getDefaultUri() {
89 protected ListeningExecutorService getPool() {
93 protected SchemaContextHolder getSchemaContextHolder() {
94 return schemaContextHolder;
97 protected BindingIndependentMappingService getMappingService() {
98 return mappingService;
102 public ListenableFuture<Set<Class<? extends RpcService>>> getRpcServices() {
103 ListenableFuture<Set<Class<? extends RpcService>>> future = pool.submit(new Callable<Set<Class<? extends RpcService>>>() {
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());
114 return RestconfUtils.rpcServicesFromInputStream(response.getEntityInputStream(),mappingService,schemaContextHolder.getSchemaContext());
121 public <T extends RpcService> RpcServiceContext<T> getRpcServiceContext(Class<T> rpcService) {
122 RestRpcServiceContext restRpcServiceContext = new RestRpcServiceContext(rpcService,this.defaultUri);
123 return restRpcServiceContext;
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>>() {
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);
136 if (response.getStatus() != 200) {
137 throw new RuntimeException("Failed : HTTP error code : "
138 + response.getStatus());
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);
150 return evtStreamInfos;
157 public ListenableEventStreamContext getEventStreamContext(EventStreamInfo info) {
158 RestListenableEventStreamContext listenableEventStream = new RestListenableEventStreamContext(defaultUri);
159 return listenableEventStream;
163 public ConfigurationDatastore getConfigurationDatastore() {
164 if (configurationDatastoreAccessor == null)
165 configurationDatastoreAccessor = new ConfigurationDataStoreImpl(this);
166 return configurationDatastoreAccessor;
170 public OperationalDatastore getOperationalDatastore() {
171 if(operationalDatastoreAccessor == null)
172 operationalDatastoreAccessor = new OperationalDataStoreImpl(this);
173 return operationalDatastoreAccessor;
177 public void close() {
178 this.pool.shutdown();
183 public void onGlobalContextUpdated(SchemaContext context) {
187 protected Client getRestClient() {
191 public SchemaContext getSchemaContext() {
192 return this.schemaContextHolder.getSchemaContext();
195 protected <T> ListenableFuture<T> get(final String path, final Function<ClientResponse, T> processingFunction) {
196 return pool.submit(new GetAndTransformTask<T>(constructPath(path),processingFunction));
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));
203 protected String constructPath(String path) {
204 return getDefaultUri().toString() + path;
207 //, RestRestconfService {
209 private class GetAndTransformTask<T> implements Callable<T> {
211 private final Function<ClientResponse, T> transformation;
212 private final String path;
213 private final String acceptType;
215 public GetAndTransformTask(String path, Function<ClientResponse, T> processingFunction) {
217 this.transformation = processingFunction;
218 this.acceptType = ResourceMediaTypes.XML.getMediaType();
221 public GetAndTransformTask(String path, String mediaType, Function<ClientResponse, T> processingFunction) {
223 this.transformation = processingFunction;
224 this.acceptType = mediaType;
230 WebResource resource = restClient.resource(path);
231 ClientResponse response = resource.accept(acceptType).get(ClientResponse.class);
233 // Applies the specific transformation for supplied resource.
234 return transformation.apply(response);