/*************************************************************************
* Copyright 2009-2015 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.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.entities;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.Callable;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import com.eucalyptus.records.Logs;
import com.eucalyptus.util.Callback;
import com.eucalyptus.util.EucalyptusCloudException;
import com.eucalyptus.util.Exceptions;
import com.eucalyptus.util.FUtils;
import com.eucalyptus.util.async.Callbacks;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import static com.eucalyptus.util.Parameters.checkParam;
import static org.hamcrest.Matchers.notNullValue;
import javax.annotation.Nonnull;
public class Transactions {
private static <T> TransactionResource get( T obj ) {
return Entities.transactionFor( obj );
}
private static TransactionException transformException( Throwable t ) {
Logs.exhaust( ).error( t, t );
PersistenceExceptions.throwFiltered( t );
if ( t instanceof InterruptedException ) {
Thread.currentThread( ).interrupt( );
return new TransactionExecutionException( t.getCause( ).getMessage( ), t.getCause( ) );
} else if ( t instanceof EucalyptusCloudException ) {
return new TransactionExecutionException( t.getMessage( ), t );
} else if ( t instanceof UndeclaredThrowableException ) {
return new TransactionCallbackException( t.getCause( ).getMessage( ), t.getCause( ) );
} else {
return new TransactionInternalException( t.getMessage( ), t );
}
}
/**
* List all entities matching the given restriction.
*
* @param restriction The entity restriction
* @param <T> The entity type
* @return The list of matching entities
* @throws TransactionException If an error occurs
*/
@Nonnull
public static <T> List<T> findAll( @Nonnull EntityRestriction<T> restriction ) throws TransactionException {
return each( restriction, Callbacks.<T>noop( ) );
}
/**
* List all entities matching the given restriction.
*
* <p>The given callback is invoked within the transaction for each item.</p>
*
* @param restriction The entity restriction
* @param callback The callback to use
* @param <T> The entity type
* @return The list of matching entities
* @throws TransactionException If an error occurs
*/
public static <T> List<T> each(
@Nonnull EntityRestriction<T> restriction,
@Nonnull Callback<T> callback
) throws TransactionException {
checkParam( restriction, notNullValue() );
checkParam( callback, notNullValue() );
try ( final TransactionResource db = Transactions.get( restriction.getEntityClass( ) ) ) {
final List<T> res = Entities.criteriaQuery( restriction ).list( );
for ( T t : res ) {
try {
callback.fire( t );
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
}
db.commit( );
return res;
} catch ( TransactionCallbackException | UndeclaredThrowableException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( Exception e ) {
Logs.extreme( ).error( e, e );
throw new TransactionInternalException( e );
}
}
/**
* Get the unique entity matching the given restriction.
*
* @param restriction The entity restriction
* @param <T> The entity type
* @return The entity
* @throws TransactionException If an error occurs or a unique matching entity is not found
*/
public static <T> T one( @Nonnull EntityRestriction<T> restriction ) throws TransactionException {
return one( restriction, Functions.<T>identity( ) );
}
/**
* Get the unique entity matching the given restriction.
*
* <p>The given callback is invoked with the transaction for the entity</p>
*
* @param restriction The entity restriction
* @param callback The callback to use
* @param <T> The entity type
* @return The entity
* @throws TransactionException If an error occurs or a unique matching entity is not found
*/
public static <T> T one(
@Nonnull EntityRestriction<T> restriction,
@Nonnull final Callback<T> callback
) throws TransactionException {
return one( restriction, FUtils.function( callback ) );
}
/**
* Get the transformed unique entity matching the given restriction.
*
* <p>The given transform is invoked within the transaction for the entity</p>
*
* @param restriction The entity restriction
* @param function The entity transformation function
* @param <S> The result type
* @param <T> The entity type
* @return The transformed entity or null if the function returns null
* @throws TransactionException If an error occurs
*/
public static <S, T> S one(
@Nonnull EntityRestriction<T> restriction,
@Nonnull Function<T, S> function
) throws TransactionException {
return one( restriction, Predicates.alwaysTrue( ), function );
}
/**
* Get the transformed unique entity matching the given restriction and predicate.
*
* <p>The given transform is invoked within the transaction for the entity</p>
*
* @param restriction The entity restriction
* @param predicate The predicate for entity matching
* @param function The entity transformation function
* @param <S> The result type
* @param <T> The entity type
* @return The transformed entity or null if the function returns null
* @throws TransactionException If an error occurs
*/
public static <S, T> S one(
@Nonnull final EntityRestriction<T> restriction,
@Nonnull final Predicate<? super T> predicate,
@Nonnull final Function<? super T, S> function
) throws TransactionException {
return one( restriction.getEntityClass( ), new Callable<T>( ) {
@Override
public T call( ) throws TransactionException {
return Entities.criteriaQuery( restriction ).uniqueResult( );
}
}, predicate, function );
}
/**
* Filtered listing of entities for the given restriction and condition.
*
* @param restriction The entity restriction
* @param predicate The predicate for entity matching
* @param <T> The entity type
* @return The list of matching entities
* @throws TransactionException If an error occurs
*/
public static <T> List<T> filter(
@Nonnull final EntityRestriction<T> restriction,
@Nonnull final Predicate<? super T> predicate
) throws TransactionException {
final Function<T, T> function = Functions.identity( );
return filteredTransform( restriction, predicate, function );
}
/**
* Transformed listing of entities for the given restriction.
* <p>The given transform is invoked within the transaction for the entity</p>
*
* @param restriction The entity restriction
* @param function The entity transformation function
* @param <T> The entity type
* @return The list of matching entities
* @throws TransactionException If an error occurs
*/
public static <S, T> List<S> transform(
@Nonnull final EntityRestriction<T> restriction,
@Nonnull final Function<T, S> function
) throws TransactionException {
Predicate<T> p = Predicates.alwaysTrue( );
return filteredTransform( restriction, p, function );
}
/**
* Transformed filtered listing of entities for the given restriction.
* <p>The given transform is invoked within the transaction for the entity</p>
*
* @param restriction The entity restriction
* @param predicate The predicate for entity matching
* @param function The entity transformation function
* @param <T> The entity type
* @param <O> The result type
* @return The list of matching transformed entities
* @throws TransactionException If an error occurs
*/
public static <T, O> List<O> filteredTransform(
@Nonnull final EntityRestriction<T> restriction,
@Nonnull final Predicate<? super T> predicate,
@Nonnull final Function<? super T, O> function
) throws TransactionException {
checkParam( restriction, notNullValue() );
final Supplier<List<T>> resultsSupplier = new Supplier<List<T>>() {
@Override
public List<T> get( ) {
return Entities.criteriaQuery( restriction ).list( );
}
};
return filteredTransform( restriction.getEntityClass( ), resultsSupplier, predicate, function );
}
/**
* Delete the unique entity matching the given restriction.
*
* @param restriction The entity restriction
* @param <T> The entity type
* @return true if an entity was deleted
* @throws TransactionException If an error occurs
*/
public static <T> boolean delete( @Nonnull final EntityRestriction<T> restriction ) throws TransactionException {
return delete( restriction, Predicates.<T>alwaysTrue() );
}
/**
* Delete the unique entity matching the given restriction and precondition.
*
* @param restriction The entity restriction
* @param precondition The predicate to match
* @param <T> The entity type
* @return True if an entity was deleted
* @throws TransactionException If an error occurs
*/
public static <T> boolean delete(
@Nonnull final EntityRestriction<T> restriction,
@Nonnull Predicate<? super T> precondition
) throws TransactionException {
checkParam( restriction, notNullValue() );
checkParam( precondition, notNullValue() );
try ( final TransactionResource db = Transactions.get( restriction.getEntityClass( ) ) ) {
T entity = Entities.criteriaQuery( restriction ).uniqueResult( );
try {
if ( precondition.apply( entity ) ) {
Entities.delete( entity );
db.commit( );
return true;
} else {
return false;
}
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
} catch ( TransactionCallbackException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( Exception t ) {
throw Transactions.transformException( t );
}
}
/**
* Deletes all queried entities, based on the search entity, that match the precondition.
* Returns true if all succeeded, false if otherwise. Does not stop on first failure. Attempts
* all regardless of failure
*/
public static <T> boolean deleteAll(
@Nonnull final EntityRestriction<T> restriction,
@Nonnull Predicate<? super T> precondition
) throws TransactionException {
checkParam( restriction, notNullValue() );
checkParam( precondition, notNullValue() );
try ( final TransactionResource db = Transactions.get( restriction.getEntityClass( ) ) ) {
List<T> entities = Entities.criteriaQuery( restriction ).list( );
boolean failed = false;
for(T entity : entities) {
try {
if ( precondition.apply( entity ) ) {
Entities.delete( entity );
} else {
failed = true;
}
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
}
db.commit( );
return !failed;
} catch ( TransactionCallbackException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( Exception t ) {
throw Transactions.transformException( t );
}
}
/**
* Bulk delete all matching entities, based on the restriction..
*
* @param restriction The entity restriction
* @return The count of deleted entities.
*/
public static <T> int deleteAll( @Nonnull final EntityRestriction<T> restriction ) throws TransactionException {
checkParam( restriction, notNullValue() );
try ( final TransactionResource db = Transactions.get( restriction.getEntityClass( ) ) ) {
final int count = Entities.delete( restriction ).delete( );
db.commit( );
return count;
} catch ( Exception t ) {
throw Transactions.transformException( t );
}
}
@Deprecated
public static <T> List<T> findAll( T search ) throws TransactionException {
return each( search, Callbacks.<T>noop( ) );
}
@Deprecated
public static <T> List<T> each( T search, Callback<T> c ) throws TransactionException {
return each( search, Restrictions.conjunction(), Collections.<String,String>emptyMap(), c );
}
@Deprecated
public static <T> List<T> each( final T search,
final Criterion criterion,
final Map<String,String> aliases,
final Callback<T> c ) throws TransactionException {
checkParam( search, notNullValue() );
checkParam( c, notNullValue() );
try ( final TransactionResource db = Transactions.get( search ) ) {
List<T> res = Entities.query( search, false, criterion, aliases );
for ( T t : res ) {
try {
c.fire( t );
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
}
db.commit( );
return res;
} catch ( TransactionCallbackException | UndeclaredThrowableException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( Exception e ) {
Logs.extreme( ).error( e, e );
throw new TransactionInternalException( e );
}
}
@Deprecated
public static <T> T find( T search ) throws TransactionException {
return one( search, Functions.<T>identity() );
}
@Deprecated
public static <T> T one( T search, final Callback<T> c ) throws TransactionException {
return one( search, new Function<T, T>( ) {
@Override
public T apply( T input ) {
try {
c.fire( input );
} catch ( Exception ex ) {
throw Exceptions.toUndeclared( new TransactionCallbackException( ex ) );
}
return input;
}
} );
}
@Deprecated
public static <S, T> S one( T search, Function<T, S> f ) throws TransactionException {
return one( search, Predicates.alwaysTrue(), f );
}
@Deprecated
public static <S, T> S one( final T search,
final Predicate<? super T> predicate,
final Function<? super T, S> f ) throws TransactionException {
return one( search, new Callable<T>( ){
@Override
public T call( ) throws TransactionException {
return Entities.uniqueResult( search );
}
}, predicate, f );
}
@Deprecated
public static <S, T> S one( final T search,
final Criterion criterion,
final Map<String,String> aliases,
final Predicate<? super T> predicate,
final Function<? super T, S> f ) throws TransactionException {
return one( search, new Callable<T>( ){
@Override
public T call( ) throws TransactionException {
final List<T> ts = Entities.query( search, false, criterion, aliases );
if ( ts.size( ) != 1 ) throw new NoSuchElementException( );
return ts.get( 0 );
}
}, predicate, f );
}
private static <S, T> S one( final EntityRestriction<T> type,
final Callable<T> lookup,
final Predicate<? super T> predicate,
final Function<? super T, S> f ) throws TransactionException {
checkParam( type, notNullValue() );
return one( type.getEntityClass( ), lookup, predicate, f );
}
private static <S, T> S one( final T type,
final Callable<T> lookup,
final Predicate<? super T> predicate,
final Function<? super T, S> f ) throws TransactionException {
checkParam( type, notNullValue() );
return one( type.getClass( ), lookup, predicate, f );
}
private static <S, T> S one( final Class<?> type,
final Callable<T> lookup,
final Predicate<? super T> predicate,
final Function<? super T, S> f ) throws TransactionException {
checkParam( f, notNullValue() );
try ( final TransactionResource db = Transactions.get( type ) ) {
T entity = lookup.call();
if ( !predicate.apply( entity ) ) {
throw new NoSuchElementException();
}
try {
S res = f.apply( entity );
db.commit( );
return res;
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
} catch ( TransactionCallbackException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( UndeclaredThrowableException e ) {
Logs.extreme( ).error( e, e );
if ( e.getCause( ) instanceof TransactionException ) {
throw e;
} else {
throw new TransactionCallbackException( e.getCause( ) );
}
} catch ( Exception t ) {
throw Transactions.transformException( t );
}
}
@Deprecated
public static <T> List<T> filter( T search, Predicate<? super T> condition ) throws TransactionException {
Function<T, T> f = Functions.identity( );
return filteredTransform( search, condition, f );
}
@Deprecated
public static <T> List<T> filter( final T search,
final Predicate<? super T> condition,
final Criterion criterion,
final Map<String,String> aliases ) throws TransactionException {
Function<T, T> f = Functions.identity( );
return filteredTransform( search, criterion, aliases, condition, f );
}
@Deprecated
public static <S, T> List<S> transform( T search, Function<T, S> f ) throws TransactionException {
Predicate<T> p = Predicates.alwaysTrue( );
return filteredTransform( search, p, f );
}
@Deprecated
public static <T, O> List<O> filteredTransform( final T search,
final Predicate<? super T> condition,
final Function<? super T, O> transform ) throws TransactionException {
checkParam( search, notNullValue() );
final Supplier<List<T>> resultsSupplier = new Supplier<List<T>>() {
@Override
public List<T> get() {
return Entities.query( search );
}
};
return filteredTransform( search.getClass(), resultsSupplier, condition, transform );
}
@Deprecated
public static <T, O> List<O> filteredTransform( final T search,
final Criterion criterion,
final Map<String,String> aliases,
final Predicate<? super T> condition,
final Function<? super T, O> transform ) throws TransactionException {
checkParam( search, notNullValue() );
final Supplier<List<T>> resultsSupplier = new Supplier<List<T>>() {
@Override
public List<T> get() {
return Entities.query( search, false, criterion, aliases );
}
};
return filteredTransform( search.getClass(), resultsSupplier, condition, transform );
}
private static <T, O> List<O> filteredTransform( Class<?> searchClass,
Supplier<List<T>> searchResultSupplier,
Predicate<? super T> condition,
Function<? super T, O> transform ) throws TransactionException {
checkParam( searchResultSupplier, notNullValue() );
checkParam( condition, notNullValue() );
checkParam( transform, notNullValue() );
List<O> res = Lists.newArrayList( );
try ( final TransactionResource db = Transactions.get( searchClass ) ) {
List<T> queryResults = searchResultSupplier.get();
for ( T t : queryResults ) {
if ( condition.apply( t ) ) {
try {
res.add( transform.apply( t ) );
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
}
}
db.commit( );
return res;
} catch ( TransactionCallbackException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( Exception e ) {
throw Transactions.transformException( e );
}
}
public static <T> T save( T saveMe ) throws TransactionException {
return save( saveMe, new Callback<T>( ) {
@Override
public void fire( T t ) {}
} );
}
public static <T> T save( T saveMe, Callback<T> c ) throws TransactionException {
checkParam( saveMe, notNullValue() );
checkParam( c, notNullValue() );
try ( final TransactionResource db = Transactions.get( saveMe ) ) {
T entity = Entities.merge( saveMe );
try {
c.fire( entity );
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
db.commit( );
return entity;
} catch ( TransactionCallbackException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( Exception t ) {
throw Transactions.transformException( t );
}
}
/**
* Save a new entity.
*
* <p>Unlike {@link #save} this method will never update an existing entity.</p>
*
* @param saveMe The new entity to save.
* @param <T> The entity type.
* @return The persistent entity
* @throws TransactionException If an error occurs
*/
public static <T> T saveDirect( T saveMe ) throws TransactionException {
checkParam( saveMe, notNullValue() );
try ( final TransactionResource db = Transactions.get( saveMe ) ) {
final T entity = Entities.persist( saveMe );
db.commit( );
return entity;
} catch ( Exception t ) {
throw Transactions.transformException( t );
}
}
@Deprecated
public static <T> boolean delete( T search ) throws TransactionException {
return delete( search, Predicates.alwaysTrue() );
}
@Deprecated
public static <T> boolean delete( T search, Predicate<? super T> precondition ) throws TransactionException {
checkParam( search, notNullValue() );
checkParam( precondition, notNullValue() );
try ( final TransactionResource db = Transactions.get( search ) ) {
T entity = Entities.uniqueResult( search );
try {
if ( precondition.apply( entity ) ) {
Entities.delete( entity );
db.commit( );
return true;
} else {
return false;
}
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
} catch ( TransactionCallbackException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( Exception t ) {
throw Transactions.transformException( t );
}
}
/**
* Deletes all queried entities, based on the search entity, that match the precondition.
* Returns true if all succeeded, false if otherwise. Does not stop on first failure. Attempts
* all regardless of failure
* @param search
* @param precondition
* @return
* @throws TransactionException
*/
@Deprecated
public static <T> boolean deleteAll( T search, Predicate<? super T> precondition ) throws TransactionException {
checkParam( search, notNullValue() );
checkParam( precondition, notNullValue() );
try ( final TransactionResource db = Transactions.get( search ) ) {
List<T> entities = Entities.query(search);
boolean failed = false;
for(T entity : entities) {
try {
if ( precondition.apply( entity ) ) {
Entities.delete( entity );
} else {
failed = true;
}
} catch ( Exception ex ) {
throw new TransactionCallbackException( ex );
}
}
db.commit( );
return !failed;
} catch ( TransactionCallbackException e ) {
Logs.extreme( ).error( e, e );
throw e;
} catch ( Exception t ) {
throw Transactions.transformException( t );
}
}
}