/** * Copyright (C) 2009-2015 Dell, Inc. * See annotations for authorship information * * ==================================================================== * 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 org.dasein.cloud; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Properties; /** * Represents a core cloud independent of account-specific connectivity information. You can pre-register cloud objects * at system start up time and use the {@link #getInstance(String)} method to fetch the clouds during the system * life cycle. Clouds are uniquely identified by their API endpoints. * <p>Created by George Reese: 2/27/14 8:40 PM</p> * @author George Reese * @version 2014.03 initial version (issue #123) * @since 2014.03 */ public class Cloud { static private HashMap<String,Cloud> clouds = new HashMap<String, Cloud>(); /** * Fetches a cloud object using its {@link #getEndpoint()} as its identifying value. If no cloud has been registered * under that endpoint, this method will return <code>null</code>. * @param endpoint the API endpoint of the desired cloud * @return any cloud matching the specified endpoint */ static public @Nullable Cloud getInstance(@Nonnull String endpoint) { return clouds.get(endpoint); } /** * Registers a cloud with the specified state information under the named endpoint. If a cloud is already registered * under that endpoint, this method will simply return the original as if it were a call to {@link #getInstance(String)}. * @param providerName the name of the organization providing the cloud service (e.g. Amazon) * @param cloudName the name of the cloud service being provided (e.g. AWS) * @param endpoint the bootstrap endpoint for any API requests * @param providerClass the Dasein Cloud implementation of the {@link org.dasein.cloud.CloudProvider} abstract class supporting connectivity to this cloud * @return a cloud matching the specified state information with that cloud cached for future reference */ static public @Nonnull Cloud register(@Nonnull String providerName, @Nonnull String cloudName, @Nonnull String endpoint, @Nonnull Class<? extends CloudProvider> providerClass) { if( clouds.containsKey(endpoint) ) { return clouds.get(endpoint); } Cloud cloud = new Cloud(); cloud.endpoint = endpoint; cloud.cloudName = cloudName; cloud.providerClass = providerClass; cloud.providerName = providerName; clouds.put(endpoint, cloud); return cloud; } private String cloudName; private String endpoint; private Class<? extends CloudProvider> providerClass; private String providerName; private Cloud() { } /** * Constructs an instance of the Dasein Cloud {@link org.dasein.cloud.CloudProvider} abstract class supporting * connectivity to this cloud. The resulting provider is NOT connected to the cloud. This method exists * solely to support meta-data queries independent of any particular connection. * @return an unconnected Dasein Cloud provider supporting this cloud * @throws IllegalAccessException there is an error in the Dasein Cloud {@link org.dasein.cloud.CloudProvider} implementation for this cloud that prevents it from loading (likely private constructor) * @throws InstantiationException there is an error in the Dasein Cloud {@link org.dasein.cloud.CloudProvider} implementation for this cloud that prevents it from loading (likely an error in the constructor) */ public @Nonnull CloudProvider buildProvider() throws IllegalAccessException, InstantiationException { return providerClass.newInstance(); } /** * Creates a context object based on values specified in a Java {@link Properties} file. * Creates a context against this cloud with the specified configuration information. This context can then be used * to connect to the cloud for API requests * @param forAccountNumber the account number of the account with the cloud provider * @param inRegionId the region to which a connection should be made * @param configurationProperties a properties object containing values for the required fields for connecting to this cloud * @return a configured context ready to be connected to the cloud via {@link ProviderContext#connect(CloudProvider)} * @throws InternalException an error occurred loading the {@link CloudProvider} implementation for this cloud or UTF-8 is not supported */ public @Nonnull ProviderContext createContext(@Nonnull String forAccountNumber, @Nonnull String inRegionId, @Nonnull Properties configurationProperties) throws InternalException { try { List<ContextRequirements.Field> fields = buildProvider().getContextRequirements().getConfigurableValues(); ArrayList<ProviderContext.Value> values = new ArrayList<ProviderContext.Value>(); for( ContextRequirements.Field f : fields ) { if( f.type.equals(ContextRequirements.FieldType.KEYPAIR) ) { String[] parts = new String[2]; parts[0] = configurationProperties.getProperty(f.name + "_public." + forAccountNumber); parts[1] = configurationProperties.getProperty(f.name + "_private." + forAccountNumber); if( parts[0] != null && parts[1] != null ) { values.add(ProviderContext.Value.parseValue(f, parts)); } else if( f.required ) { throw new InternalException("Unable to find configuration value " + f.name + " in the properties"); } } else { String value = configurationProperties.getProperty(f.name + "." + forAccountNumber); if( value != null ) { values.add(ProviderContext.Value.parseValue(f, value)); } else if( f.required ) { throw new InternalException("Unable to find configuration value " + f.name + " in the properties"); } } } return createContext(forAccountNumber, inRegionId, values.toArray(new ProviderContext.Value<?>[values.size()])); } catch( UnsupportedEncodingException e ) { throw new InternalException(e); } catch( InstantiationException e ) { throw new InternalException(e); } catch( IllegalAccessException e ) { throw new InternalException(e); } } /** * Creates a context against this cloud with the specified configuration information. This context can then be used * to connect to the cloud for API requests * @param forAccountNumber the account number of the account with the cloud provider * @param inRegionId the region to which a connection should be made * @param values any configuration values to provide (to see what values are expected, query {@link org.dasein.cloud.CloudProvider#getContextRequirements()}) * @return a configured context ready to be connected to the cloud via {@link ProviderContext#connect(CloudProvider)} */ public @Nonnull ProviderContext createContext(@Nonnull String forAccountNumber, @Nonnull String inRegionId, ProviderContext.Value<?> ... values) { return ProviderContext.getContext(this, forAccountNumber, inRegionId, values); } @Override public boolean equals(Object other) { return other != null && (other == this || getClass().getName().equals(other.getClass().getName()) && endpoint.equals(((Cloud) other).endpoint)); } /** * @return the name of the cloud */ public @Nonnull String getCloudName() { return cloudName; } /** * @return the endpoint to which API connections are initially made */ public @Nonnull String getEndpoint() { return endpoint; } /** * @return the class for the {@link org.dasein.cloud.CloudProvider} implementation for this cloud */ public @Nonnull Class<? extends CloudProvider> getProviderClass() { return providerClass; } /** * @return the name of the organization providing the cloud services behind this cloud */ public @Nonnull String getProviderName() { return providerName; } @Override public int hashCode() { return endpoint.hashCode(); } @Override public @Nonnull String toString() { return endpoint; } }