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