/*
* Copyright 2014 Stormpath, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.stormpath.sdk.impl.client;
import com.stormpath.sdk.account.Account;
import com.stormpath.sdk.account.AccountCriteria;
import com.stormpath.sdk.account.AccountList;
import com.stormpath.sdk.api.ApiKey;
import com.stormpath.sdk.application.Application;
import com.stormpath.sdk.application.ApplicationCriteria;
import com.stormpath.sdk.application.ApplicationList;
import com.stormpath.sdk.application.CreateApplicationRequest;
import com.stormpath.sdk.cache.CacheManager;
import com.stormpath.sdk.client.AuthenticationScheme;
import com.stormpath.sdk.client.Client;
import com.stormpath.sdk.impl.api.ApiKeyResolver;
import com.stormpath.sdk.impl.authc.credentials.ClientCredentials;
import com.stormpath.sdk.client.Proxy;
import com.stormpath.sdk.directory.CreateDirectoryRequest;
import com.stormpath.sdk.directory.Directory;
import com.stormpath.sdk.directory.DirectoryCriteria;
import com.stormpath.sdk.directory.DirectoryList;
import com.stormpath.sdk.ds.DataStore;
import com.stormpath.sdk.group.GroupCriteria;
import com.stormpath.sdk.group.GroupList;
import com.stormpath.sdk.impl.ds.DefaultDataStore;
import com.stormpath.sdk.impl.http.RequestExecutor;
import com.stormpath.sdk.impl.http.authc.RequestAuthenticatorFactory;
import com.stormpath.sdk.impl.tenant.DefaultTenantResolver;
import com.stormpath.sdk.impl.tenant.TenantResolver;
import com.stormpath.sdk.impl.util.BaseUrlResolver;
import com.stormpath.sdk.lang.Assert;
import com.stormpath.sdk.lang.Classes;
import com.stormpath.sdk.organization.*;
import com.stormpath.sdk.query.Options;
import com.stormpath.sdk.resource.Resource;
import com.stormpath.sdk.resource.ResourceException;
import com.stormpath.sdk.saml.*;
import com.stormpath.sdk.tenant.Tenant;
import com.stormpath.sdk.tenant.TenantOptions;
import java.lang.reflect.Constructor;
import java.util.Map;
/**
* The default {@link Client} implementation.
* <h3>DataStore API</h3>
* <p>As of 0.8, this class implements the {@link
* DataStore} interface, but this implementation merely acts as a wrapper to the underlying 'real' {@code DataStore}
* instance. This is a convenience mechanism to eliminate the constant need to call {@code client.getDataStore()} every
* time one needs to instantiate or look up a Resource.</p>
*
* @see <a href="http://www.stormpath.com/docs/quickstart/connect">Communicating with Stormpath: Get your API Key</a>
* @since 1.0.alpha
*/
public class DefaultClient implements Client {
private final DataStore dataStore;
private TenantResolver tenantResolver;
/**
* Instantiates a new Client instance that will communicate with the Stormpath REST API. See the class-level
* JavaDoc for a usage example.
*
* @param clientCredentials the Stormpath account credentials that will be used to authenticate the client with
* Stormpath's API server
* @param apiKeyResolver Stormpath API Key resolver
* @param baseUrlResolver Stormpath base URL resolver
* @param proxy the HTTP proxy to be used when communicating with the Stormpath API server (can be
* null)
* @param cacheManager the {@link com.stormpath.sdk.cache.CacheManager} that should be used to cache
* Stormpath REST resources (can be null)
* @param authenticationScheme the HTTP authentication scheme to be used when communicating with the Stormpath API
* server (can be null)
* @since 1.2.0
*/
public DefaultClient(ClientCredentials clientCredentials, ApiKeyResolver apiKeyResolver, BaseUrlResolver baseUrlResolver, Proxy proxy, CacheManager cacheManager, AuthenticationScheme authenticationScheme, RequestAuthenticatorFactory requestAuthenticatorFactory, int connectionTimeout) {
Assert.notNull(clientCredentials, "clientCredentials argument cannot be null.");
Assert.notNull(apiKeyResolver, "apiKeyResolver argument cannot be null.");
Assert.notNull(baseUrlResolver, "baseUrlResolver argument cannot be null.");
Assert.isTrue(connectionTimeout >= 0, "connectionTimeout cannot be a negative number.");
RequestExecutor requestExecutor = createRequestExecutor(clientCredentials, proxy, authenticationScheme, requestAuthenticatorFactory, connectionTimeout);
this.dataStore = createDataStore(requestExecutor, baseUrlResolver, clientCredentials, apiKeyResolver, cacheManager);
this.tenantResolver = new DefaultTenantResolver(dataStore);
}
/**
* Instantiates a new Client instance that will communicate with the Stormpath REST API. See the class-level
* JavaDoc for a usage example.
*
* @param clientCredentials the Stormpath account credentials that will be used to authenticate the client with
* Stormpath's API server
* @param apiKeyResolver Stormpath API Key resolver
* @param baseUrlResolver Stormpath base URL resolver
* @param proxy the HTTP proxy to be used when communicating with the Stormpath API server (can be
* null)
* @param cacheManager the {@link com.stormpath.sdk.cache.CacheManager} that should be used to cache
* Stormpath REST resources (can be null)
* @param authenticationScheme the HTTP authentication scheme to be used when communicating with the Stormpath API
* server (can be null)
* @param tenantResolver resolver which returns current tenant details
* @since 1.2.0
*/
public DefaultClient(ClientCredentials clientCredentials, ApiKeyResolver apiKeyResolver, BaseUrlResolver baseUrlResolver, Proxy proxy, CacheManager cacheManager, AuthenticationScheme authenticationScheme, RequestAuthenticatorFactory requestAuthenticatorFactory, int connectionTimeout, TenantResolver tenantResolver) {
Assert.notNull(clientCredentials, "clientCredentials argument cannot be null.");
Assert.notNull(apiKeyResolver, "apiKeyResolver argument cannot be null.");
Assert.notNull(baseUrlResolver, "baseUrlResolver argument cannot be null.");
Assert.isTrue(connectionTimeout >= 0, "connectionTimeout cannot be a negative number.");
Assert.notNull(tenantResolver, "tenantResolver argument cannot be null.");
RequestExecutor requestExecutor = createRequestExecutor(clientCredentials, proxy, authenticationScheme, requestAuthenticatorFactory, connectionTimeout);
this.dataStore = createDataStore(requestExecutor, baseUrlResolver, clientCredentials, apiKeyResolver, cacheManager);
this.tenantResolver = tenantResolver;
}
/**
* @since 1.2.0
*/
protected DataStore createDataStore(RequestExecutor requestExecutor, BaseUrlResolver baseUrlResolver, ClientCredentials clientCredentials, ApiKeyResolver apiKeyResolver, CacheManager cacheManager) {
return new DefaultDataStore(requestExecutor, baseUrlResolver, clientCredentials, apiKeyResolver, cacheManager);
}
@Override
public Tenant getCurrentTenant() {
return this.tenantResolver.getCurrentTenant();
}
@Override
public ApiKey getApiKey() {
return this.dataStore.getApiKey();
}
@Override
public CacheManager getCacheManager() {
return this.dataStore.getCacheManager();
}
@Override
public DataStore getDataStore() {
return this.dataStore;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private RequestExecutor createRequestExecutor(ClientCredentials clientCredentials, Proxy proxy, AuthenticationScheme authenticationScheme, RequestAuthenticatorFactory requestAuthenticatorFactory, int connectionTimeout) {
String className = "com.stormpath.sdk.impl.http.httpclient.HttpClientRequestExecutor";
Class requestExecutorClass;
if (Classes.isAvailable(className)) {
requestExecutorClass = Classes.forName(className);
} else {
//we might be able to check for other implementations in the future, but for now, we only support
//HTTP calls via the HttpClient. Throw an exception:
String msg = "Unable to find the '" + className + "' implementation on the classpath. Please ensure you " +
"have added the stormpath-sdk-httpclient .jar file to your runtime classpath.";
throw new RuntimeException(msg);
}
Constructor<RequestExecutor> ctor = Classes.getConstructor(requestExecutorClass, ClientCredentials.class, Proxy.class, AuthenticationScheme.class, RequestAuthenticatorFactory.class, Integer.class);
return Classes.instantiate(ctor, clientCredentials, proxy, authenticationScheme, requestAuthenticatorFactory, connectionTimeout);
}
// ========================================================================
// DataStore methods (delegate to underlying DataStore instance)
// ========================================================================
/**
* Delegates to the internal {@code dataStore} instance. This is a convenience mechanism to eliminate the constant
* need to call {@code client.getDataStore()} every time one needs to instantiate Resource.
*
* @param clazz the Resource class to instantiate.
* @param <T> the Resource sub-type
* @return a new instance of the specified Resource.
*/
@Override
public <T extends Resource> T instantiate(Class<T> clazz) {
return this.dataStore.instantiate(clazz);
}
/**
* Delegates to the internal {@code dataStore} instance. This is a convenience mechanism to eliminate the constant
* need to call {@code client.getDataStore()} every time one needs to look up a Resource.
*
* @param href the resource URL of the resource to retrieve
* @param clazz the {@link Resource} sub-interface to instantiate
* @param <T> type parameter indicating the returned value is a {@link Resource} instance.
* @return an instance of the specified class based on the data returned from the specified {@code href} URL.
*/
@Override
public <T extends Resource> T getResource(String href, Class<T> clazz) {
return this.dataStore.getResource(href, clazz);
}
/**
* Delegates to the internal {@code dataStore} instance. This is a convenience mechanism to eliminate the constant
* need to call {@code client.getDataStore()} every time one needs to look up a Resource.
*
* @param href the URL of the resource to retrieve
* @param clazz the {@link Resource} sub-interface to instantiate
* @param options the {@link Options} sub-interface with the properties to expand
* @param <T> type parameter indicating the returned value is a {@link Resource} instance.
* @return an instance of the specified {@code Class} based on the data returned from the specified {@code href} URL.
* @since 1.0.RC4.6
*/
@Override
public <T extends Resource, O extends Options> T getResource(String href, Class<T> clazz, O options) {
return this.dataStore.getResource(href, clazz, options);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public Application createApplication(Application application) throws ResourceException {
return getCurrentTenant().createApplication(application);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public Application createApplication(CreateApplicationRequest request) throws ResourceException {
return getCurrentTenant().createApplication(request);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public ApplicationList getApplications() {
return getCurrentTenant().getApplications();
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public ApplicationList getApplications(Map<String, Object> queryParams) {
return getCurrentTenant().getApplications(queryParams);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public ApplicationList getApplications(ApplicationCriteria criteria) {
return getCurrentTenant().getApplications(criteria);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public Directory createDirectory(Directory directory) {
return getCurrentTenant().createDirectory(directory);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC7
*/
@Override
public Organization createOrganization(Organization organization) {
return getCurrentTenant().createOrganization(organization);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC7
*/
@Override
public Organization createOrganization(CreateOrganizationRequest request) throws ResourceException {
return getCurrentTenant().createOrganization(request);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC7
*/
@Override
public OrganizationList getOrganizations() {
return getCurrentTenant().getOrganizations();
}
/**
* {@inheritDoc}
*
* @since 1.0.RC7
*/
@Override
public OrganizationList getOrganizations(Map<String, Object> queryParams) {
return getCurrentTenant().getOrganizations(queryParams);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC7
*/
@Override
public OrganizationList getOrganizations(OrganizationCriteria criteria) {
return getCurrentTenant().getOrganizations(criteria);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public Directory createDirectory(CreateDirectoryRequest createDirectoryRequest) throws ResourceException {
return getCurrentTenant().createDirectory(createDirectoryRequest);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public DirectoryList getDirectories() {
return getCurrentTenant().getDirectories();
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public DirectoryList getDirectories(Map<String, Object> queryParams) {
return getCurrentTenant().getDirectories(queryParams);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public DirectoryList getDirectories(DirectoryCriteria criteria) {
return getCurrentTenant().getDirectories(criteria);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC
*/
@Override
public Account verifyAccountEmail(String token) {
return getCurrentTenant().verifyAccountEmail(token);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC3
*/
@Override
public AccountList getAccounts() {
return getCurrentTenant().getAccounts();
}
/**
* {@inheritDoc}
*
* @since 1.0.RC3
*/
@Override
public AccountList getAccounts(AccountCriteria criteria) {
return getCurrentTenant().getAccounts(criteria);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC3
*/
@Override
public AccountList getAccounts(Map<String, Object> queryParams) {
return getCurrentTenant().getAccounts(queryParams);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC3
*/
@Override
public GroupList getGroups() {
return getCurrentTenant().getGroups();
}
/**
* {@inheritDoc}
*
* @since 1.0.RC3
*/
@Override
public GroupList getGroups(GroupCriteria criteria) {
return getCurrentTenant().getGroups(criteria);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC3
*/
@Override
public GroupList getGroups(Map<String, Object> queryParams) {
return getCurrentTenant().getGroups(queryParams);
}
/**
* {@inheritDoc}
*
* @since 1.0.RC4.6
*/
@Override
public Tenant getCurrentTenant(TenantOptions tenantOptions) {
return this.tenantResolver.getCurrentTenant(tenantOptions);
}
/**
* {@inheritDoc}
*
* @since 1.3.0
*/
@Override
public RegisteredSamlServiceProvider createRegisterdSamlServiceProvider(RegisteredSamlServiceProvider registeredSamlServiceProvider) throws ResourceException {
return getCurrentTenant().createRegisterdSamlServiceProvider(registeredSamlServiceProvider);
}
/**
* {@inheritDoc}
*
* @since 1.3.0
*/
@Override
public RegisteredSamlServiceProviderList getRegisterdSamlServiceProviders() {
return getCurrentTenant().getRegisterdSamlServiceProviders();
}
/**
* {@inheritDoc}
*
* @since 1.3.0
*/
@Override
public RegisteredSamlServiceProviderList getRegisterdSamlServiceProviders(RegisteredSamlServiceProviderCriteria criteria) {
return getCurrentTenant().getRegisterdSamlServiceProviders(criteria);
}
}