/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.core.portal.image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.concurrent.locks.Lock;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.google.common.base.Preconditions;
import com.enonic.cms.framework.blob.BlobKey;
import com.enonic.cms.framework.blob.BlobRecord;
import com.enonic.cms.framework.blob.BlobStore;
import com.enonic.cms.framework.util.GenericConcurrencyLock;
import com.enonic.cms.framework.util.ImageHelper;
import com.enonic.cms.core.content.access.ContentAccessResolver;
import com.enonic.cms.core.content.binary.AttachmentNotFoundException;
import com.enonic.cms.core.content.binary.BinaryDataEntity;
import com.enonic.cms.core.image.ImageRequest;
import com.enonic.cms.core.image.ImageResponse;
import com.enonic.cms.core.image.cache.ImageCache;
import com.enonic.cms.core.portal.livetrace.ImageRequestTrace;
import com.enonic.cms.core.portal.livetrace.ImageRequestTracer;
import com.enonic.cms.core.portal.livetrace.LivePortalTraceService;
import com.enonic.cms.core.preview.PreviewService;
import com.enonic.cms.core.security.user.UserEntity;
import com.enonic.cms.core.security.user.UserKey;
import com.enonic.cms.core.time.TimeService;
import com.enonic.cms.store.dao.ContentDao;
import com.enonic.cms.store.dao.GroupDao;
import com.enonic.cms.store.dao.UserDao;
@Service
public final class ImageServiceImpl
implements ImageService
{
private static final Logger LOG = LoggerFactory.getLogger( ImageServiceImpl.class );
private ImageCache imageCache;
private final ImageProcessor processor;
private ContentDao contentDao;
private GroupDao groupDao;
private UserDao userDao;
private BlobStore blobStore;
private TimeService timeService;
private PreviewService previewService;
@Autowired
private LivePortalTraceService livePortalTraceService;
private static GenericConcurrencyLock<String> concurrencyLock = GenericConcurrencyLock.create();
private File directory;
public ImageServiceImpl()
{
this.processor = new ImageProcessor();
}
public boolean accessibleInPortal( ImageRequest req )
{
ImageRequestAccessResolver imageRequestAccessResolver =
new ImageRequestAccessResolver( contentDao, new ContentAccessResolver( groupDao ) );
imageRequestAccessResolver.imageRequester( userDao.findByKey( req.getRequester().getKey() ) );
imageRequestAccessResolver.requireMainVersion();
imageRequestAccessResolver.requireOnlineNow( timeService.getNowAsDateTime(), previewService );
ImageRequestAccessResolver.Access access = imageRequestAccessResolver.isAccessible( req );
return access == ImageRequestAccessResolver.Access.OK;
}
public ImageResponse process( ImageRequest imageRequest )
{
Preconditions.checkNotNull( imageRequest, "imageRequest cannot be null" );
final ImageRequestTrace imageRequestTrace = livePortalTraceService.getCurrentTrace().getImageRequestTrace();
String blobKey = getBlobKey( imageRequest );
if ( blobKey == null )
{
return ImageResponse.notFound();
}
imageRequest.setBlobKey( blobKey );
final Lock locker = concurrencyLock.getLock( imageRequest.getCacheKey() );
try
{
ImageRequestTracer.startConcurrencyBlockTimer( imageRequestTrace );
locker.lock();
ImageRequestTracer.stopConcurrencyBlockTimer( imageRequestTrace );
ImageResponse res = imageCache.get( imageRequest );
if ( res != null )
{
ImageRequestTracer.traceImageResponse( imageRequestTrace, res );
ImageRequestTracer.traceUsedCachedResult( imageRequestTrace, true );
return res;
}
try
{
res = doProcess( imageRequest );
ImageRequestTracer.traceImageResponse( imageRequestTrace, res );
ImageRequestTracer.traceUsedCachedResult( imageRequestTrace, false );
return res;
}
catch ( AttachmentNotFoundException e )
{
LOG.error( "Cannot read image with key {} from configured BLOB directory ( {} ). Check your CMS configuration.", blobKey,
directory.getAbsolutePath() );
return ImageResponse.notFound();
}
catch ( Exception e )
{
throw new ImageProcessorException(
"Failed to process image [contentKey=" + imageRequest.getContentKey() + "] : " + e.getMessage(), e );
}
}
finally
{
locker.unlock();
}
}
public Long getImageTimestamp( final ImageRequest req )
{
final UserKey userKey = req.getUserKey();
if ( userKey != null )
{
final UserEntity user = this.userDao.findByKey( userKey.toString() );
if ( user != null )
{
return user.getTimestamp().getMillis();
}
return null;
}
final BinaryDataEntity binaryData = new BinaryDataForImageRequestResolver( contentDao ).resolveBinaryData( req );
if ( binaryData == null )
{
throw new ImageProcessorException( "Image not found (Content key = " + req.getContentKey().toString() + ")", null );
}
return binaryData.getCreatedAt().getTime();
}
private String getBlobKey( ImageRequest req )
{
if ( req.getUserKey() != null )
{
return getBlobKeyForUser( req.getUserKey() );
}
BinaryDataEntity binaryData = new BinaryDataForImageRequestResolver( contentDao ).resolveBinaryData( req );
if ( binaryData == null )
{
return null;
}
req.setBinaryDataKey( binaryData.getBinaryDataKey() );
return binaryData.getBlobKey();
}
private String getBlobKeyForUser( UserKey key )
{
UserEntity entity = this.userDao.findByKey( key.toString() );
if ( entity == null )
{
return null;
}
byte[] photo = entity.getPhoto();
if ( photo == null )
{
return null;
}
return DigestUtils.shaHex( photo );
}
private byte[] fetchImage( ImageRequest req )
{
if ( req.getUserKey() != null )
{
UserEntity entity = this.userDao.findByKey( req.getUserKey().toString() );
if ( entity == null )
{
return null;
}
return entity.getPhoto();
}
BlobRecord binary = this.blobStore.getRecord( new BlobKey( req.getBlobKey() ) );
if ( binary == null )
{
throw AttachmentNotFoundException.notFound( req.getBlobKey() );
}
return binary.getAsBytes();
}
private ImageResponse doProcess( ImageRequest req )
throws Exception
{
byte[] bytes = fetchImage( req );
BufferedImage image = ImageHelper.readImage( bytes );
ImageResponse imageResponse = processor.process( req, image );
imageCache.put( req, imageResponse );
return imageResponse;
}
@Autowired
public void setImageCache( ImageCache imageCache )
{
this.imageCache = imageCache;
}
@Autowired
public void setContentDao( ContentDao contentDao )
{
this.contentDao = contentDao;
}
@Autowired
public void setUserDao( UserDao userDao )
{
this.userDao = userDao;
}
@Autowired
public void setBlobStore( BlobStore blobStore )
{
this.blobStore = blobStore;
}
@Autowired
public void setTimeService( TimeService timeService )
{
this.timeService = timeService;
}
@Autowired
public void setPreviewService( PreviewService previewService )
{
this.previewService = previewService;
}
@Autowired
public void setGroupDao( GroupDao groupDao )
{
this.groupDao = groupDao;
}
@Value("${cms.blobstore.dir}")
public void setDirectory( File directory )
{
this.directory = directory;
}
}