/************************************************************************* * Copyright 2009-2014 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.entities; import java.util.Date; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import javax.annotation.Nullable; import org.hibernate.criterion.Criterion; import com.eucalyptus.auth.principal.AccountFullName; import com.eucalyptus.util.Callback; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.LogUtil; import com.eucalyptus.auth.principal.OwnerFullName; import com.eucalyptus.auth.type.RestrictedType; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; /** * Support for persistence DAOs. * * @param <RT> The RestrictedType for the AbstractOwnedPersistent * @param <AP> The AbstractOwnedPersistent type * @param <PE> The base persistence exception type. */ public abstract class AbstractPersistentSupport<RT extends RestrictedType, AP extends AbstractPersistent & RestrictedType, PE extends Exception> { protected final String typeDescription; protected AbstractPersistentSupport( final String typeDescription ) { this.typeDescription = typeDescription; } public <T> T lookupByName( @Nullable final OwnerFullName ownerFullName, final String name, final Function<? super AP,T> transform ) throws PE { return lookupByName( ownerFullName, name, Predicates.alwaysTrue(), transform ); } public <T> T lookupByName( @Nullable final OwnerFullName ownerFullName, final String name, final Predicate<? super AP> filter, final Function<? super AP,T> transform ) throws PE { return lookupByExample( exampleWithName( ownerFullName, name ), ownerFullName, name, filter, transform ); } public <T> T lookupByExample( final AP example, @Nullable final OwnerFullName ownerFullName, final String key, final Predicate<? super AP> filter, final Function<? super AP,T> transform ) throws PE { try { return Transactions.one( example, filter, transform ); } catch ( NoSuchElementException e ) { throw notFoundException( qualifyOwner("Unable to find "+typeDescription+" '"+key+"'", ownerFullName), e ); } catch ( Exception e ) { throw metadataException( qualifyOwner("Error finding "+typeDescription+" '"+key+"'", ownerFullName), e ); } } public List<AP> list( final OwnerFullName ownerFullName ) throws PE { try { return Transactions.findAll( exampleWithOwner( ownerFullName ) ); } catch ( Exception e ) { throw metadataException( qualifyOwner( "Failed to find "+typeDescription+"s", ownerFullName ), e ); } } public <T> List<T> list( final OwnerFullName ownerFullName, final Predicate<? super AP> filter, final Function<? super AP,T> transform ) throws PE { try { return Transactions.filteredTransform( exampleWithOwner( ownerFullName ), filter, transform ); } catch ( Exception e ) { throw metadataException( qualifyOwner( "Failed to find "+typeDescription+"s", ownerFullName ), e ); } } public <T> List<T> list( final OwnerFullName ownerFullName, final Criterion criterion, final Map<String,String> aliases, final Predicate<? super AP> filter, final Function<? super AP,T> transform ) throws PE { try { return Transactions.filteredTransform( exampleWithOwner( ownerFullName ), criterion, aliases, filter, transform ); } catch ( Exception e ) { throw metadataException( qualifyOwner( "Failed to find "+typeDescription+"s", ownerFullName ), e ); } } public <T> List<T> listByExample( final AP example, final Predicate<? super AP> filter, final Function<? super AP,T> transform ) throws PE { try { return Transactions.filteredTransform( example, filter, transform ); } catch ( Exception e ) { throw metadataException( "Failed to find "+typeDescription+"s by example: " + LogUtil.dumpObject( example ), e ); } } public <T> List<T> listByExample( final AP example, final Predicate<? super AP> filter, final Criterion criterion, final Map<String,String> aliases, final Function<? super AP,T> transform ) throws PE { try { return Transactions.filteredTransform( example, criterion, aliases, filter, transform ); } catch ( Exception e ) { throw metadataException( "Failed to find "+typeDescription+"s by example: " + LogUtil.dumpObject(example), e ); } } public long countByExample( final AP example ) throws PE { try { return Entities.count( example ); } catch ( Exception e ) { throw metadataException( "Failed to count "+typeDescription+"s by example: " + LogUtil.dumpObject( example ), e ); } } public long countByExample( final AP example, final Criterion criterion, final Map<String,String> aliases ) throws PE { try { return Entities.count( example, criterion, aliases ); } catch ( Exception e ) { throw metadataException( "Failed to count "+typeDescription+"s by example: " + LogUtil.dumpObject(example), e ); } } public AP updateByExample( final AP example, final OwnerFullName ownerFullName, final String key, final Callback<AP> updateCallback ) throws PE { try { return Transactions.one( example, updateCallback ); } catch ( NoSuchElementException e ) { throw notFoundException( qualifyOwner( "Unable to find "+typeDescription+" '"+key+"'", ownerFullName ), e ); } catch ( Exception e ) { throw metadataException( qualifyOwner( "Error updating "+typeDescription+" '"+key+"'", ownerFullName ), e ); } } public <T> T updateByExample( final AP example, final OwnerFullName ownerFullName, final String key, final Function<? super AP,T> updateTransform ) throws PE { try { return Transactions.one( example, updateTransform ); } catch ( NoSuchElementException e ) { throw notFoundException( qualifyOwner( "Unable to find "+typeDescription+" '"+key+"'", ownerFullName ), e ); } catch ( Exception e ) { throw metadataException( qualifyOwner( "Error updating "+typeDescription+" '"+key+"'", ownerFullName ), e ); } } public <T> T updateByExample( final AP example, final Criterion criterion, final Map<String,String> aliases, final OwnerFullName ownerFullName, final String key, final Function<? super AP,T> updateTransform ) throws PE { try { return Transactions.one( example, criterion, aliases, Predicates.alwaysTrue( ), updateTransform ); } catch ( NoSuchElementException e ) { throw notFoundException( qualifyOwner( "Unable to find "+typeDescription+" '"+key+"'", ownerFullName ), e ); } catch ( Exception e ) { throw metadataException( qualifyOwner( "Error updating "+typeDescription+" '"+key+"'", ownerFullName ), e ); } } public AP save( final AP natGateway ) throws PE { try { return Transactions.saveDirect( natGateway ); } catch ( Exception e ) { throw metadataException( "Error creating "+typeDescription+" '"+ natGateway.getDisplayName()+"'", e ); } } public boolean delete( final RT metadata ) throws PE { return delete( metadata, Predicates.<AP>alwaysTrue( ) ); } public boolean delete( final RT metadata, final Predicate<? super AP> precondition ) throws PE { try { final String accountNumber = metadata instanceof RestrictedType.AccountRestrictedType ? ( (RestrictedType.AccountRestrictedType) metadata ).getOwnerAccountNumber( ) : metadata.getOwner( ).getAccountNumber( ); return Transactions.delete( exampleWithName( AccountFullName.getInstance( accountNumber ), metadata.getDisplayName( ) ), precondition ); } catch ( NoSuchElementException e ) { return false; } catch ( Exception e ) { throw metadataException( "Error deleting "+typeDescription+" '"+describe( metadata )+"'", e ); } } public List<AP> deleteByExample( final AP example ) throws PE { try { return Transactions.each( example, new Callback<AP>(){ @Override public void fire( final AP input ) { Entities.delete( input ); } } ); } catch ( Exception e ) { throw metadataException( "Error deleting "+typeDescription+"s by example: "+LogUtil.dumpObject( example ), e ); } } public List<AP> deleteByExample( final AP example, final Criterion criterion, final Map<String,String> aliases ) throws PE { try { return Transactions.each( example, criterion, aliases, new Callback<AP>(){ @Override public void fire( final AP input ) { Entities.delete( input ); } } ); } catch ( Exception e ) { throw metadataException( "Error deleting "+typeDescription+"s by example: "+LogUtil.dumpObject( example ), e ); } } protected String describe( final RestrictedType metadata ) { return metadata.getDisplayName(); } protected abstract PE notFoundException( String message, Throwable cause ); protected abstract PE metadataException( String message, Throwable cause ); protected abstract AP exampleWithOwner( OwnerFullName ownerFullName ); protected abstract AP exampleWithName( OwnerFullName ownerFullName, String name ); protected String qualifyOwner( final String text, final OwnerFullName ownerFullName ) { return ownerFullName == null ? text : text + " for " + ownerFullName; } public static Function<AbstractPersistent,Date> creation( ) { return AbstractPersistentDateFunctions.CREATION; } public static Function<AbstractPersistent,Date> lastUpdate( ) { return AbstractPersistentDateFunctions.LAST_UPDATE; } public AbstractPersistentSupport<RT, AP, PE> withRetries( ) { return withRetries( Entities.CONCURRENT_UPDATE_RETRIES ); } public AbstractPersistentSupport<RT, AP, PE> withRetries( final int retries ) { return new RetryingAbstractPersistentSupport<>( this, retries ); } private static class RetryingAbstractPersistentSupport<RT extends RestrictedType, AP extends AbstractPersistent & RestrictedType, PE extends Exception> extends DelegatingAbstractPersistentSupport<RT, AP, PE> { private final int retries; private RetryingAbstractPersistentSupport( final AbstractPersistentSupport<RT, AP, PE> delegate, final int retries ) { super( delegate ); this.retries = retries; } @Override public <T> T updateByExample( final AP example, final OwnerFullName ownerFullName, final String key, final Function<? super AP, T> updateTransform ) throws PE { return updateWithRetries( example.getClass(), new Function<Void, T>() { @Override public T apply( @Nullable final Void nothing ) { try { return RetryingAbstractPersistentSupport.super.updateByExample( example, ownerFullName, key, updateTransform ); } catch ( final Exception e ) { throw Exceptions.toUndeclared( e ); } } }, ownerFullName, key ); } @Override public AP updateByExample( final AP example, final OwnerFullName ownerFullName, final String key, final Callback<AP> updateCallback ) throws PE { return updateWithRetries( example.getClass( ), new Function<Void, AP>() { @Override public AP apply( @Nullable final Void nothing ) { try { return RetryingAbstractPersistentSupport.super.updateByExample( example, ownerFullName, key, updateCallback ); } catch ( final Exception e ) { throw Exceptions.toUndeclared( e ); } } }, ownerFullName, key ); } @Override public <T> T updateByExample( final AP example, final Criterion criterion, final Map<String, String> aliases, final OwnerFullName ownerFullName, final String key, final Function<? super AP, T> updateTransform ) throws PE { return updateWithRetries( example.getClass( ), new Function<Void, T>() { @Override public T apply( @Nullable final Void nothing ) { try { return RetryingAbstractPersistentSupport.super.updateByExample( example, criterion, aliases, ownerFullName, key, updateTransform ); } catch ( final Exception e ) { throw Exceptions.toUndeclared( e ); } } }, ownerFullName, key ); } @Override public List<AP> deleteByExample( final AP example ) throws PE { return deleteWithRetries( example.getClass( ), new Function<Void, List<AP>>() { @Override public List<AP> apply( @Nullable final Void nothing ) { try { return RetryingAbstractPersistentSupport.super.deleteByExample( example ); } catch ( final Exception e ) { throw Exceptions.toUndeclared( e ); } } }, example ); } @Override public List<AP> deleteByExample( final AP example, final Criterion criterion, final Map<String, String> aliases ) throws PE { return deleteWithRetries( example.getClass( ), new Function<Void, List<AP>>() { @Override public List<AP> apply( @Nullable final Void nothing ) { try { return RetryingAbstractPersistentSupport.super.deleteByExample( example, criterion, aliases ); } catch ( final Exception e ) { throw Exceptions.toUndeclared( e ); } } }, example ); } @Override public AbstractPersistentSupport<RT, AP, PE> withRetries( ) { return this; } @Override public AbstractPersistentSupport<RT, AP, PE> withRetries( final int retries ) { return this; } private <T> T updateWithRetries( final Class<?> type, final Function<Void, T> updateFunction, final OwnerFullName ownerFullName, final String key ) throws PE { try { return Entities.asTransaction( type, updateFunction, retries ).apply( null ); } catch ( final Exception e ) { throw metadataException( qualifyOwner( "Error updating "+typeDescription+" '"+key+"'", ownerFullName ), e ); } } private <T> T deleteWithRetries( final Class<?> type, final Function<Void, T> deleteFunction, final AP example ) throws PE { try { return Entities.asTransaction( type, deleteFunction, retries ).apply( null ); } catch ( final Exception e ) { throw metadataException( "Error deleting "+typeDescription+"s by example: "+LogUtil.dumpObject( example ), e ); } } } private static class DelegatingAbstractPersistentSupport<RT extends RestrictedType, AP extends AbstractPersistent & RestrictedType, PE extends Exception> extends AbstractPersistentSupport<RT, AP, PE> { private final AbstractPersistentSupport<RT, AP, PE> delegate; private DelegatingAbstractPersistentSupport( final AbstractPersistentSupport<RT, AP, PE> delegate ) { super( delegate.typeDescription ); this.delegate = delegate; } public <T> T lookupByName( @Nullable final OwnerFullName ownerFullName, final String name, final Function<? super AP, T> transform ) throws PE { return delegate.lookupByName( ownerFullName, name, transform ); } @Override public <T> T lookupByName( @Nullable final OwnerFullName ownerFullName, final String name, final Predicate<? super AP> filter, final Function<? super AP, T> transform ) throws PE { return delegate.lookupByName( ownerFullName, name, filter, transform ); } public <T> T lookupByExample( final AP example, @Nullable final OwnerFullName ownerFullName, final String key, final Predicate<? super AP> filter, final Function<? super AP, T> transform ) throws PE { return delegate.lookupByExample( example, ownerFullName, key, filter, transform ); } @Override public List<AP> list( final OwnerFullName ownerFullName ) throws PE { return delegate.list( ownerFullName ); } public <T> List<T> list( final OwnerFullName ownerFullName, final Predicate<? super AP> filter, final Function<? super AP, T> transform ) throws PE { return delegate.list( ownerFullName, filter, transform ); } public <T> List<T> list( final OwnerFullName ownerFullName, final Criterion criterion, final Map<String, String> aliases, final Predicate<? super AP> filter, final Function<? super AP, T> transform ) throws PE { return delegate.list( ownerFullName, criterion, aliases, filter, transform ); } public <T> List<T> listByExample( final AP example, final Predicate<? super AP> filter, final Function<? super AP, T> transform ) throws PE { return delegate.listByExample( example, filter, transform ); } public <T> List<T> listByExample( final AP example, final Predicate<? super AP> filter, final Criterion criterion, final Map<String, String> aliases, final Function<? super AP, T> transform ) throws PE { return delegate.listByExample( example, filter, criterion, aliases, transform ); } public long countByExample( final AP example ) throws PE { return delegate.countByExample( example ); } public long countByExample( final AP example, final Criterion criterion, final Map<String, String> aliases ) throws PE { return delegate.countByExample( example, criterion, aliases ); } public AP updateByExample( final AP example, final OwnerFullName ownerFullName, final String key, final Callback<AP> updateCallback ) throws PE { return delegate.updateByExample( example, ownerFullName, key, updateCallback ); } public <T> T updateByExample( final AP example, final OwnerFullName ownerFullName, final String key, final Function<? super AP, T> updateTransform ) throws PE { return delegate.updateByExample( example, ownerFullName, key, updateTransform ); } @Override public <T> T updateByExample( final AP example, final Criterion criterion, final Map<String, String> aliases, final OwnerFullName ownerFullName, final String key, final Function<? super AP, T> updateTransform ) throws PE { return delegate.updateByExample( example, criterion, aliases, ownerFullName, key, updateTransform ); } public AP save( final AP metadata ) throws PE { return delegate.save( metadata ); } public boolean delete( final RT metadata ) throws PE { return delegate.delete( metadata ); } @Override public boolean delete( final RT metadata, final Predicate<? super AP> precondition ) throws PE { return delegate.delete( metadata, precondition ); } public List<AP> deleteByExample( final AP example ) throws PE { return delegate.deleteByExample( example ); } public List<AP> deleteByExample( final AP example, final Criterion criterion, final Map<String, String> aliases ) throws PE { return delegate.deleteByExample( example, criterion, aliases ); } @Override public String describe( final RestrictedType metadata ) { return delegate.describe( metadata ); } @Override public PE notFoundException( final String message, final Throwable cause ) { return delegate.notFoundException( message, cause ); } @Override public PE metadataException( final String message, final Throwable cause ) { return delegate.metadataException( message, cause ); } @Override public AP exampleWithOwner( final OwnerFullName ownerFullName ) { return delegate.exampleWithOwner( ownerFullName ); } @Override public AP exampleWithName( final OwnerFullName ownerFullName, final String name ) { return delegate.exampleWithName( ownerFullName, name ); } @Override public String qualifyOwner( final String text, final OwnerFullName ownerFullName ) { return delegate.qualifyOwner( text, ownerFullName ); } public static Function<AbstractPersistent, Date> creation() { return AbstractPersistentSupport.creation(); } public static Function<AbstractPersistent, Date> lastUpdate() { return AbstractPersistentSupport.lastUpdate(); } @Override public AbstractPersistentSupport<RT, AP, PE> withRetries() { return delegate.withRetries(); } @Override public AbstractPersistentSupport<RT, AP, PE> withRetries( final int retries ) { return delegate.withRetries( retries ); } } private enum AbstractPersistentDateFunctions implements Function<AbstractPersistent,Date> { CREATION { @Nullable @Override public Date apply( @Nullable final AbstractPersistent abstractPersistent ) { return abstractPersistent == null ? null : abstractPersistent.getCreationTimestamp( ); } }, LAST_UPDATE { @Nullable @Override public Date apply( @Nullable final AbstractPersistent abstractPersistent ) { return abstractPersistent == null ? null : abstractPersistent.getLastUpdateTimestamp( ); } }, } }