1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.jclouds.rest;
20
21 import static com.google.common.base.Preconditions.checkNotNull;
22 import static com.google.common.base.Predicates.equalTo;
23 import static com.google.common.base.Predicates.instanceOf;
24 import static com.google.common.base.Predicates.not;
25 import static com.google.common.base.Splitter.on;
26 import static com.google.common.collect.Iterables.addAll;
27 import static com.google.common.collect.Iterables.any;
28 import static com.google.common.collect.Iterables.filter;
29 import static com.google.inject.Scopes.SINGLETON;
30 import static com.google.inject.name.Names.bindProperties;
31 import static com.google.inject.util.Types.newParameterizedType;
32 import static org.jclouds.Constants.PROPERTY_API;
33 import static org.jclouds.Constants.PROPERTY_API_VERSION;
34 import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
35 import static org.jclouds.Constants.PROPERTY_ENDPOINT;
36 import static org.jclouds.Constants.PROPERTY_IDENTITY;
37 import static org.jclouds.Constants.PROPERTY_ISO3166_CODES;
38 import static org.jclouds.Constants.PROPERTY_PROVIDER;
39 import static org.jclouds.Constants.PROPERTY_TIMEOUTS_PREFIX;
40
41 import java.net.URI;
42 import java.util.*;
43 import java.util.Map.Entry;
44
45 import javax.inject.Inject;
46 import javax.inject.Named;
47 import javax.inject.Singleton;
48
49 import com.google.common.collect.*;
50 import org.jclouds.concurrent.MoreExecutors;
51 import org.jclouds.concurrent.SingleThreaded;
52 import org.jclouds.concurrent.config.ConfiguresExecutorService;
53 import org.jclouds.concurrent.config.ExecutorServiceModule;
54 import org.jclouds.http.RequiresHttp;
55 import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
56 import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
57 import org.jclouds.location.Iso3166;
58 import org.jclouds.location.Provider;
59 import org.jclouds.location.config.ProvideIso3166CodesByLocationIdViaProperties;
60 import org.jclouds.logging.config.LoggingModule;
61 import org.jclouds.logging.jdk.config.JDKLoggingModule;
62 import org.jclouds.rest.annotations.Api;
63 import org.jclouds.rest.annotations.ApiVersion;
64 import org.jclouds.rest.annotations.Credential;
65 import org.jclouds.rest.annotations.Identity;
66 import org.jclouds.rest.config.CredentialStoreModule;
67 import org.jclouds.rest.config.RestClientModule;
68 import org.jclouds.rest.config.RestModule;
69 import org.jclouds.rest.internal.RestContextImpl;
70
71 import com.google.common.annotations.VisibleForTesting;
72 import com.google.common.base.Predicate;
73 import com.google.inject.AbstractModule;
74 import com.google.inject.Guice;
75 import com.google.inject.Injector;
76 import com.google.inject.Key;
77 import com.google.inject.Module;
78 import com.google.inject.Provides;
79 import com.google.inject.TypeLiteral;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 public class RestContextBuilder<S, A> {
95
96 static class BindPropertiesAndPrincipalContext extends AbstractModule {
97 protected Properties properties;
98
99 protected BindPropertiesAndPrincipalContext(Properties properties) {
100 this.properties = checkNotNull(properties, "properties");
101 }
102
103 @Provides
104 @Singleton
105 @Named("CONSTANTS")
106 protected Multimap<String, String> constants() {
107 ImmutableMultimap.Builder<String, String> builder = ImmutableMultimap.<String, String> builder();
108 for (Entry<Object, Object> entry : properties.entrySet())
109 if (entry.getValue() != null)
110 builder.put(entry.getKey().toString(), entry.getValue().toString());
111 return LinkedHashMultimap.create(builder.build());
112 }
113
114 @Provides
115 @Singleton
116 @Named("TIMEOUTS")
117 protected Map<String, Long> timeouts() {
118 final ImmutableMap.Builder<String, Long> builder = ImmutableMap.<String, Long> builder();
119 for (final Entry<Object, Object> entry : properties.entrySet()) {
120 final String key = String.valueOf(entry.getKey());
121 if (key.startsWith(PROPERTY_TIMEOUTS_PREFIX) && entry.getValue() != null) {
122 try {
123 final Long val = Long.valueOf(String.valueOf(entry.getValue()));
124 builder.put(key.replaceFirst(PROPERTY_TIMEOUTS_PREFIX, ""), val);
125 } catch (final Throwable t) {}
126 }
127 }
128 return builder.build();
129 }
130
131 @Override
132 protected void configure() {
133 Properties toBind = new Properties();
134 toBind.putAll(checkNotNull(properties, "properties"));
135 toBind.putAll(System.getProperties());
136 bindProperties(binder(), toBind);
137 bind(String.class).annotatedWith(Provider.class).toInstance(
138 checkNotNull(toBind.getProperty(PROPERTY_PROVIDER), PROPERTY_PROVIDER));
139 bind(URI.class).annotatedWith(Provider.class).toInstance(
140 URI.create(checkNotNull(toBind.getProperty(PROPERTY_ENDPOINT), PROPERTY_ENDPOINT)));
141 bind(new TypeLiteral<Set<String>>() {
142 }).annotatedWith(Iso3166.class).toInstance(
143 ImmutableSet.copyOf(filter(on(',').split(
144 checkNotNull(toBind.getProperty(PROPERTY_ISO3166_CODES), PROPERTY_ISO3166_CODES)),
145 not(equalTo("")))));
146 bind(new TypeLiteral<Map<String, Set<String>>>() {
147 }).annotatedWith(Iso3166.class).toProvider(ProvideIso3166CodesByLocationIdViaProperties.class);
148 if (toBind.containsKey(PROPERTY_API))
149 bind(String.class).annotatedWith(Api.class).toInstance(toBind.getProperty(PROPERTY_API));
150 if (toBind.containsKey(PROPERTY_API_VERSION))
151 bind(String.class).annotatedWith(ApiVersion.class).toInstance(toBind.getProperty(PROPERTY_API_VERSION));
152 if (toBind.containsKey(PROPERTY_IDENTITY))
153 bind(String.class).annotatedWith(Identity.class).toInstance(
154 checkNotNull(toBind.getProperty(PROPERTY_IDENTITY), PROPERTY_IDENTITY));
155 if (toBind.containsKey(PROPERTY_CREDENTIAL))
156 bind(String.class).annotatedWith(Credential.class).toInstance(toBind.getProperty(PROPERTY_CREDENTIAL));
157 }
158 }
159
160 protected Properties properties;
161 protected List<Module> modules = new ArrayList<Module>(3);
162 protected Class<A> asyncClientType;
163 protected Class<S> syncClientType;
164
165 @Inject
166 public RestContextBuilder(Class<S> syncClientClass, Class<A> asyncClientClass, Properties properties) {
167 this.asyncClientType = checkNotNull(asyncClientClass, "asyncClientType");
168 this.syncClientType = checkNotNull(syncClientClass, "syncClientType");
169 this.properties = checkNotNull(properties, "properties");
170 }
171
172 public RestContextBuilder<S, A> withModules(Iterable<Module> modules) {
173 addAll(this.modules, modules);
174 return this;
175 }
176
177 public Injector buildInjector() {
178 addContextModule(modules);
179 addClientModuleIfNotPresent(modules);
180 addLoggingModuleIfNotPresent(modules);
181 addHttpModuleIfNeededAndNotPresent(modules);
182 ifHttpConfigureRestOtherwiseGuiceClientFactory(modules);
183 addExecutorServiceIfNotPresent(modules);
184 addCredentialStoreIfNotPresent(modules);
185 modules.add(new BindPropertiesAndPrincipalContext(properties));
186 return Guice.createInjector(modules);
187 }
188
189 @VisibleForTesting
190 protected void addLoggingModuleIfNotPresent(List<Module> modules) {
191 if (!any(modules, instanceOf(LoggingModule.class)))
192 modules.add(new JDKLoggingModule());
193 }
194
195 @VisibleForTesting
196 protected void addHttpModuleIfNeededAndNotPresent(List<Module> modules) {
197 if (defaultOrAtLeastOneModuleRequiresHttp(modules) && nothingConfiguresAnHttpService(modules))
198 modules.add(new JavaUrlHttpCommandExecutorServiceModule());
199 }
200
201 private boolean nothingConfiguresAnHttpService(List<Module> modules) {
202 return (!any(modules, new Predicate<Module>() {
203 public boolean apply(Module input) {
204 return input.getClass().isAnnotationPresent(ConfiguresHttpCommandExecutorService.class);
205 }
206
207 }));
208 }
209
210 @VisibleForTesting
211 protected void addContextModuleIfNotPresent(List<Module> modules) {
212 if (!any(modules, new Predicate<Module>() {
213 public boolean apply(Module input) {
214 return input.getClass().isAnnotationPresent(ConfiguresRestContext.class);
215 }
216
217 })) {
218 addContextModule(modules);
219 }
220 }
221
222 @VisibleForTesting
223 protected void addContextModule(List<Module> modules) {
224 modules.add(new AbstractModule() {
225
226 @SuppressWarnings( { "unchecked", "rawtypes" })
227 @Override
228 protected void configure() {
229 bind(
230 (TypeLiteral) TypeLiteral.get(newParameterizedType(RestContext.class, syncClientType,
231 asyncClientType))).to(
232 TypeLiteral.get(newParameterizedType(RestContextImpl.class, syncClientType, asyncClientType))).in(
233 SINGLETON);
234
235 }
236
237 public String toString() {
238 return String.format("configure rest context %s->%s", syncClientType.getSimpleName(), asyncClientType
239 .getSimpleName());
240 }
241
242 });
243 }
244
245 @VisibleForTesting
246 protected void ifHttpConfigureRestOtherwiseGuiceClientFactory(List<Module> modules) {
247 if (defaultOrAtLeastOneModuleRequiresHttp(modules)) {
248 modules.add(new RestModule());
249 }
250 }
251
252 private boolean defaultOrAtLeastOneModuleRequiresHttp(List<Module> modules) {
253 return atLeastOneModuleRequiresHttp(modules) || !restClientModulePresent(modules);
254 }
255
256 private boolean atLeastOneModuleRequiresHttp(List<Module> modules) {
257 return any(modules, new Predicate<Module>() {
258 public boolean apply(Module input) {
259 return input.getClass().isAnnotationPresent(RequiresHttp.class);
260 }
261 });
262 }
263
264 @VisibleForTesting
265 protected void addClientModuleIfNotPresent(List<Module> modules) {
266 if (!restClientModulePresent(modules)) {
267 addClientModule(modules);
268 }
269 }
270
271 private boolean restClientModulePresent(List<Module> modules) {
272 return any(modules, new Predicate<Module>() {
273 public boolean apply(Module input) {
274 return input.getClass().isAnnotationPresent(ConfiguresRestClient.class);
275 }
276
277 });
278 }
279
280 protected void addClientModule(List<Module> modules) {
281 modules.add(new RestClientModule<S, A>(syncClientType, asyncClientType));
282 }
283
284 @VisibleForTesting
285 protected void addExecutorServiceIfNotPresent(List<Module> modules) {
286 if (!any(modules, new Predicate<Module>() {
287 public boolean apply(Module input) {
288 return input.getClass().isAnnotationPresent(ConfiguresExecutorService.class);
289 }
290 }
291
292 )) {
293 if (any(modules, new Predicate<Module>() {
294 public boolean apply(Module input) {
295 return input.getClass().isAnnotationPresent(SingleThreaded.class);
296 }
297 })) {
298 modules.add(new ExecutorServiceModule(MoreExecutors.sameThreadExecutor(), MoreExecutors
299 .sameThreadExecutor()));
300 } else {
301 modules.add(new ExecutorServiceModule());
302 }
303 }
304 }
305
306 @VisibleForTesting
307 protected void addCredentialStoreIfNotPresent(List<Module> modules) {
308 if (!any(modules, new Predicate<Module>() {
309 public boolean apply(Module input) {
310 return input.getClass().isAnnotationPresent(ConfiguresCredentialStore.class);
311 }
312 }
313
314 )) {
315 modules.add(new CredentialStoreModule());
316 }
317 }
318
319 @VisibleForTesting
320 public Properties getProperties() {
321 return properties;
322 }
323
324 @SuppressWarnings("unchecked")
325 public <T extends RestContext<S, A>> T buildContext() {
326 Injector injector = buildInjector();
327 return (T) injector
328 .getInstance(Key.get(newParameterizedType(RestContext.class, syncClientType, asyncClientType)));
329 }
330 }