/**
* Copyright 2014 Comcast Cable Communications Management, LLC
*
* This file is part of CATS.
*
* CATS 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, either version 3 of the License, or
* (at your option) any later version.
*
* CATS 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 CATS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.comcast.cats.provider;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.comcast.cats.Settop;
import com.comcast.cats.domain.Allocation;
import com.comcast.cats.domain.exception.AllocationException;
import com.comcast.cats.domain.service.AllocationService;
import com.comcast.cats.provider.impl.SettopExclusiveAccessToken;
/**
* Minimum implementation for interacting with Allocation service given a known
* authToken is available.
*
* @author cfrede001
*
*/
class SettopLockHandler implements SettopExclusiveAccessEnforcer
{
private static Logger logger = LoggerFactory
.getLogger( SettopExclusiveAccessEnforcer.class );
protected final AllocationService allocationService;
protected String authToken;
private StringBuilder lockErrorMessage = new StringBuilder( "" );
private StringBuilder releaseErrorMessage = new StringBuilder( "" );
/**
* Need to ensure that allocations are referenced through thread safe Map.
*/
protected final Map< String, SettopExclusiveAccessToken > allocations = new ConcurrentHashMap< String, SettopExclusiveAccessToken >();
public SettopLockHandler( AllocationService allocationService, String authToken )
{
this.allocationService = allocationService;
this.authToken = authToken;
}
/**
* {@inheritDoc}
*/
public void lock( Settop settop ) throws AllocationException
{
logger.trace( "executing settop lock" );
if ( allocationService == null )
{
logger.error( "AllocationService not configured" );
throw new AllocationException( "AllocationService not configured" );
}
Allocation allocation = allocationService.createByComponentId( settop.getId());
//Generate and add the AllocationToken.
createAllocationToken(settop, allocation, false);
}
/**
* {@inheritDoc}
*/
public void reacquire( Settop settop ) throws AllocationException
{
logger.trace( "executing settop reacquire" );
if ( allocationService == null )
{
logger.error( "AllocationService not configured" );
throw new AllocationException( "AllocationService not configured" );
}
Allocation allocation = allocationService.reacquireByComponentId( settop.getId());
//Generate and add the AllocationToken.
createAllocationToken(settop, allocation, true);
}
protected void createAllocationToken(Settop settop, Allocation allocation, boolean reaquire) throws AllocationException {
if ( null == allocation )
{
String message=reaquire?"Reacquire":"Acquire";
throw new AllocationException( message+" Failed for:"+settop.getHostMacAddress() );
}
String allocationId = allocation.getId();
logger.trace( "Allocation created successfully, using information to create token." );
SettopExclusiveAccessToken token = new SettopExclusiveAccessToken( authToken, settop, allocationId, reaquire);
allocations.put( settop.getId(), token );
settop.setLocked( true );
logger.trace( "ExclusiveAccessToken Created with Information: " + token.toString() );
}
/**
* Return the allocation settop token if it is present in either the
* reacquired list or in allocation list.
*
* @param settop
* - Settop attempting to be located.
* @return SettopExclusiveAccessToken for the settop or null if doesn't
* exist.
*/
private SettopExclusiveAccessToken getSettopExclusiveAccessToken( Settop settop )
{
SettopExclusiveAccessToken token=getSettopTokenFromAllocatonList(settop);
return token;
}
/**
* Return the allocation settop token from the allocations list
*
* @param settop
* - Settop attempting to be located.
* @return SettopExclusiveAccessToken for the settop or null if doesn't
* exist.
*/
private SettopExclusiveAccessToken getSettopTokenFromAllocatonList( Settop settop )
{
return allocations.get( settop.getId() );
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void release( Settop settop ) throws AllocationException
{
SettopExclusiveAccessToken token = getSettopTokenFromAllocatonList( settop );
if ( token != null )
{
if(!token.isReaquire()) {
allocationService.release( token.getAllocationId() );
}
logger.trace( "ExclusiveAccessToken Information:\n" + token.toString() );
logger.trace( "ExclusiveAccessToken is being destroyed" );
allocations.remove( settop.getId() );
settop.setLocked( false );
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isLocked( Settop settop )
{
if ( getSettopExclusiveAccessToken( settop ) != null )
{
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean verify( Settop settop ) throws AllocationException
{
SettopExclusiveAccessToken token = getSettopExclusiveAccessToken( settop );
if ( null == token )
{
String msg = "No settop token found for settop " + settop.toString();
logger.warn( msg );
throw new AllocationException( msg );
}
return allocationService.verify( token.getAllocationId() );
}
/**
* {@inheritDoc}
*/
@Override
public List< Settop > lock( List< Settop > settopList ) throws AllocationException
{
return lock( settopList, false );
}
/**
* {@inheritDoc}
*/
@Override
public List< Settop > lock( List< Settop > settopList, boolean failOnLockError ) throws AllocationException
{
List< Settop > allocatedSettops = new LinkedList< Settop >();
for ( Settop settop : settopList )
{
try
{
lock( settop );
logger.debug( "Locked -" + settop.getHostMacAddress() );
}
catch ( Exception exception )
{
logger.error( "Exception on locking settop " + settop.toString(), exception );
lockErrorMessage.append( "Error on locking settop " + settop.getHostMacAddress().toString() + " - "
+ exception + "\n" );
if ( failOnLockError )
{
throw new AllocationException( exception.getMessage() );
}
}
}
// If no settops could be locked throw an exception.
if ( allocatedSettops.size() <= 0 )
{
throw new AllocationException( "No Settops could be locked from list" );
}
return allocatedSettops;
}
/**
* {@inheritDoc}
*/
@Override
public void release( List< Settop > settopList ) throws AllocationException
{
if ( settopList.isEmpty() )
{
logger.warn( "No settops found to release" );
}
else
{
int successfulReleases = 0;
for ( Settop settop : settopList )
{
try
{
release( settop );
logger.debug( "Released -" + settop.getHostMacAddress() );
successfulReleases++;
}
catch ( Exception exception )
{
logger.error( "Error on releasing settop" + settop.toString(), exception );
releaseErrorMessage.append( "Error on releasing settop " + settop.getHostMacAddress().toString()
+ " - " + exception + "\n" );
}
}
if ( successfulReleases <= 0 )
{
throw new AllocationException( "No settops could be successfully released" );
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void release() throws AllocationException
{
release( getActiveSettops() );
}
/**
* to get lock error message
*
* @return String
*/
@Override
public String getLockErrorMessage()
{
return lockErrorMessage.toString();
}
/**
* to get release error message
*
* @return String
*/
@Override
public String getReleaseErrorMessage()
{
return releaseErrorMessage.toString();
}
/**
* Convenience method to get all the currently allocated settops.
* @return list of currently allocated {@linkplain Settop}.
*/
@Override
public List< Settop > getActiveSettops()
{
List< Settop > settops = new ArrayList< Settop >( allocations.size() );
for ( SettopExclusiveAccessToken settopToken : allocations.values() )
{
settops.add( settopToken.getSettop() );
}
return settops;
}
}