/************************************************************************* * Copyright 2009-2013 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.autoscaling.common.internal.tags; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.log4j.Logger; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Property; import org.hibernate.criterion.Restrictions; import com.eucalyptus.autoscaling.common.AutoScalingMetadata; import com.eucalyptus.autoscaling.common.internal.metadata.AutoScalingMetadataNotFoundException; import com.eucalyptus.entities.AbstractOwnedPersistent; import com.eucalyptus.entities.TransactionException; import com.eucalyptus.util.Classes; import com.eucalyptus.auth.principal.OwnerFullName; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; /** * Support functionality for each resource that supports tagging */ public abstract class TagSupport { private static final Logger log = Logger.getLogger( TagSupport.class ); private static final ConcurrentMap<String,TagSupport> supportByResourceType = Maps.newConcurrentMap(); private static final ConcurrentMap<Class<? extends AutoScalingMetadata>,TagSupport> supportByClass = Maps.newConcurrentMap(); private static final LoadingCache<Class,Class> metadataClassMap = CacheBuilder.newBuilder().build( new CacheLoader<Class,Class>() { @Override public Class load( final Class instanceClass ) { final List<Class<?>> interfaces = Lists.newArrayList(); for ( final Class clazz : Classes.interfaceAncestors().apply( instanceClass ) ) { interfaces.add( clazz ); } Collections.reverse( interfaces ); return Iterables.find( interfaces, Predicates.and( Predicates.not( Predicates.<Class<?>>equalTo( AutoScalingMetadata.class ) ), Classes.subclassOf( AutoScalingMetadata.class ) ) ); } }); private final Class<? extends AbstractOwnedPersistent> resourceClass; private final Class<? extends AutoScalingMetadata> cloudMetadataClass; private final String resourceType; private final String resourceClassIdField; private final String tagClassResourceField; protected <T extends AbstractOwnedPersistent & AutoScalingMetadata> TagSupport( @Nonnull final Class<T> resourceClass, @Nonnull final String resourceType, @Nonnull final String resourceClassIdField, @Nonnull final String tagClassResourceField ) { this.resourceClass = resourceClass; this.cloudMetadataClass = subclassFor( resourceClass ); this.resourceType = resourceType; this.resourceClassIdField = resourceClassIdField; this.tagClassResourceField = tagClassResourceField; } public abstract Tag createOrUpdate( AutoScalingMetadata metadata, OwnerFullName ownerFullName, String key, String value, Boolean propagateAtLaunch ); public abstract Tag example( @Nonnull AutoScalingMetadata metadata, @Nonnull OwnerFullName ownerFullName, @Nullable String key, @Nullable String value ); public abstract Tag example( @Nonnull OwnerFullName ownerFullName ); protected <T extends Tag> T example( @Nonnull T tag, @Nonnull OwnerFullName ownerFullName ) { tag.setOwner( ownerFullName ); return tag; } public abstract AutoScalingMetadata lookup( OwnerFullName owner, String identifier ) throws TransactionException; /** * Get the tags for the given resources, grouped by ID and ordered for display. * * @param owner The account for the tags * @param identifiers The resource identifiers for the tags * @param tagPredicate Predicate for filtering tags * @return The tag map with an entry for each requested resource */ public Map<String,List<Tag>> getResourceTagMap( final OwnerFullName owner, final Iterable<String> identifiers, final Predicate<? super Tag> tagPredicate) { final Map<String,List<Tag>> tagMap = Maps.newHashMap(); for ( final String id : identifiers ) { tagMap.put( id, Lists.<Tag>newArrayList() ); } if ( !tagMap.isEmpty() ) { final Tag example = example( owner ); final DetachedCriteria detachedCriteria = DetachedCriteria.forClass( resourceClass ) .add( Restrictions.in( resourceClassIdField, Lists.newArrayList( identifiers ) ) ) .setProjection( Projections.id() ); final Criterion idRestriction = Property.forName( tagClassResourceField ).in( detachedCriteria ); try { final List<Tag> tags = Tags.list( example, tagPredicate, idRestriction, Collections.<String, String>emptyMap() ); for ( final Tag tag : tags ) { tagMap.get( tag.getResourceId() ).add( tag ); } } catch ( AutoScalingMetadataNotFoundException e ) { log.error( e, e ); } Ordering<Tag> order = Ordering.natural().onResultOf( Tags.key() ); for ( final String id : identifiers ) { Collections.sort( tagMap.get( id ), order ); } } return tagMap; } /** * Get the tags for the given resources, grouped by ID and ordered for display. * * @param owner The account for the tags * @param identifier The resource identifier for the tags * @param tagPredicate Predicate for filtering tags * @return The tags for the resource */ public List<Tag> getResourceTags( final OwnerFullName owner, final String identifier, final Predicate<? super Tag> tagPredicate ) { return getResourceTagMap( owner, Collections.singleton( identifier ), tagPredicate ).get( identifier ); } @Nonnull public Class<? extends AutoScalingMetadata> getCloudMetadataClass() { return cloudMetadataClass; } @Nonnull public String getResourceType() { return resourceType; } public static TagSupport forResourceClass( @Nonnull final Class<? extends AutoScalingMetadata> metadataClass ) { return supportByClass.get( subclassFor( metadataClass ) ); } public static TagSupport fromResource( @Nonnull final AutoScalingMetadata metadata ) { return supportByClass.get( subclassFor( metadata.getClass() ) ); } public static TagSupport fromResourceType( @Nonnull final String resourceType ) { return supportByResourceType.get( resourceType ); } static void registerTagSupport( @Nonnull final TagSupport tagSupport ) { supportByClass.put( tagSupport.getCloudMetadataClass(), tagSupport ); supportByResourceType.put( tagSupport.getResourceType(), tagSupport ); } @SuppressWarnings( "unchecked" ) private static Class<? extends AutoScalingMetadata> subclassFor( Class<? extends AutoScalingMetadata> metadataInstance ) { return metadataClassMap.getUnchecked( metadataInstance ); } Class<? extends AbstractOwnedPersistent> getResourceClass() { return resourceClass; } String getResourceClassIdField() { return resourceClassIdField; } String getTagClassResourceField() { return tagClassResourceField; } }