View Javadoc

1   /**
2    * Licensed to jclouds, Inc. (jclouds) under one or more
3    * contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  jclouds licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.jclouds.rest;
20  
21  import static com.google.common.base.Preconditions.checkNotNull;
22  import static com.google.common.base.Throwables.propagate;
23  import static com.google.common.collect.Iterables.concat;
24  import static com.google.common.collect.Iterables.size;
25  import static com.google.common.collect.Iterables.transform;
26  import static java.util.Collections.EMPTY_LIST;
27  import static org.jclouds.util.Throwables2.propagateAuthorizationOrOriginalException;
28  
29  import java.io.File;
30  import java.io.IOException;
31  import java.util.Properties;
32  
33  import org.jclouds.javax.annotation.Nullable;
34  import javax.inject.Inject;
35  
36  import org.jclouds.PropertiesBuilder;
37  import org.jclouds.location.reference.LocationConstants;
38  import org.jclouds.util.Modules2;
39  import org.jclouds.util.Strings2;
40  
41  import com.google.common.base.Charsets;
42  import com.google.common.base.Function;
43  import com.google.common.base.Joiner;
44  import com.google.common.base.Splitter;
45  import com.google.common.collect.ImmutableSet;
46  import com.google.common.io.Files;
47  import com.google.inject.Module;
48  
49  /**
50   * Helper class to instantiate {@code RestContext} instances. "blobstore.properties"
51   * 
52   * At least one property is needed needed per context:
53   * <ul>
54   * <li>tag.contextbuilder=classname extends RestContextBuilder</li>
55   * </ul>
56   * 
57   * Optional properties are as follows
58   * <ul>
59   * <li>tag.contextbuilder=classname extends RestContextBuilder</li>
60   * <li>tag.propertiesbuilder=classname extends HttpPropertiesBuilder</li>
61   * </ul>
62   * Ex.
63   * 
64   * <pre>
65   * azureblob.contextbuilder=org.jclouds.azure.storage.blob.blobstore.AzureRestContextBuilder
66   * azureblob.propertiesbuilder=org.jclouds.azure.storage.blob.AzureBlobPropertiesBuilder
67   * </pre>
68   * 
69   * @author Adrian Cole
70   */
71  public class RestContextFactory {
72  
73     public static <S, A> RestContextSpec<S, A> contextSpec(String provider, String endpoint, String apiVersion,
74           String iso3166Codes, String identity, String credential, Class<S> sync, Class<A> async,
75           Class<PropertiesBuilder> propertiesBuilderClass, Class<RestContextBuilder<S, A>> contextBuilderClass,
76           Iterable<Module> modules) {
77        return new RestContextSpec<S, A>(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async,
78              propertiesBuilderClass, contextBuilderClass, modules);
79     }
80  
81     public static <S, A> RestContextSpec<S, A> contextSpec(String provider, String endpoint, String apiVersion,
82           String iso3166Codes, String identity, String credential, Class<S> sync, Class<A> async) {
83        return new RestContextSpec<S, A>(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async);
84     }
85  
86     @SuppressWarnings({ "unchecked", "rawtypes" })
87     public static <S, A> RestContextSpec<S, A> contextSpec(String provider, String endpoint, String apiVersion,
88           String iso3166Codes, String identity, String credential, Class<S> sync, Class<A> async,
89           Iterable<Module> modules) {
90        return new RestContextSpec<S, A>(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async,
91              PropertiesBuilder.class, (Class) RestContextBuilder.class, modules);
92     }
93  
94     private final static Properties NO_PROPERTIES = new Properties();
95     private final Properties properties;
96  
97     /**
98      * Initializes with the default properties built-in to jclouds. This is typically stored in the
99      * classpath resource {@code rest.properties}
100     * 
101     * @see RestContextFactory#getPropertiesFromResource
102     */
103    public RestContextFactory() {
104       this("/rest.properties");
105    }
106 
107    /**
108     * Initializes with the default properties built-in to jclouds. This is typically stored in the
109     * classpath resource {@code filename}
110     * 
111     * @param filename
112     *           name of the properties file to initialize from
113     * @throws IOException
114     *            if the default properties file cannot be loaded
115     * @see #getPropertiesFromResource
116     */
117    public RestContextFactory(String filename) {
118       this(getPropertiesFromResource(filename));
119    }
120 
121    /**
122     * Loads the default properties that define the {@code RestContext} objects. <h3>properties file
123     * format</h3>
124     * 
125     * Two properties are needed per context:
126     * <ul>
127     * <li>tag.contextbuilder=classname extends RestContextBuilder</li>
128     * <li>tag.propertiesbuilder=classname extends HttpPropertiesBuilder</li>
129     * </ul>
130     * Ex.
131     * 
132     * <pre>
133     * azureblob.contextbuilder=org.jclouds.azure.storage.blob.blobstore.AzureRestContextBuilder
134     * azureblob.propertiesbuilder=org.jclouds.azure.storage.blob.AzureBlobPropertiesBuilder
135     * </pre>
136     * 
137     * @param filename
138     *           name of file to load from in the resource path
139     * @return properties object with these items loaded for each tag
140     */
141    public static Properties getPropertiesFromResource(String filename) {
142       Properties properties = new Properties();
143       try {
144          properties.load(RestContextFactory.class.getResourceAsStream(filename));
145       } catch (IOException e) {
146          propagate(e);
147       }
148       properties.putAll(System.getProperties());
149       return properties;
150    }
151 
152    /**
153     * Initializes the {@code RestContext} definitions from the specified properties.
154     */
155    @Inject
156    public RestContextFactory(Properties properties) {
157       this.properties = properties;
158    }
159 
160    public <S, A> RestContextBuilder<S, A> createContextBuilder(String provider, String identity, String credential) {
161       return createContextBuilder(provider, identity, credential, ImmutableSet.<Module> of(), NO_PROPERTIES);
162    }
163 
164    /**
165     * @see RestContextFactory#createContextBuilder(String, Properties, Iterable<? extends Module>,
166     *      Properties)
167     */
168    public <S, A> RestContextBuilder<S, A> createContextBuilder(String provider, Properties overrides) {
169       return createContextBuilder(provider, null, null, ImmutableSet.<Module> of(), overrides);
170    }
171 
172    /**
173     * @see RestContextFactory#createContextBuilder(String, Properties, Iterable<? extends Module>,
174     *      Properties)
175     */
176    public <S, A> RestContextBuilder<S, A> createContextBuilder(String provider, Iterable<Module> modules) {
177       return createContextBuilder(provider, null, null, modules, NO_PROPERTIES);
178    }
179 
180    /**
181     * 
182     * Identity will be found by searching {@code jclouds.identity} failing that
183     * {@code provider.identity} where provider corresponds to the parameter. Same pattern is used
184     * for credential ({@code jclouds.credential} failing that {@code provider.credential}).
185     * 
186     * @param <S>
187     *           Type of the provider specific client
188     * @param <A>
189     *           Type of the provide specific async client (same as above, yet all methods return
190     *           {@code Future} results)
191     * @param provider
192     *           name of the provider (ex. s3, bluelock, etc.)
193     * @param wiring
194     *           defines how objects are bound to interfaces, pass in here to override this, or
195     *           specify service implementations.
196     * @param overrides
197     *           properties to pass to the context.
198     */
199    public <S, A> RestContextBuilder<S, A> createContextBuilder(String provider, Iterable<? extends Module> wiring,
200          Properties overrides) {
201       return createContextBuilder(provider, null, null, wiring, overrides);
202    }
203 
204    @SuppressWarnings("unchecked")
205    public <S, A> RestContextBuilder<S, A> createContextBuilder(String provider, @Nullable String identity,
206          @Nullable String credential, Properties properties) {
207       return createContextBuilder(provider, identity, credential, EMPTY_LIST, properties);
208    }
209 
210    public <S, A> RestContextBuilder<S, A> createContextBuilder(String provider, @Nullable String identity,
211          @Nullable String credential, Iterable<? extends Module> wiring) {
212       return createContextBuilder(provider, identity, credential, wiring, NO_PROPERTIES);
213    }
214 
215    /**
216     * Creates a new remote context.
217     * 
218     * @param provider
219     * @param identity
220     *           nullable, if credentials are present in the overrides
221     * @param credential
222     *           nullable, if credentials are present in the overrides
223     * @param wiring
224     *           Configuration you'd like to pass to the context. Ex. ImmutableSet.<Module>of(new
225     *           ExecutorServiceModule(myexecutor))
226     * @param overrides
227     *           properties to override defaults with.
228     * @return initialized context ready for use
229     */
230    public <S, A> RestContextBuilder<S, A> createContextBuilder(String providerName, @Nullable String identity,
231          @Nullable String credential, Iterable<? extends Module> wiring, Properties _overrides) {
232       checkNotNull(wiring, "wiring");
233       RestContextSpec<S, A> contextSpec = createContextSpec(providerName, identity, credential, wiring, _overrides);
234       return createContextBuilder(contextSpec, _overrides);
235    }
236 
237    public static Properties toProperties(RestContextSpec<?, ?> contextSpec) {
238       checkNotNull(contextSpec, "contextSpec");
239 
240       Properties props = new Properties();
241 
242       props.setProperty(contextSpec.provider + ".endpoint", contextSpec.endpoint);
243       props.setProperty(contextSpec.provider + ".apiversion", contextSpec.apiVersion);
244       props.setProperty(contextSpec.provider + "." + LocationConstants.ISO3166_CODES, contextSpec.iso3166Codes);
245       props.setProperty(contextSpec.provider + ".identity", contextSpec.identity);
246       if (contextSpec.credential != null)
247          props.setProperty(contextSpec.provider + ".credential", contextSpec.credential);
248       if (contextSpec.sync != null) {
249          props.setProperty(contextSpec.provider + ".sync", contextSpec.sync.getName());
250          props.setProperty(contextSpec.provider + ".async", checkNotNull(contextSpec.async, "contextSpec.async")
251                .getName());
252       } else {
253          props.setProperty(contextSpec.provider + ".contextbuilder",
254                checkNotNull(contextSpec.contextBuilderClass, "contextSpec.contextBuilderClass").getName());
255 
256          props.setProperty(contextSpec.provider + ".propertiesbuilder",
257                checkNotNull(contextSpec.propertiesBuilderClass, "contextSpec.propertiesBuilderClass").getName());
258       }
259       if (size(contextSpec.modules) > 0) {
260          props.setProperty(contextSpec.provider + ".modules",
261                Joiner.on(',').join(transform(contextSpec.modules, new Function<Module, String>() {
262 
263                   @Override
264                   public String apply(Module from) {
265                      return from.getClass().getName();
266                   }
267 
268                })));
269       }
270       return props;
271    }
272 
273    @SuppressWarnings("unchecked")
274    public <S, A> RestContextSpec<S, A> createContextSpec(String providerName, String identity, String credential,
275          Properties _overrides) {
276       return createContextSpec(providerName, identity, credential, EMPTY_LIST, _overrides);
277    }
278 
279    @SuppressWarnings("unchecked")
280    public <S, A> RestContextSpec<S, A> createContextSpec(String providerName, String identity, String credential,
281          Iterable<? extends Module> wiring, Properties _overrides) {
282       checkNotNull(providerName, "providerName");
283       checkNotNull(_overrides, "overrides");
284 
285       Properties props = new Properties();
286       props.putAll(this.properties);
287       props.putAll(_overrides);
288 
289       String endpoint = props.getProperty(providerName + "." + LocationConstants.ENDPOINT, null);
290       String iso3166Codes = props.getProperty(providerName + "." + LocationConstants.ISO3166_CODES, null);
291       String apiVersion = props.getProperty(providerName + ".apiversion", null);
292       identity = props.getProperty(providerName + ".identity", props.getProperty("jclouds.identity", identity));
293       credential = loadCredentialOrDefault(props, providerName + ".credential",
294             loadCredentialOrDefault(props, "jclouds.credential", credential));
295       String syncClassName = props.getProperty(providerName + ".sync", null);
296       String asyncClassName = props.getProperty(providerName + ".async", null);
297       Iterable<Module> modules = concat(Modules2.modulesForProviderInProperties(providerName, props), wiring);
298 
299       Class<RestContextBuilder<S, A>> contextBuilderClass;
300       Class<PropertiesBuilder> propertiesBuilderClass;
301       Class<S> sync;
302       Class<A> async;
303       try {
304          contextBuilderClass = Providers.resolveContextBuilderClass(providerName, props);
305          propertiesBuilderClass = Providers.resolvePropertiesBuilderClass(providerName, props);
306          sync = (Class<S>) (syncClassName != null ? Class.forName(syncClassName) : null);
307          async = (Class<A>) (asyncClassName != null ? Class.forName(asyncClassName) : null);
308       } catch (IllegalArgumentException e) {
309          throw new IllegalArgumentException(
310                String.format(
311                      "The specified provider \"%s\" is either not configured or supported. Currently configured providers are:%n  %s%nCheck this list, as the provider name may have changed. If you are sure that provider name is correct, check that your project has a dependency on org.jclouds.provider/%s, or on org.jclouds/jclouds-all.",
312                      providerName, Providers.getSupportedProviders(), providerName), e);
313       } catch (Exception e) {
314          propagate(e);
315          assert false : "exception should have propogated " + e;
316          return null;
317       }
318       RestContextSpec<S, A> contextSpec = new RestContextSpec<S, A>(providerName, endpoint, apiVersion, iso3166Codes,
319             identity, credential, sync, async, propertiesBuilderClass, contextBuilderClass, modules);
320       return contextSpec;
321    }
322 
323    static String loadCredentialOrDefault(Properties properties, String property, String credential) {
324       if (properties.containsKey(property))
325          return properties.getProperty(property);
326       else if (properties.containsKey(property + ".resource"))
327          try {
328             return Strings2.toStringAndClose(RestContextFactory.class.getResourceAsStream(properties
329                   .getProperty(property + ".resource")));
330          } catch (IOException e) {
331             throw new RuntimeException("error reading resource: " + properties.getProperty(property + ".resource"));
332          }
333       else if (properties.containsKey(property + ".file"))
334          try {
335             return Files.toString(new File(properties.getProperty(property + ".file")), Charsets.UTF_8);
336          } catch (IOException e) {
337             throw new RuntimeException("error reading file: " + properties.getProperty(property + ".file"));
338          }
339       else
340          return credential;
341    }
342 
343    public static <S, A> RestContextBuilder<S, A> createContextBuilder(RestContextSpec<S, A> contextSpec) {
344       return createContextBuilder(contextSpec, NO_PROPERTIES);
345    }
346 
347    @SuppressWarnings("unchecked")
348    public static <S, A> RestContextBuilder<S, A> createContextBuilder(RestContextSpec<S, A> contextSpec,
349          Properties overrides) {
350       return createContextBuilder(contextSpec, EMPTY_LIST, overrides);
351    }
352 
353    public static <S, A> RestContextBuilder<S, A> createContextBuilder(RestContextSpec<S, A> contextSpec,
354          Iterable<Module> modules) {
355       return createContextBuilder(contextSpec, modules, NO_PROPERTIES);
356    }
357 
358    public static <S, A> RestContextBuilder<S, A> createContextBuilder(RestContextSpec<S, A> contextSpec,
359          Iterable<Module> modules, Properties overrides) {
360       try {
361          PropertiesBuilder builder = contextSpec.propertiesBuilderClass.getConstructor(Properties.class).newInstance(
362                overrides);
363 
364          builder.provider(contextSpec.provider);
365          if (contextSpec.apiVersion != null)
366             builder.apiVersion(contextSpec.apiVersion);
367          if (contextSpec.iso3166Codes != null)
368             builder.iso3166Codes(Splitter.on('.').split(contextSpec.iso3166Codes));
369          if (contextSpec.identity != null)
370             builder.credentials(contextSpec.identity, contextSpec.credential);
371          if (contextSpec.endpoint != null)
372             builder.endpoint(contextSpec.endpoint);
373 
374          RestContextBuilder<S, A> contextBuilder = Providers.initContextBuilder(contextSpec.contextBuilderClass,
375                contextSpec.sync, contextSpec.async, builder.build());
376 
377          contextBuilder.withModules(concat(modules, contextSpec.modules));
378 
379          return contextBuilder;
380       } catch (Exception e) {
381          return propagateAuthorizationOrOriginalException(e);
382       }
383    }
384 
385    /**
386     * @see RestContextFactory#createContextBuilder(String, String, String)
387     */
388    public <S, A> RestContext<S, A> createContext(String provider, String identity, String credential) {
389       RestContextBuilder<S, A> builder = createContextBuilder(provider, identity, credential);
390       return buildContextUnwrappingExceptions(builder);
391    }
392 
393    public static <S, A> RestContext<S, A> buildContextUnwrappingExceptions(RestContextBuilder<S, A> builder) {
394       try {
395          return builder.buildContext();
396       } catch (Exception e) {
397          return propagateAuthorizationOrOriginalException(e);
398       }
399    }
400 
401    /**
402     * @see RestContextFactory#createContextBuilder(String, Properties)
403     */
404    public <S, A> RestContext<S, A> createContext(String provider, Properties overrides) {
405       RestContextBuilder<S, A> builder = createContextBuilder(provider, overrides);
406       return buildContextUnwrappingExceptions(builder);
407    }
408 
409    /**
410     * @see RestContextFactory#createContextBuilder(String, Iterable)
411     */
412    public <S, A> RestContext<S, A> createContext(String provider, Iterable<? extends Module> wiring,
413          Properties overrides) {
414       RestContextBuilder<S, A> builder = createContextBuilder(provider, wiring, overrides);
415       return buildContextUnwrappingExceptions(builder);
416    }
417 
418    /**
419     * @see RestContextFactory#createContextBuilder(String, String,String, Properties)
420     */
421    public <S, A> RestContext<S, A> createContext(String provider, @Nullable String identity,
422          @Nullable String credential, Properties properties) {
423       RestContextBuilder<S, A> builder = createContextBuilder(provider, identity, credential, properties);
424       return buildContextUnwrappingExceptions(builder);
425    }
426 
427    /**
428     * @see RestContextFactory#createContextBuilder(String, String,String, Iterable)
429     */
430    public <S, A> RestContext<S, A> createContext(String provider, @Nullable String identity,
431          @Nullable String credential, Iterable<? extends Module> wiring) {
432       RestContextBuilder<S, A> builder = createContextBuilder(provider, identity, credential, wiring);
433       return buildContextUnwrappingExceptions(builder);
434    }
435 
436    /**
437     * @see RestContextFactory#createContextBuilder(String, String,String, Iterable, Properties)
438     */
439    public <S, A> RestContext<S, A> createContext(String provider, @Nullable String identity,
440          @Nullable String credential, Iterable<? extends Module> wiring, Properties overrides) {
441       RestContextBuilder<S, A> builder = createContextBuilder(provider, identity, credential, wiring, overrides);
442       return buildContextUnwrappingExceptions(builder);
443    }
444 
445    /**
446     * @see RestContextFactory#createContextBuilder(RestContextSpec)
447     */
448    public static <S, A> RestContext<S, A> createContext(RestContextSpec<S, A> contextSpec) {
449       RestContextBuilder<S, A> builder = createContextBuilder(contextSpec);
450       return buildContextUnwrappingExceptions(builder);
451    }
452 
453    /**
454     * @see RestContextFactory#createContextBuilder(RestContextSpec, Properties)
455     */
456    public static <S, A> RestContext<S, A> createContext(RestContextSpec<S, A> contextSpec, Properties overrides) {
457       RestContextBuilder<S, A> builder = createContextBuilder(contextSpec, overrides);
458       return buildContextUnwrappingExceptions(builder);
459    }
460 
461    /**
462     * @see RestContextFactory#createContextBuilder(RestContextSpec, Iterable)
463     */
464    public static <S, A> RestContext<S, A> createContext(RestContextSpec<S, A> contextSpec, Iterable<Module> modules) {
465       RestContextBuilder<S, A> builder = createContextBuilder(contextSpec, modules);
466       return buildContextUnwrappingExceptions(builder);
467    }
468 
469    /**
470     * @see RestContextFactory#createContextBuilder(RestContextSpec, Iterable, Properties)
471     */
472    public static <S, A> RestContext<S, A> createContext(RestContextSpec<S, A> contextSpec, Iterable<Module> modules,
473          Properties overrides) {
474       RestContextBuilder<S, A> builder = createContextBuilder(contextSpec, modules, overrides);
475       return buildContextUnwrappingExceptions(builder);
476    }
477 }