net.anotheria.anosite.photoserver.api.photo.PhotoAPIImpl Maven / Gradle / Ivy
The newest version!
package net.anotheria.anosite.photoserver.api.photo;
import net.anotheria.anoplass.api.APIException;
import net.anotheria.anoplass.api.APIFinder;
import net.anotheria.anoplass.api.APIInitException;
import net.anotheria.anoplass.api.AbstractAPIImpl;
import net.anotheria.anoplass.api.NoLoggedInUserException;
import net.anotheria.anoplass.api.generic.login.LoginAPI;
import net.anotheria.anoprise.dualcrud.CrudService;
import net.anotheria.anoprise.dualcrud.CrudServiceException;
import net.anotheria.anoprise.dualcrud.DualCrudConfig;
import net.anotheria.anoprise.dualcrud.DualCrudService;
import net.anotheria.anoprise.dualcrud.DualCrudServiceFactory;
import net.anotheria.anoprise.dualcrud.SaveableID;
import net.anotheria.anoprise.metafactory.MetaFactory;
import net.anotheria.anoprise.metafactory.MetaFactoryException;
import net.anotheria.anosite.photoserver.api.access.AlbumAction;
import net.anotheria.anosite.photoserver.api.access.PhotoAction;
import net.anotheria.anosite.photoserver.api.blur.BlurSettingsAPI;
import net.anotheria.anosite.photoserver.api.blur.BlurSettingsAPIException;
import net.anotheria.anosite.photoserver.api.photo.ceph.PhotoCephClientService;
import net.anotheria.anosite.photoserver.api.photo.fs.PhotoStorageFSService;
import net.anotheria.anosite.photoserver.api.photo.fs.PhotoStorageToFoldersFSService;
import net.anotheria.anosite.photoserver.api.photo.google.cloud.PhotoGoogleCloudStorageService;
import net.anotheria.anosite.photoserver.api.upload.PhotoUploadAPIConfig;
import net.anotheria.anosite.photoserver.presentation.shared.PhotoDimension;
import net.anotheria.anosite.photoserver.presentation.shared.PhotoUtil;
import net.anotheria.anosite.photoserver.presentation.shared.PhotoUtilException;
import net.anotheria.anosite.photoserver.service.storage.AlbumBO;
import net.anotheria.anosite.photoserver.service.storage.AlbumNotFoundServiceException;
import net.anotheria.anosite.photoserver.service.storage.AlbumWithPhotosServiceException;
import net.anotheria.anosite.photoserver.service.storage.DefaultPhotoNotFoundServiceException;
import net.anotheria.anosite.photoserver.service.storage.PhotoBO;
import net.anotheria.anosite.photoserver.service.storage.PhotoNotFoundServiceException;
import net.anotheria.anosite.photoserver.service.storage.StorageConfig;
import net.anotheria.anosite.photoserver.service.storage.StorageService;
import net.anotheria.anosite.photoserver.service.storage.StorageServiceException;
import net.anotheria.anosite.photoserver.shared.ApprovalStatus;
import net.anotheria.anosite.photoserver.shared.CroppingType;
import net.anotheria.anosite.photoserver.shared.ModifyPhotoSettings;
import net.anotheria.anosite.photoserver.shared.PhotoServerConfig;
import net.anotheria.anosite.photoserver.shared.vo.PhotoVO;
import net.anotheria.anosite.photoserver.shared.vo.PreviewSettingsVO;
import net.anotheria.moskito.aop.annotation.Accumulate;
import net.anotheria.moskito.aop.annotation.Accumulates;
import net.anotheria.moskito.aop.annotation.Monitor;
import net.anotheria.util.StringUtils;
import net.anotheria.util.concurrency.IdBasedLock;
import net.anotheria.util.concurrency.IdBasedLockManager;
import net.anotheria.util.concurrency.SafeIdBasedLockManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.*;
import java.util.stream.Collectors;
/**
* {@link net.anotheria.anosite.photoserver.api.photo.PhotoAPIImpl} main implementation.
*
* @author Alexandr Bolbat
* @version $Id: $Id
*/
@Monitor(producerId = "PS_PhotoAPIImpl", category = "api", subsystem = "photoserver")
@Accumulates({
@Accumulate(valueName = "Avg", intervalName = "5m"),
@Accumulate(valueName = "Avg", intervalName = "1h"),
@Accumulate(valueName = "Req", intervalName = "5m"),
@Accumulate(valueName = "Req", intervalName = "1h"),
@Accumulate(valueName = "Err", intervalName = "5m"),
@Accumulate(valueName = "Err", intervalName = "1h"),
@Accumulate(valueName = "Time", intervalName = "5m"),
@Accumulate(valueName = "Time", intervalName = "1h")
})
public class PhotoAPIImpl extends AbstractAPIImpl implements PhotoAPI {
/**
* Logger.
*/
private static final Logger LOG = LoggerFactory.getLogger(PhotoAPIImpl.class);
/**
* {@link IdBasedLockManager} instance.
*/
private static final IdBasedLockManager LOCK_MANAGER = new SafeIdBasedLockManager<>();
/**
* {@link PhotoUploadAPIConfig} instance.
*/
private static final PhotoUploadAPIConfig photoUploadAPIConfig = PhotoUploadAPIConfig.getInstance();
/**
* {@link PhotoAPIConfig} instance.
*/
private static final PhotoAPIConfig photoAPIConfig = PhotoAPIConfig.getInstance();
/**
* StorageService instance.
*/
private StorageService storageService;
/**
* {@link LoginAPI} instance.
*/
private LoginAPI loginAPI;
/**
* {@link BlurSettingsAPI} instance.
*/
private BlurSettingsAPI blurSettingsAPI;
/**
* {@link DualCrudService} for photos.
*/
private DualCrudService dualCrudService;
/** {@inheritDoc} */
@Override
public void init() throws APIInitException {
try {
storageService = MetaFactory.get(StorageService.class);
} catch (MetaFactoryException e) {
throw new APIInitException("Failed to get StorageService!", e);
}
loginAPI = APIFinder.findAPI(LoginAPI.class);
blurSettingsAPI = APIFinder.findAPI(BlurSettingsAPI.class);
PhotoStorageFSService photoStorageFSService = new PhotoStorageFSService();
if (PhotoServerConfig.getInstance().isPhotoCephEnabled()) {
DualCrudConfig config = DualCrudConfig.migrateOnTheFly();
config.setDeleteUponMigration(false);
config.setWriteToBoth(true);
dualCrudService = DualCrudServiceFactory.createDualCrudService(photoStorageFSService, new PhotoCephClientService(), config);
} else if (PhotoServerConfig.getInstance().isPhotoGoogleCloudEnabled()) {
CrudService left = StringUtils.isEmpty(StorageConfig.getInstance().getStorageRootSecond()) ? photoStorageFSService : new PhotoStorageToFoldersFSService();
dualCrudService = DualCrudServiceFactory.createDualCrudService(left, new PhotoGoogleCloudStorageService(), DualCrudConfig.migrateOnTheFly());
} else if (!StringUtils.isEmpty(StorageConfig.getInstance().getStorageRootSecond())) {
dualCrudService = DualCrudServiceFactory.createDualCrudService(new PhotoStorageToFoldersFSService(), null, DualCrudConfig.useLeftOnly());
} else {
dualCrudService = DualCrudServiceFactory.createDualCrudService(photoStorageFSService, null, DualCrudConfig.useLeftOnly());
}
}
// album related method's ---------------------------------------------------------
/** {@inheritDoc} */
@Override
public AlbumAO getAlbum(long albumId) throws PhotoAPIException {
return getAlbum(albumId, PhotosFiltering.DEFAULT);
}
@Override
public String getAlbumOwnerId(long albumId) throws PhotoAPIException {
try {
return storageService.getAlbumOwnerId(albumId);
} catch (StorageServiceException e) {
String message = "getAlbumOwnerId(" + albumId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public AlbumAO getAlbum(long albumId, PhotosFiltering filtering) throws PhotoAPIException {
return getAlbum(albumId, filtering, null);
}
/** {@inheritDoc} */
@Override
public AlbumAO getAlbum(long albumId, PhotosFiltering filtering, String authorId) throws PhotoAPIException {
try {
AlbumBO album = storageService.getAlbum(albumId);
isAllowedForAction(AlbumAction.VIEW, album.getUserId(), authorId); // security check
List approvedPhotos = new ArrayList<>();
if (!album.getPhotosOrder().isEmpty()) {
approvedPhotos = filterNotApproved(album, filtering);
album.setPhotosOrder(approvedPhotos.stream().map(PhotoAO::getId).collect(Collectors.toList())); // filtering not approved photos from
}
return new AlbumAO(album, approvedPhotos);
} catch (AlbumNotFoundServiceException e) {
throw new AlbumNotFoundPhotoAPIException(albumId);
} catch (StorageServiceException e) {
String message = "getAlbum(" + albumId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public List getAlbums(String userId) throws PhotoAPIException {
return getAlbums(userId, PhotosFiltering.DEFAULT);
}
@Override
public boolean hasPhotos(String userId) {
return storageService.hasPhotos(userId);
}
/** {@inheritDoc} */
@Override
public List getAlbums(String userId, PhotosFiltering filtering) throws PhotoAPIException {
return getAlbums(userId, filtering, null);
}
/** {@inheritDoc} */
@Override
public List getAlbums(String userId, PhotosFiltering filtering, String authorId) throws PhotoAPIException {
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
isAllowedForAction(AlbumAction.VIEW, userId, authorId); // security check
try {
List result = new ArrayList<>();
for (AlbumBO album : storageService.getAlbums(userId)) {
List approvedPhotos = new ArrayList<>();
if (!album.getPhotosOrder().isEmpty()) {
approvedPhotos = filterNotApproved(album, filtering);
album.setPhotosOrder(approvedPhotos.stream().map(PhotoAO::getId).collect(Collectors.toList())); // filtering not approved photos from
}
result.add(new AlbumAO(album, approvedPhotos));
}
return result;
} catch (StorageServiceException e) {
String message = "getAlbums(" + userId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public AlbumAO getDefaultAlbum(String userId) throws PhotoAPIException {
return getDefaultAlbum(userId, PhotosFiltering.DEFAULT);
}
/** {@inheritDoc} */
@Override
public AlbumAO getDefaultAlbum(String userId, PhotosFiltering filtering) throws PhotoAPIException {
return getDefaultAlbum(userId, filtering, null);
}
@Override
public Long getDefaultAlbumId(String userId, PhotosFiltering filtering) throws PhotoAPIException {
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
try {
return storageService.getDefaultAlbum(userId).getId();
} catch (StorageServiceException e) {
String message = "getDefaultAlbum(" + userId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public AlbumAO getDefaultAlbum(String userId, PhotosFiltering filtering, String authorId) throws PhotoAPIException {
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
try {
AlbumBO album = storageService.getDefaultAlbum(userId);
isAllowedForAction(AlbumAction.VIEW, album.getUserId(), authorId); // security check
List approvedPhotos = new ArrayList<>();
if (!album.getPhotosOrder().isEmpty()) {
approvedPhotos = filterNotApproved(album, filtering);
album.setPhotosOrder(approvedPhotos.stream().map(PhotoAO::getId).collect(Collectors.toList())); // filtering not approved photos from
}
return new AlbumAO(album, approvedPhotos);
} catch (StorageServiceException e) {
String message = "getDefaultAlbum(" + userId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public AlbumAO createAlbum(AlbumAO album) throws PhotoAPIException {
return createAlbum(album, null);
}
/** {@inheritDoc} */
@Override
public AlbumAO createAlbum(AlbumAO album, String authorId) throws PhotoAPIException {
if (album == null)
throw new IllegalArgumentException("Null album");
isAllowedForAction(AlbumAction.CREATE, album.getUserId(), authorId); // security check
try {
return new AlbumAO(storageService.createAlbum(new AlbumBO(album)));
} catch (StorageServiceException e) {
String message = "createAlbum(" + album + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public AlbumAO updateAlbum(AlbumAO album) throws PhotoAPIException {
return updateAlbum(album, null);
}
/** {@inheritDoc} */
@Override
public AlbumAO updateAlbum(AlbumAO album, String authorId) throws PhotoAPIException {
if (album == null)
throw new IllegalArgumentException("Null album");
isAllowedForAction(AlbumAction.EDIT, album.getUserId(), authorId); // security check
try {
return new AlbumAO(storageService.updateAlbum(new AlbumBO(album)));
} catch (StorageServiceException e) {
String message = "updateAlbum(" + album + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public AlbumAO removeAlbum(long albumId) throws PhotoAPIException {
return removeAlbum(albumId, null);
}
/** {@inheritDoc} */
@Override
public AlbumAO removeAlbum(long albumId, String authorId) throws PhotoAPIException {
AlbumAO result = getAlbum(albumId, PhotosFiltering.DISABLED);
isAllowedForAction(AlbumAction.REMOVE_PHOTO, result.getUserId(), authorId); // security check
try {
return new AlbumAO(storageService.removeAlbum(albumId));
} catch (AlbumWithPhotosServiceException e) {
throw new AlbumWithPhotosPhotoAPIException(albumId);
} catch (StorageServiceException e) {
String message = "removeAlbum(" + albumId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
// "my" album related method's ---------------------------------------------------------
/** {@inheritDoc} */
@Override
public List getMyAlbums() throws PhotoAPIException {
return getAlbums(getMyUserId(), PhotosFiltering.DISABLED);
}
/** {@inheritDoc} */
@Override
public AlbumAO getMyDefaultAlbum() throws PhotoAPIException {
return getDefaultAlbum(getMyUserId(), PhotosFiltering.DISABLED);
}
// photo related method's ---------------------------------------------------------
/** {@inheritDoc} */
@Override
public PhotoAO getMyDefaultPhoto() throws PhotoAPIException {
return getDefaultPhoto(getMyUserId());
}
/** {@inheritDoc} */
@Override
public PhotoAO getDefaultPhoto(String userId) throws PhotoAPIException {
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
try {
PhotoBO photo = storageService.getDefaultPhoto(userId);
isAllowedToMe(PhotoAction.VIEW, photo.getUserId(), userId); // security check
PhotoAO result = new PhotoAO(photo);
// populate Blur settings!
result.setBlurred(blurSettingsAPI.readMyBlurSettings(photo.getAlbumId(), photo.getId()));
result.setType(storageService.getAlbum(result.getAlbumId()).getName());
return result;
} catch (DefaultPhotoNotFoundServiceException e) {
LOG.debug("getDefaultPhoto(" + userId + ") failed");
throw new DefaultPhotoNotFoundAPIException(e.getMessage());
} catch (StorageServiceException | BlurSettingsAPIException e) {
String message = "getDefaultPhoto(" + userId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public PhotoAO getDefaultPhoto(String userId, long albumId) throws PhotoAPIException {
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
try {
PhotoBO photo = storageService.getDefaultPhoto(userId, albumId);
isAllowedToMe(PhotoAction.VIEW, photo.getUserId(), userId); // security check
PhotoAO result = new PhotoAO(photo);
// populate Blur settings!
result.setBlurred(blurSettingsAPI.readMyBlurSettings(photo.getAlbumId(), photo.getId()));
result.setType(storageService.getAlbum(result.getAlbumId()).getName());
return result;
} catch (DefaultPhotoNotFoundServiceException e) {
LOG.error("getDefaultPhoto(" + userId + ", " + albumId + ") failed", e);
throw new DefaultPhotoNotFoundAPIException(e.getMessage());
} catch (StorageServiceException | BlurSettingsAPIException e) {
String message = "getDefaultPhoto(" + userId + "," + albumId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public PhotoAO getPhoto(long photoId) throws PhotoAPIException {
try {
PhotoVO photo = storageService.getPhoto(photoId);
isAllowedToMe(PhotoAction.VIEW, photo.getUserId(), photo.getUserId()); // security check
PhotoAO result = new PhotoAO(photo);
result.setBlurred(blurSettingsAPI.readMyBlurSettings(photo.getAlbumId(), photo.getId()));
result.setType(storageService.getAlbum(result.getAlbumId()).getName());
return result;
} catch (PhotoNotFoundServiceException e) {
throw new PhotoNotFoundPhotoAPIException(photoId);
} catch (StorageServiceException | BlurSettingsAPIException e) {
String message = "getPhoto(" + photoId + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public List getPhotos(long albumId) throws PhotoAPIException {
return getPhotos(albumId, PhotosFiltering.DEFAULT);
}
/** {@inheritDoc} */
@Override
public List getPhotos(long albumId, PhotosFiltering filtering) throws PhotoAPIException {
final String defaultPhotoOwnerId = "-10"; //remove after refactoring - for now it's actual for failing security check.
isAllowedToMe(PhotoAction.VIEW, defaultPhotoOwnerId, defaultPhotoOwnerId); // security check
return getAlbum(albumId, filtering).getPhotos();
}
/**
* Create photoAO collection populated with BlurSettings, etc.
*
* @param albumId id of album
* @param photoVOs photoBO itself
* @return {@link java.util.List}
* @throws BlurSettingsAPIException on BlurSettingsAPI errors
*/
private List preparePhotos(long albumId, List photoVOs) throws BlurSettingsAPIException {
if (photoVOs.isEmpty())
return new ArrayList<>();
List result = new ArrayList<>(photoVOs.size());
List ids = new ArrayList<>(photoVOs.size());
for (PhotoBO photo : photoVOs) {
result.add(new PhotoAO(photo));
ids.add(photo.getId());
}
Map blurSettingsMap = blurSettingsAPI.readMyBlurSettings(albumId, ids);
for (PhotoAO photoAO : result) {
Boolean blurred = blurSettingsMap.get(photoAO.getId());
if (blurred != null)
photoAO.setBlurred(blurred);
try {
photoAO.setType(storageService.getAlbum(photoAO.getAlbumId()).getName());
} catch (StorageServiceException e) {
}
}
return result;
}
/** {@inheritDoc} */
@Override
public PhotoAO createPhoto(String userId, File tempFile, PreviewSettingsVO previewSettings) throws PhotoAPIException {
return createPhoto(userId, tempFile, previewSettings, false);
}
/** {@inheritDoc} */
@Override
public PhotoAO createPhoto(String userId, File tempFile, PreviewSettingsVO previewSettings, boolean restricted) throws PhotoAPIException {
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
if (tempFile == null)
throw new IllegalArgumentException("Null temp file");
isAllowedToMe(PhotoAction.ADD, userId, userId); // security check
long albumId = getDefaultAlbumId(userId, PhotosFiltering.DISABLED);
return createPhoto(userId, albumId, restricted, tempFile, previewSettings);
}
/** {@inheritDoc} */
@Override
public PhotoAO createPhoto(String userId, long albumId, File tempFile, PreviewSettingsVO previewSettings) throws PhotoAPIException {
return createPhoto(userId, albumId, false, tempFile, previewSettings);
}
/** {@inheritDoc} */
@Override
public PhotoAO createPhoto(String userId, long albumId, boolean restricted, File tempFile, PreviewSettingsVO previewSettings) throws PhotoAPIException {
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
if (tempFile == null)
throw new IllegalArgumentException("Null temp file");
isAllowedToMe(PhotoAction.ADD, userId, userId); // security check
AlbumAO album = getAlbum(albumId, PhotosFiltering.DISABLED, userId);
PhotoBO photo = new PhotoBO();
photo.setUserId(userId);
photo.setAlbumId(albumId);
photo.setRestricted(restricted);
photo.setExtension(PhotoUploadAPIConfig.getInstance().getFilePrefix());
photo.setPreviewSettings(previewSettings);
try {
// creating photo
photo = storageService.createPhoto(photo);
PhotoFileHolder photoFileHolder = new PhotoFileHolder(String.valueOf(photo.getId()), photo.getId(), photo.getExtension(), photo.getUserId());
photoFileHolder.setPhotoFileInputStream(Files.newInputStream(tempFile.toPath()));
photoFileHolder.setFileLocation(StorageConfig.getStoreFolderPath(String.valueOf(photo.getUserId())));
dualCrudService.create(photoFileHolder);
photoFileHolder.closeInputStream();
// updating photo album
album.addPhotoToPhotoOrder(photo.getId());
updateAlbum(album, userId);
return new PhotoAO(photo);
} catch (StorageServiceException | CrudServiceException | IOException e) {
String message = "createPhoto(" + userId + ", " + tempFile + ", " + previewSettings + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public PhotoAO updatePhoto(PhotoAO photo) throws PhotoAPIException {
return updatePhoto(getMyUserId(), photo);
}
/** {@inheritDoc} */
@Override
public PhotoAO updatePhoto(String userId, PhotoAO photo) throws PhotoAPIException {
if (photo == null)
throw new IllegalArgumentException("Null photo");
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
isAllowedToMe(PhotoAction.EDIT, photo.getUserId(), userId); // security check
try {
return new PhotoAO(storageService.updatePhoto(new PhotoBO(photo)));
} catch (PhotoNotFoundServiceException e) {
throw new PhotoNotFoundPhotoAPIException(photo.getId());
} catch (StorageServiceException e) {
String message = "updatePhoto(" + photo + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public PhotoAO removePhoto(long photoId) throws PhotoAPIException {
return removePhoto(getMyUserId(), photoId);
}
/** {@inheritDoc} */
@Override
public PhotoAO removePhoto(String userId, long photoId) throws PhotoAPIException {
if (StringUtils.isEmpty(userId))
throw new IllegalArgumentException("UserId is not valid");
PhotoAO photo = getPhoto(photoId);
isAllowedToMe(PhotoAction.EDIT, photo.getUserId(), userId); // security check
try {
storageService.removePhoto(photoId);
PhotoFileHolder photoFileHolder = new PhotoFileHolder(String.valueOf(photoId), photoId, photo.getExtension(), photo.getUserId());
photoFileHolder.setFileLocation(StorageConfig.getStoreFolderPath(String.valueOf(photo.getUserId())));
dualCrudService.delete(photoFileHolder);
return photo;
} catch (PhotoNotFoundServiceException e) {
throw new PhotoNotFoundPhotoAPIException(photo.getId());
} catch (StorageServiceException e) {
String message = "removePhoto(" + photo + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
} catch (CrudServiceException e) {
String message = "removePhoto(" + photo + ") fail. Delete from storage fail. ";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
} finally {
AlbumAO album = getAlbum(photo.getAlbumId(), PhotosFiltering.DISABLED);
album.removePhotofromPhotoOrder(photoId);
try {
storageService.updateAlbum(new AlbumBO(album));
} catch (StorageServiceException e) {
LOG.warn("removePhoto(" + photo + ") fail. Failed to remove photo from albums photoOrder.", e);
}
}
}
// ------------------------------------------------------------------------------------------------------------------------------------
/**
* Get currently logged in user id.
*
* @return id of currently logged in user
*/
private String getMyUserId() throws PhotoAPIException {
try {
return getLoggedInUserId();
} catch (NoLoggedInUserException e) {
throw new PhotoAPIException("User not logged in", e);
}
}
/**
* Check is user can perform action on photo.
*
* @param photoAction - action that user tries to perform.
* @param userId - user id that try to do action
* @param photoOwnerId - photo owner user id
* @throws PhotoAPIException if any errors occurs
*/
private void isAllowedToMe(PhotoAction photoAction, String photoOwnerId, String userId) throws PhotoAPIException {
if (PhotoAction.VIEW.equals(photoAction) || photoOwnerId.equals(userId))
return;
throw new NoAccessPhotoAPIException("No access.");
}
/**
* Check is user can perform action on photo.
*
* @param albumAction - action that user tries to perform.
* @param albumOwnerId - album owner id
* @throws PhotoAPIException if any errors occurs
*/
private void isAllowedForAction(AlbumAction albumAction, String albumOwnerId, String authorId) throws PhotoAPIException {
// TODO: fix this ugly method in future
boolean result = false;
switch (albumAction) {
case VIEW:
result = true; // all can see all photos
break;
case CREATE:
result = !StringUtils.isEmpty(authorId) || loginAPI.isLogedIn(); // logged in users can add albums
break;
case REMOVE_PHOTO:
result = (StringUtils.isEmpty(authorId) && albumOwnerId != null) ||//case for not logged user(some deletion job for example)
loginAPI.isLogedIn() && albumOwnerId != null && getMyUserId().equals(albumOwnerId); // logged in users can do anything with own albums
break;
default:
result = (!StringUtils.isEmpty(authorId) && albumOwnerId != null && authorId.equals(albumOwnerId) || loginAPI.isLogedIn() && albumOwnerId != null && getMyUserId().equals(albumOwnerId)); // logged in users can do anything with own albums
break;
}
if (!result)
throw new NoAccessPhotoAPIException("No access.");
}
/** {@inheritDoc} */
@Override
public int getWaitingApprovalPhotosCount() throws PhotoAPIException {
try {
return storageService.getWaitingApprovalPhotosCount();
} catch (StorageServiceException e) {
String message = "getWaitingApprovalPhotosCount() failed.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public List getWaitingApprovalPhotos(int amount) throws PhotoAPIException {
if (amount < 0)
throw new IllegalArgumentException("Illegal photos amount selected amount[" + amount + "]");
if (amount == 0) {
return new ArrayList<>();
}
try {
// use same method as for bulk change of approval statuses.
return map(storageService.getWaitingApprovalPhotos(amount));
} catch (StorageServiceException e) {
String message = "getWaitingApprovalPhotos(" + amount + ") failed.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/**
* Map {@link PhotoBO} collection to {@link PhotoAO} collection.
*
* @param waitingApprovalPhotos incoming PhotoBO collection
* @return mapped result
*/
private List map(List waitingApprovalPhotos) {
List result = new ArrayList<>(waitingApprovalPhotos.size());
for (PhotoBO photoBO : waitingApprovalPhotos)
result.add(new PhotoAO(photoBO));
return result;
}
/** {@inheritDoc} */
@Override
public void setApprovalStatus(long photoId, ApprovalStatus status) throws PhotoAPIException {
if (status == null) {
throw new IllegalArgumentException("Null ApprovalStatus!");
}
try {
// using same method as for bulk change of approval statuses.
storageService.updatePhotoApprovalStatuses(Collections.singletonMap(photoId, status));
} catch (StorageServiceException e) {
String message = "setApprovalStatus(" + photoId + ", " + status + ") failed.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public void setApprovalStatuses(Map statuses) throws PhotoAPIException {
if (statuses == null) {
throw new IllegalArgumentException("Null argument received!");
}
try {
storageService.updatePhotoApprovalStatuses(statuses);
} catch (StorageServiceException e) {
String message = "setApprovalStatuses(" + statuses + ") failed.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
/** {@inheritDoc} */
@Override
public PhotoAO movePhoto(long photoId, long newAlbumId) throws PhotoAPIException {
PhotoAO photo = getPhoto(photoId);
AlbumAO album = getAlbum(newAlbumId, PhotosFiltering.DISABLED);
AlbumAO oldAlbum = getAlbum(photo.getAlbumId(), PhotosFiltering.DISABLED);
if (!album.getUserId().equals(photo.getUserId()))
throw new NoAccessPhotoAPIException("No access.");
isAllowedToMe(PhotoAction.EDIT, photo.getUserId(), photo.getUserId()); // security check
isAllowedForAction(AlbumAction.EDIT, album.getUserId(), photo.getUserId()); // security check
try {
PhotoAO updatedPhoto = new PhotoAO(storageService.movePhoto(photoId, newAlbumId));
oldAlbum.removePhotofromPhotoOrder(photoId);
storageService.updateAlbum(new AlbumBO(oldAlbum));
album.addPhotoToPhotoOrder(photoId);
storageService.updateAlbum(new AlbumBO(album));
return updatedPhoto;
} catch (PhotoNotFoundServiceException e) {
throw new PhotoNotFoundPhotoAPIException(photo.getId());
} catch (StorageServiceException e) {
String message = "updatePhoto(" + photo + ") fail.";
LOG.warn(message, e);
throw new PhotoAPIException(message, e);
}
}
@Override
public InputStream getPhotoContent(PhotoAO photo) throws PhotoAPIException {
if (photo == null)
throw new IllegalArgumentException("Photo is null");
try {
return getPhotoContent(String.valueOf(photo.getId()), photo);
} catch (CrudServiceException e) {
String message = "Unable to read photo stream from storage: " + e.getMessage();
LOG.error(message, e);
throw new PhotoAPIException(message, e);
}
}
@Override
public InputStream getPhotoContent(long photoId) throws PhotoAPIException {
try{
return getCachedPhotoContent(getPhoto(photoId), new ModifyPhotoSettings(), true, CroppingType.BOTH.getValue(), false);
} catch (Exception e) {
String message = "Unable to read photo stream from storage: " + e.getMessage();
LOG.error(message, e);
throw new PhotoAPIException(message, e);
}
}
@Override
public InputStream getCachedPhotoContent(PhotoAO photoAO, ModifyPhotoSettings modifyPhotoSettings, boolean cropped, int croppingType, boolean blurred) throws PhotoAPIException {
// preparing cached photo postfix
String cachedFileName = String.valueOf(photoAO.getId());
cachedFileName += cropped ? "_c_t" + croppingType : "";
if (modifyPhotoSettings.isResized()) {
switch (modifyPhotoSettings.getResizeType()) {
case SIZE:
cachedFileName += "_s" + modifyPhotoSettings.getSize();
break;
case BOUNDING_AREA:
cachedFileName += "_ba" + modifyPhotoSettings.getBoundaryWidth() + "_" + modifyPhotoSettings.getBoundaryHeight();
break;
}
}
cachedFileName += blurred ? "_b" : "";
try {
return getPhotoContent(cachedFileName, photoAO);
} catch (CrudServiceException e) {
//Cached file not found, try to generate new.
}
// locking all incoming photo modification requests for same picture
IdBasedLock lock = LOCK_MANAGER.obtainLock(cachedFileName);
lock.lock();
try {
// checking again cached photo and steaming it if exist
try {
return getPhotoContent(cachedFileName, photoAO);
} catch (CrudServiceException e) {
//Cached file not found in lock again, try to generate new.
}
modifyPhotoSettings.setCropped(cropped);
modifyPhotoSettings.setBlurred(blurred);
modifyPhotoSettings.setCroppingType(CroppingType.valueOf(croppingType));
// modifying photo and storing to new photo file
modifyPhoto(cachedFileName, modifyPhotoSettings, photoAO);
//try to read cached photo again after save
return getPhotoContent(cachedFileName, photoAO);
} catch (IOException | CrudServiceException e) {
String failMsg = "Unable to process cached file after save. " + e.getMessage();
LOG.error(failMsg, e);
throw new PhotoAPIException(failMsg);
} finally {
lock.unlock();
}
}
/**
* Modify photo and store new file in storage.
*
* @param cachedFileName file name
* @param modifyPhotoSettings {@link ModifyPhotoSettings} for photo
* @param photoAO {@link PhotoAO} instance
* @throws PhotoAPIException if errors occurs
* @throws IOException if errors occurs
* @throws CrudServiceException if errors occurs
*/
private void modifyPhoto(String cachedFileName, ModifyPhotoSettings modifyPhotoSettings, PhotoAO photoAO) throws PhotoAPIException, IOException, CrudServiceException {
final PreviewSettingsVO pvSettings = photoAO.getPreviewSettings();
// read photo file
PhotoUtil putil = new PhotoUtil();
try {
putil.read(getPhotoContent(photoAO));
} catch (PhotoUtilException e) {
throw new PhotoAPIException("Unable to read photo. " + e.getMessage());
}
// if blur param is present or photo should be blurred for user we have to blur image
if (modifyPhotoSettings.isBlurred())
putil.blur();
// if preview param is present we have to crop image first
if (modifyPhotoSettings.isCropped()) {
PhotoDimension move = new PhotoDimension(pvSettings.getX(), pvSettings.getY());
PhotoDimension crop = new PhotoDimension(pvSettings.getWidth(), pvSettings.getHeight());
PhotoDimension originalDimension = new PhotoDimension(putil.getWidth(), putil.getHeight());
PhotoDimension workbenchDimension;
if (putil.getHeight() > putil.getWidth()) {
workbenchDimension = new PhotoDimension(photoUploadAPIConfig.getWorkbenchWidth() * putil.getWidth() / putil.getHeight(),
photoUploadAPIConfig.getWorkbenchWidth());
} else {
workbenchDimension = new PhotoDimension(photoUploadAPIConfig.getWorkbenchWidth(), photoUploadAPIConfig.getWorkbenchWidth() * putil.getHeight()
/ putil.getWidth());
}
PhotoDimension xy = move.getRelationTo(workbenchDimension, originalDimension);
PhotoDimension wh = crop.getRelationTo(workbenchDimension, originalDimension);
putil.crop(xy.w, xy.h, wh.w, wh.h);
}
// scale photo if needed
if (modifyPhotoSettings.isResized()) {
int height = putil.getHeight();
int width = putil.getWidth();
switch (modifyPhotoSettings.getResizeType()) {
// scale by size
case SIZE:
int size = modifyPhotoSettings.getSize();
switch (modifyPhotoSettings.getCroppingType()) {
case HEIGHT:
putil.scale((int) ((double) size / height * width), size);
break;
case NATURAL_HEIGHT:
height = height < size ? height : size;
width = (int) ((double) height / putil.getHeight() * width);
putil.scale(width, height);
break;
case WIDTH:
putil.scale(size, (int) ((double) size / width * height));
break;
case NATURAL_WIDTH:
width = width < size ? width : size;
height = (int) ((double) width / putil.getWidth() * height);
putil.scale(width, height);
break;
case BOTH:
putil.scale(size);
break;
case NATURAL_BOTH:
if (width > size && height > size)
putil.scale(size);
break;
}
break;
// scale along the bounding area
case BOUNDING_AREA:
scaleAlongBoundary(putil, width, height, modifyPhotoSettings.getBoundaryWidth(), modifyPhotoSettings.getBoundaryHeight());
break;
}
}
File baseFolder = new File(StorageConfig.getTmpStoreFolderPath(photoAO.getUserId()));
baseFolder.mkdirs();
File tmpFile = new File(baseFolder, cachedFileName + photoAO.getExtension());
putil.write(photoAPIConfig.getJpegQuality(), tmpFile);
PhotoFileHolder photoFileHolder = new PhotoFileHolder(cachedFileName, photoAO.getId(), photoAO.getExtension(), photoAO.getUserId());
photoFileHolder.setPhotoFileInputStream(Files.newInputStream(tmpFile.toPath()));
photoFileHolder.setFileLocation(StorageConfig.getStoreFolderPath(String.valueOf(photoAO.getUserId())));
dualCrudService.create(photoFileHolder);
photoFileHolder.closeInputStream();
tmpFile.delete();
}
private InputStream getPhotoContent(String id, PhotoAO photoAO) throws CrudServiceException {
PhotoFileHolder photoFileHolder = new PhotoFileHolder(id, photoAO.getId(), photoAO.getExtension(), photoAO.getUserId());
photoFileHolder.setFileLocation(StorageConfig.getStoreFolderPath(String.valueOf(photoAO.getUserId())));
SaveableID saveableID = new SaveableID();
saveableID.setOwnerId(photoFileHolder.getOwnerId());
saveableID.setSaveableId(photoFileHolder.getFilePath() + "______USER_ID______" + photoAO.getUserId());
PhotoFileHolder ret = dualCrudService.read(saveableID);
InputStream inputStream = ret.getPhotoFileInputStream();
ret.closeInputStream();
return inputStream;
}
/**
* Scale width and height of the original image along the incoming width and height of the bounding area .
*
* @param photoUtil {@link PhotoUtil}
* @param width original image width
* @param height original image height
* @param boundaryWidth width of the bounding area
* @param boundaryHeight height of the bounding area
*/
private void scaleAlongBoundary(final PhotoUtil photoUtil, int width, int height, int boundaryWidth, int boundaryHeight) {
double boundaryAspectRatio = (double) boundaryWidth / boundaryHeight;
double originalImageAspectRatio = (double) width / height;
if (boundaryAspectRatio < originalImageAspectRatio) {
photoUtil.scale(boundaryWidth, (int) ((double) boundaryWidth / width * height));
return;
}
if (boundaryAspectRatio > originalImageAspectRatio) {
photoUtil.scale((int) ((double) boundaryHeight / height * width), boundaryHeight);
return;
}
// case when aspect ratio is the same
int size = boundaryWidth >= boundaryHeight ? boundaryWidth : boundaryHeight;
photoUtil.scale(size);
}
private List filterNotApproved(AlbumBO album, PhotosFiltering filtering) throws PhotoAPIException {
try {
List photos = preparePhotos(album.getId(), storageService.getPhotos(album.getUserId(), album.getId()));
Map photosMap = new LinkedHashMap<>();
List result = new ArrayList<>();
if (filtering == null)
filtering = PhotosFiltering.DEFAULT;
if (!filtering.filteringEnabled || !PhotoServerConfig.getInstance().isPhotoApprovingEnabled()) {
photosMap = photos.stream().collect(Collectors.toMap(PhotoAO::getId, photo -> photo));
} else {
for (PhotoAO photo : photos) {
if (loginAPI.isLogedIn() && loginAPI.getLogedUserId().equalsIgnoreCase(String.valueOf(photo.getUserId()))) {
photosMap.put(photo.getId(), photo);
continue;
}
if (filtering.allowedStatuses.contains(photo.getApprovalStatus()))
photosMap.put(photo.getId(), photo);
}
}
for (Long id : album.getPhotosOrder()) {
PhotoAO photo = photosMap.remove(id);
if (photo != null) {
result.add(photo);
}
}
return result;
} catch (APIException | StorageServiceException | BlurSettingsAPIException e) {
throw new PhotoAPIException("filterNotApproved(" + album.getId() + ") fail.", e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy