/*
* Data Hub Service (DHuS) - For Space data distribution.
* Copyright (C) 2013,2014,2015 GAEL Systems
*
* This file is part of DHuS software sources.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package fr.gael.dhus.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import fr.gael.dhus.database.dao.CollectionDao;
import fr.gael.dhus.database.dao.ProductDao;
import fr.gael.dhus.database.dao.UserDao;
import fr.gael.dhus.database.object.Collection;
import fr.gael.dhus.database.object.Product;
import fr.gael.dhus.database.object.Role;
import fr.gael.dhus.database.object.User;
import fr.gael.dhus.service.exception.CollectionNameExistingException;
import fr.gael.dhus.service.exception.RequiredFieldMissingException;
import fr.gael.dhus.system.config.ConfigurationManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.hibernate.Hibernate;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* Collection Service provides connected clients with a set of method to
* interact with it.
*/
@Service
public class CollectionService extends WebService
{
private static final Logger LOGGER = LogManager.getLogger(CollectionService.class);
@Autowired
private CollectionDao collectionDao;
@Autowired
private ProductDao productDao;
@Autowired
private UserDao userDao;
@Autowired
private SecurityService securityService;
@Autowired
private SearchService searchService;
@Autowired
private ConfigurationManager cfgManager;
@PreAuthorize ("hasRole('ROLE_DATA_MANAGER')")
@Transactional (readOnly=false, propagation=Propagation.REQUIRED)
public Collection createCollection(Collection collection) throws
RequiredFieldMissingException, CollectionNameExistingException
{
// Can user securityService.getCurrentUser() because
// there is a required role.
User user = securityService.getCurrentUser();
checkRequiredFields(collection);
checkName(collection);
return collectionDao.create (collection, user);
}
@PreAuthorize ("hasRole('ROLE_DATA_MANAGER')")
@Transactional (readOnly=false, propagation=Propagation.REQUIRED)
public void updateCollection(Collection collection) throws
RequiredFieldMissingException
{
checkRequiredFields(collection);
Collection c = collectionDao.read (collection.getUUID ());
String old_name = c.getName();
String new_name = collection.getName();
c.setName(new_name);
c.setDescription (collection.getDescription ());
if (!new_name.equals(old_name))
{
Iterator<Collection> collectionIterator =
collectionDao.getAllCollections ();
while (collectionIterator.hasNext ())
{
c = collectionIterator.next ();
for (Product product : c.getProducts ())
{
searchService.index (product);
}
}
}
}
@PreAuthorize ("hasRole('ROLE_DATA_MANAGER')")
@Transactional (readOnly=false, propagation=Propagation.REQUIRED)
public void deleteCollection(String uuid)
{
Collection collection = collectionDao.read (uuid);
LOGGER.info("Removing collection " + collection.getName ());
for (Product product : collection.getProducts ())
{
searchService.index (product);
}
collectionDao.delete (collection);
}
@PreAuthorize ("hasAnyRole('ROLE_DATA_MANAGER','ROLE_SEARCH')")
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public Collection getCollection (String uuid)
{
return systemGetCollection (uuid);
}
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public List<Collection> getCollections (Product product)
{
return collectionDao.getCollectionsOfProduct(product.getId());
}
@PreAuthorize ("hasRole('ROLE_DATA_MANAGER')")
@Transactional (readOnly=false, propagation=Propagation.REQUIRED)
public void removeProducts (String uuid, Long[] pids)
{
collectionDao.removeProducts (uuid, pids, null);
long start = new Date ().getTime ();
for (Long pid: pids)
{
searchService.index(productDao.read(pid));
}
long end = new Date ().getTime ();
LOGGER.info("[SOLR] Remove " + pids.length +
" product(s) from collection spent " + (end-start) + "ms" );
}
@PreAuthorize ("hasRole('ROLE_DATA_MANAGER')")
@Transactional (readOnly=false, propagation=Propagation.REQUIRED)
public void addProducts (String uuid, Long[] pids)
{
for (int i = 0; i < pids.length; i++)
{
systemAddProduct (uuid, pids[i], true);
}
}
@Transactional (readOnly=false, propagation=Propagation.REQUIRED)
public void systemAddProduct (String uuid, Long pid, boolean followRights)
{
Collection collection = collectionDao.read (uuid);
Product product = productDao.read (pid);
this.addProductInCollection(collection, product);
searchService.index(product);
}
private void addProductInCollection (Collection collection, Product product)
{
Hibernate.initialize(collection.getProducts());
if (!collection.getProducts().contains(product))
{
collection.getProducts().add(product);
collectionDao.update (collection);
}
}
@PreAuthorize ("hasAnyRole('ROLE_DATA_MANAGER','ROLE_SEARCH')")
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public List<Long> getProductIds(String uuid)
{
User user = securityService.getCurrentUser();
return collectionDao.getProductIds (uuid, user);
}
@PreAuthorize ("hasAnyRole('ROLE_DATA_MANAGER','ROLE_SEARCH')")
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public Integer count ()
{
// Can use securityService.getCurrentUser() because
// there is a required
// role.
User user = securityService.getCurrentUser ();
return collectionDao.count (user);
}
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
private void checkName (Collection collection)
throws RequiredFieldMissingException, CollectionNameExistingException
{
final String toCheck = collection.getName ();
if (toCheck == null)
{
throw new RequiredFieldMissingException (
"At least one required field is empty.");
}
Iterator<Collection> it = collectionDao.getAllCollections ();
while (it.hasNext ())
{
final String name = it.next ().getName ();
if (toCheck.equals (name))
{
throw new CollectionNameExistingException ("Collection name '" +
collection.getName () + "' is already used.");
}
}
}
private void checkRequiredFields(Collection collection) throws
RequiredFieldMissingException
{
if (collection.getName () == null || collection.getName ().trim ()
.isEmpty ())
{
throw new RequiredFieldMissingException (
"At least one required field is empty.");
}
}
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public Product getProduct (String uuid, String collection_uuid, User u)
{
Product p = productDao.getProductByUuid(uuid);
if (collectionDao.contains(collection_uuid, p.getId()))
return p;
return null;
}
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public List<Product> getAuthorizedProducts (String uuid, User u)
{
Collection collection = collectionDao.read (uuid);
if (collection == null)
{
return Collections.emptyList ();
}
List<Product> products = new LinkedList<> ();
Iterator<Long> it = getProductIds (collection.getUUID ()).iterator ();
while (it.hasNext ())
{
Long pid = it.next ();
if (pid != null)
{
Product p = productDao.read (pid);
if (p != null)
{
products.add (p);
}
}
}
return products;
}
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public String getCollectionUUIDByName(String collection_name)
{
return collectionDao.getCollectionUUIDByName(collection_name);
}
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public boolean hasAccessToCollection (String cid, String uid)
{
User user = userDao.read (uid);
if (user == null) return false;
if (user.getRoles ().contains (Role.DATA_MANAGER)) return true;
return collectionDao.hasAccessToCollection (cid, uid);
}
@Transactional (readOnly=true, propagation=Propagation.REQUIRED)
public boolean containsProduct (String uuid, Long pid)
{
if (uuid == null) return false;
return collectionDao.contains (uuid, pid);
}
@Transactional (readOnly=true)
public List<Collection> getCollectionsOfProduct(Product p)
{
return collectionDao.getCollectionsOfProduct(p.getId());
}
/**
* Retrieves collections higher authorized collection of the given user in
* function of the given criteria.
*
* @param criteria criteria contains filter and order of required collection.
* @param user
* @param skip number of skipped valid results.
* @param top max of valid results.
* @return a list of {@link Collection}
*/
@Transactional(readOnly = true)
public List<Collection> getHigherCollections (DetachedCriteria criteria,
User user, int skip, int top)
{
if (criteria == null)
{
criteria = DetachedCriteria.forClass (Collection.class);
}
List<String> cids = new ArrayList<> ();
if (cfgManager.isDataPublic () ||
user.getRoles ().contains (Role.DATA_MANAGER))
{
Iterator<Collection> it = collectionDao.getAllCollections ();
while (it.hasNext ())
{
cids.add (it.next ().getUUID ());
}
}
else
{
List<String> collections =
collectionDao.getAuthorizedCollections (user.getUUID ());
Iterator<String> it = collections.iterator ();
while (it.hasNext ())
{
cids.add (it.next ());
}
}
if (!cids.isEmpty())
{
criteria.add(Restrictions.in("uuid", cids));
}
return collectionDao.listCriteria (criteria, skip, top);
}
@Transactional(readOnly = true)
public int countHigherCollections (DetachedCriteria detached, User user)
{
return getHigherCollections (detached, user, 0, 0).size ();
}
/**
* Counts of authorized collections for the given user.
* @param user the user to filter collections.
* @return number of collections than can see the user.
*/
@Transactional(readOnly = true)
public int countAuthorizedCollections (User user)
{
if (user == null)
{
throw new IllegalArgumentException ("User must not be null !");
}
if (cfgManager.isDataPublic () ||
user.getRoles ().contains (Role.DATA_MANAGER))
{
return productDao.count ();
}
return 0;
}
/**
* Retrieves all authorized collections for the given user.
*
* @param u the given user.
* @return a set of authorized collections.
*/
@Transactional(readOnly = true)
public Set<Collection> getAuthorizedCollection (User u)
{
HashSet<Collection> collections = new HashSet<> ();
for (String cid : collectionDao.getAuthorizedCollections (u.getUUID ()))
{
Collection collection = collectionDao.read (cid);
if (collection != null)
{
collections.add (collection);
}
}
return collections;
}
/**
* Retrieves a collection by its name.
* <p>Checks also if the given user is authorized to access it.</p>
*
* @param name collection name.
* @param u the current user.
* @return the named collection or null.
*/
@Transactional(readOnly = true)
public Collection getAuthorizedCollectionByName (String name, User u)
{
if (name != null && u != null)
{
Collection collection = collectionDao.read (
getCollectionUUIDByName (name));
if (collection != null
&& (cfgManager.isDataPublic () ||
collection.getAuthorizedUsers ().contains (u)))
{
return collection;
}
}
return null;
}
/**
* Retrieves all products contained in a collection.
* @return a set of products.
*/
@Transactional(readOnly = true)
public Set<Product> getAllProductInCollection ()
{
Set<Product> products = new HashSet<> ();
Iterator<Collection> it = collectionDao.getAllCollections ();
while (it.hasNext ())
{
products.addAll (it.next ().getProducts ());
}
return products;
}
public Collection systemGetCollection (String id)
{
return collectionDao.read (id);
}
}