/* * Copyright 2013-2014 the original author or authors. * * 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.springframework.cloud.aws.context.config.xml; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.cloud.aws.core.credentials.CredentialsProviderFactoryBean; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; import org.w3c.dom.Element; import java.util.ArrayList; import java.util.List; import static org.springframework.cloud.aws.core.config.AmazonWebserviceClientConfigurationUtils.replaceDefaultCredentialsProvider; /** * {@link org.springframework.beans.factory.xml.BeanDefinitionParser} implementation which parses the * <context-credentials/> Element * * @author Agim Emruli * @since 1.0 */ class ContextCredentialsBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { private static final String STATIC_CREDENTIALS_PROVIDER_BEAN_CLASS_NAME = "com.amazonaws.auth.AWSStaticCredentialsProvider"; private static final String INSTANCE_CREDENTIALS_PROVIDER_BEAN_CLASS_NAME = "com.amazonaws.auth.InstanceProfileCredentialsProvider"; private static final String PROFILE_CREDENTIALS_PROVIDER_BEAN_CLASS_NAME = "com.amazonaws.auth.profile.ProfileCredentialsProvider"; private static final String ACCESS_KEY_ATTRIBUTE_NAME = "access-key"; private static final String SECRET_KEY_ATTRIBUTE_NAME = "secret-key"; @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { return CredentialsProviderFactoryBean.CREDENTIALS_PROVIDER_BEAN_NAME; } @Override protected Class<?> getBeanClass(Element element) { return CredentialsProviderFactoryBean.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { if (parserContext.getRegistry().containsBeanDefinition(CredentialsProviderFactoryBean.CREDENTIALS_PROVIDER_BEAN_NAME)) { parserContext.getReaderContext().error("Multiple <context-credentials/> detected. The <context-credentials/> is only allowed once per application context", element); } List<Element> elements = DomUtils.getChildElements(element); ManagedList<BeanDefinition> credentialsProviders = new ManagedList<>(elements.size()); for (Element credentialsProviderElement : elements) { if ("simple-credentials".equals(credentialsProviderElement.getLocalName())) { credentialsProviders.add(getCredentialsProvider(STATIC_CREDENTIALS_PROVIDER_BEAN_CLASS_NAME, getCredentials(credentialsProviderElement, parserContext))); } if ("instance-profile-credentials".equals(credentialsProviderElement.getLocalName())) { credentialsProviders.add(getCredentialsProvider(INSTANCE_CREDENTIALS_PROVIDER_BEAN_CLASS_NAME)); } if ("profile-credentials".equals(credentialsProviderElement.getLocalName())) { credentialsProviders.add(getCredentialsProvider(PROFILE_CREDENTIALS_PROVIDER_BEAN_CLASS_NAME, getProfileConfiguration(credentialsProviderElement).toArray())); } } builder.addConstructorArgValue(credentialsProviders); replaceDefaultCredentialsProvider(parserContext.getRegistry(), CredentialsProviderFactoryBean.CREDENTIALS_PROVIDER_BEAN_NAME); } private static BeanDefinition getCredentialsProvider(String credentialsProviderClassName, Object... constructorArg) { BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(credentialsProviderClassName); for (Object o : constructorArg) { beanDefinitionBuilder.addConstructorArgValue(o); } return beanDefinitionBuilder.getBeanDefinition(); } /** * Creates a bean definition for the credentials object. This methods creates a bean definition instead of the direct * implementation to allow property place holder to change any place holder used for the access or secret key. * * @param credentialsProviderElement * - The element that contains the credentials attributes ACCESS_KEY_ATTRIBUTE_NAME and SECRET_KEY_ATTRIBUTE_NAME * @param parserContext * - Used to report any errors if there is no ACCESS_KEY_ATTRIBUTE_NAME or SECRET_KEY_ATTRIBUTE_NAME available with * a * valid value * @return - the bean definition with an {@link com.amazonaws.auth.BasicAWSCredentials} class */ private static BeanDefinition getCredentials(Element credentialsProviderElement, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition("com.amazonaws.auth.BasicAWSCredentials"); builder.addConstructorArgValue(getAttributeValue(ACCESS_KEY_ATTRIBUTE_NAME, credentialsProviderElement, parserContext)); builder.addConstructorArgValue(getAttributeValue(SECRET_KEY_ATTRIBUTE_NAME, credentialsProviderElement, parserContext)); return builder.getBeanDefinition(); } private static List<String> getProfileConfiguration(Element element) { List<String> constructorArguments = new ArrayList<>(2); if (StringUtils.hasText(element.getAttribute("profilePath"))) { constructorArguments.add(element.getAttribute("profilePath")); } if (StringUtils.hasText(element.getAttribute("profileName"))) { constructorArguments.add(element.getAttribute("profileName")); } return constructorArguments; } /** * Returns the attribute value and reports an error if the attribute value is null or empty. Normally the reported * error leads into an exception which will be thrown through the {@link org.springframework.beans.factory.parsing.ProblemReporter} * implementation. * * @param attribute * - The name of the attribute which will be valuated * @param element * - The element that contains the attribute * @param parserContext * - The parser context used to report errors * @return - The attribute value */ private static String getAttributeValue(String attribute, Element element, ParserContext parserContext) { String attributeValue = element.getAttribute(attribute); if (!StringUtils.hasText(attributeValue)) { parserContext.getReaderContext().error("The '" + attribute + "' attribute must not be empty", element); } return attributeValue; } }