You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gcube-cms-suite/geoportal-service/src/main/java/org/gcube/application/geoportal/service/engine/mongo/ProfiledMongoManager.java

360 lines
15 KiB
Java

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

package org.gcube.application.geoportal.service.engine.mongo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.mongodb.client.MongoDatabase;
import com.vdurmont.semver4j.Semver;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.gcube.application.cms.plugins.LifecycleManager;
import org.gcube.application.cms.plugins.faults.StepException;
import org.gcube.application.cms.plugins.model.PluginDescriptor;
import org.gcube.application.cms.plugins.reports.ExecutionReport;
import org.gcube.application.cms.plugins.requests.StepExecutionRequest;
import org.gcube.application.geoportal.common.faults.StorageException;
import org.gcube.application.geoportal.common.model.document.*;
import org.gcube.application.geoportal.common.model.legacy.Concessione;
import org.gcube.application.geoportal.common.model.legacy.WorkspaceContent;
import org.gcube.application.geoportal.common.model.profile.Field;
import org.gcube.application.geoportal.common.model.profile.HandlerDeclaration;
import org.gcube.application.geoportal.common.model.profile.Profile;
import org.gcube.application.geoportal.common.model.rest.QueryRequest;
import org.gcube.application.geoportal.common.rest.TempFile;
import org.gcube.application.geoportal.common.utils.JSONPathWrapper;
import org.gcube.application.geoportal.common.utils.StorageUtils;
import org.gcube.application.geoportal.service.engine.ImplementationProvider;
import org.gcube.application.geoportal.service.engine.WorkspaceManager;
import org.gcube.application.geoportal.service.model.internal.faults.ConfigurationException;
import org.gcube.application.geoportal.service.model.internal.faults.DeletionException;
import org.gcube.application.cms.Serialization;
import org.gcube.application.geoportal.service.utils.UserUtils;
import org.gcube.common.storagehub.client.dsl.FolderContainer;
import org.gcube.common.storagehub.model.exceptions.StorageHubException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Consumer;
import static org.gcube.application.cms.Serialization.*;
@Slf4j
public class ProfiledMongoManager extends MongoManager implements MongoManagerI<ProfiledDocument>{
@Getter
Profile profile;
MongoDatabase db=null;
public ProfiledMongoManager(String profileId) throws ConfigurationException {
// Check Profile ID
log.info("Loading profile ID {} ",profileId);
if(profileId==null) throw new InvalidParameterException("Profile ID cannot be null");
Map<String,Profile> profiles=ImplementationProvider.get().getProfiles().getObject();
if(!profiles.containsKey(profileId)) {
log.debug("Asked profile {} not found. Available ones are {} ",profileId,profiles.keySet());
throw new WebApplicationException("Profile " + profileId + " not registered", Response.Status.NOT_FOUND);
}
profile=profiles.get(profileId);
log.debug("Loaded Profile {} ",profile);
// Connect to DB
String toUseDB=super.client.getConnection().getDatabase();
log.info("Connecting to DB {} ",toUseDB);
// TODO MAP OF DATABASES?
db=client.getTheClient().getDatabase(toUseDB);
}
@Getter(lazy = true)
private final LifecycleManager manager=getLCManager();
private LifecycleManager getLCManager() {
try{
LifecycleManager toReturn=null;
//Getting Lifecycle Manager declaration from Profile
List<HandlerDeclaration> handlerDeclarations= profile.getHandlersMap().get(PluginDescriptor.BaseTypes.LIFECYCLE_MANAGER);
if(handlerDeclarations==null || handlerDeclarations.isEmpty()) throw new ConfigurationException("No Lifecycle Handler defined for profile ID "+profile.getId());
if(handlerDeclarations.size()>1) throw new ConfigurationException("Too many Lifecycle Handlers defined ("+handlerDeclarations+") in profile ID "+profile.getId());
HandlerDeclaration lcHandlerDeclaration=handlerDeclarations.get(0);
// Loading Lifecycle Manager
log.debug("Looking for handler {} ",lcHandlerDeclaration);
toReturn=(LifecycleManager) ImplementationProvider.get().getPluginManager().getObject().get(lcHandlerDeclaration.getId());
if(toReturn==null) throw new ConfigurationException("Unable to find Lifecycle Manager Plugin. ID "+lcHandlerDeclaration.getId());
return toReturn;
} catch(Throwable t){
log.warn("Unable to load LC Manager ",t);
return null;
}
}
private String getCollectionName(){
// TODO Profile can directly specify, use ID only as default
return profile.getId();
}
@Override
public MongoDatabase getDatabase(){
return db;
}
@Override
public ProfiledDocument registerNew(Document toRegisterDoc) throws IOException, StepException {
log.info("Registering new document in {} ",profile.getId());
log.debug("Going to register {}",toRegisterDoc.toJson());
ProfiledDocument toRegister = new ProfiledDocument();
toRegister.setTheDocument(toRegisterDoc);
PublicationInfo pubInfo=new PublicationInfo();
pubInfo.setCreationInfo(UserUtils.getCurrent().asInfo());
// TODO Set Access From Profile
Access access=new Access();
access.setLicense("");
access.setPolicy(AccessPolicy.OPEN);
pubInfo.setAccess(access);
toRegister.setInfo(pubInfo);
toRegister.setProfileID(profile.getId());
toRegister.setProfileVersion(profile.getVersion());
toRegister.setVersion(new Semver("1.0.0"));
// Apply Lifecycle
toRegister=step(toRegister,StepExecutionRequest.Steps.ON_INIT_DOCUMENT,null).getResult();
log.debug("Going to register {} ",toRegister);
// Insert object
ObjectId id =insert(asDocument(toRegister),getCollectionName());
log.info("Obtained id {} ",id);
return getByID(id.toHexString());
}
@Override
public ProfiledDocument update(String id, Document toSet) throws IOException, StepException {
log.trace("Replacing {} ",toSet);
ProfiledDocument toUpdate=getByID(id);
toUpdate.setTheDocument(toSet);
toUpdate=onUpdate(toUpdate);
return convert(replace(asDocument(toUpdate),getCollectionName()),ProfiledDocument.class);
}
private ProfiledDocument onUpdate(ProfiledDocument toUpdate) throws StepException {
UserUtils.AuthenticatedUser u = UserUtils.getCurrent();
toUpdate.getInfo().setLastEditInfo(u.asInfo());
toUpdate.setVersion(toUpdate.getVersion().withIncPatch());
return step(toUpdate,StepExecutionRequest.Steps.ON_UPDATE_DOCUMENT,null).getResult();
}
@Override
public void delete(String id,boolean force) throws DeletionException {
log.debug("Deleting by ID {}, force {}",id,force);
try{
ProfiledDocument doc =getByID(id);
// TODO INVOKE LIFECYCLE
//if(!force&&isPublished(id)) throw new Exception("Cannot delete published documents. Unpublish it or use force = true");
try{
// TODO CHECK PHASE AND STATUS
// DEINDEX
// DEMATERIALIZE
// DELETE CONTENT
// DELETE ENTRY
throw new DeletionException("IMPLEMENT THIS");
// delete(asId(id), getCollectionName());
}catch(DeletionException e) {
//storing updated - partially deleted
// concessione=onUpdate(concessione);
// replace(asDocument(concessione), collectionName);
throw e;
}
}catch(Throwable t){
throw new DeletionException("Unable to delete "+id,t);
}
}
@Override
public ProfiledDocument getByID(String id){
Document doc=super.getById(asId(id),getCollectionName());
if(doc==null) throw new WebApplicationException("No document with ID "+id);
return convert(doc,ProfiledDocument.class);
}
@Override
public Iterable<Document> query(QueryRequest queryRequest) {
log.info("Querying {} ",queryRequest);
LinkedBlockingQueue queue=new LinkedBlockingQueue<Concessione>();
query(queryRequest,getCollectionName()).forEach(
(Consumer<? super Document>) (Document d)->{try{
queue.put(d);
}catch(Throwable t){log.warn("Unable to translate "+d);}});
log.info("Returned {} elements ",queue.size());
return queue;
}
@Override
public Iterable<ProfiledDocument> filter(QueryRequest queryRequest) {
log.info("Searching concessione for filter {} ",queryRequest);
LinkedBlockingQueue queue=new LinkedBlockingQueue<Concessione>();
query(queryRequest,getCollectionName()).forEach(
(Consumer<? super Document>) (Document d)->{try{
queue.put(d);
}catch(Throwable t){log.warn("Unable to translate "+d);}});
log.info("Returned {} elements ",queue.size());
return queue;
}
@Override
public ProfiledDocument performStep(String id, String step, Document options) throws StepException, JsonProcessingException {
ExecutionReport report = step(getByID(id), step, options);
return convert(replace(asDocument(report.getResult()),getCollectionName()),ProfiledDocument.class);
}
/**
* NB Put at path :
*
*
*
*/
@Override
public ProfiledDocument registerFileSet(String id, String destination, Document attributes, List<TempFile> files) throws ConfigurationException, StorageHubException, StorageException, StepException, JsonProcessingException {
log.info("Registering fileset [size : {}] for {} at {} with options {}",files.size(),id,destination,attributes);
ProfiledDocument doc=getByID(id);
WorkspaceManager ws=new WorkspaceManager();
StorageUtils storage=ImplementationProvider.get().getStorageProvider().getObject();
log.debug("Checking {}  path against profile {}",destination,profile.getId());
JSONPathWrapper schemaWrapper= new JSONPathWrapper(profile.getSchema().toJson());
List<Object> fieldDefinitions=schemaWrapper.getByPath(destination);
if(fieldDefinitions==null || fieldDefinitions.isEmpty()) throw new WebApplicationException("No Field found in schema "+profile.getId()+" at "+destination, Response.Status.BAD_REQUEST);
if(fieldDefinitions.size()>1) throw new WebApplicationException("Multiple field definitions ("+fieldDefinitions.size()+") found in "+profile.getId()+" for "+destination,Response.Status.BAD_REQUEST);
Field fieldDefinition=Serialization.convert(fieldDefinitions.get(0),Field.class);
log.debug("Field definition is {}",fieldDefinition);
JSONPathWrapper docWrapper=new JSONPathWrapper(doc.getTheDocument().toJson());
List<RegisteredFileSet> found=docWrapper.getByPath(destination,RegisteredFileSet.class);
if(fieldDefinition.getMaxCardinality()==1 && (!found.isEmpty())){
throw new WebApplicationException("Cannot add registered fileset at "+destination+" : field is not collection.",Response.Status.BAD_REQUEST);
}
RegisteredFileSet registeredFileSet=prepareRegisteredFileSet(doc,profile,destination,attributes,files,storage,ws);
log.debug("Registered fileset is {} ",registeredFileSet);
if(fieldDefinition.getMaxCardinality()>1){
// Field is collection
found.add(registeredFileSet);
docWrapper.set(destination,found);
}
else {
docWrapper.set(destination,registeredFileSet);
}
log.debug("Setting result on profiled document");
doc.setTheDocument(Document.parse(docWrapper.getCtx().jsonString()));
doc=onUpdate(doc);
return convert(replace(asDocument(doc),getCollectionName()),ProfiledDocument.class);
}
@Override
public ProfiledDocument deleteFileSet(String id, String destination, Boolean force) throws ConfigurationException, StorageHubException, StorageException, StepException, JsonProcessingException {
throw new RuntimeException("Implement this");
}
// @Override
// public ProfiledDocument deleteRegisteredFileSet(String id, String destination, List<TempFile> files) {
// throw new RuntimeException("TO IMPLEMENT");
// }
private ExecutionReport step(ProfiledDocument theDocument,String step,Document callParameters) throws StepException {
log.info("[Profile {} ] Invoking Step {} on {}" ,profile.getId(),step,getManager().getDescriptor());
StepExecutionRequest request=new StepExecutionRequest();
request.setCallParameters(callParameters);
request.setDocument(theDocument);
request.setProfile(profile);
request.setStep(step);
log.debug("Requesting Step Execution {} ",request);
ExecutionReport report= getManager().performStep(request);
log.debug("Report is {}",report);
if(report.getResult()==null) throw new StepException("Report result is null");
return report;
}
private static final RegisteredFileSet prepareRegisteredFileSet(ProfiledDocument doc, Profile profile,String destination,
Document attributes,List<TempFile> files, StorageUtils storage,WorkspaceManager ws) throws StorageHubException, StorageException {
log.debug("Preparing Registered FileSet..");
attributes.putIfAbsent(RegisteredFileSet.CREATION_INFO,UserUtils.getCurrent().asInfo());
attributes.putIfAbsent(RegisteredFileSet.ACCESS,doc.getInfo().getAccess());
FolderContainer base=ws.createFolder(new WorkspaceManager.FolderOptions(
doc.get_id(),"Base Folder for profiled document. Profile "+profile.getId(),null));
FolderContainer sectionFolder=ws.createFolder(new WorkspaceManager.FolderOptions(
doc.get_id()+destination,"Registered Fileset at path "+destination,base));
attributes.putIfAbsent(RegisteredFileSet.FOLDER_ID,sectionFolder.getId());
ArrayList<RegisteredFile> registeredFiles=new ArrayList<>();
for (TempFile f : files) {
InputStream is=null;
try{
log.debug("Opening temp file {}",f);
String fileUrl=storage.getURL(f.getId());
log.debug("Got URL {} from ID {}",fileUrl,f.getId());
is=new URL(fileUrl).openStream();
RegisteredFile registered=ws.registerFile(new WorkspaceManager.FileOptions(f.getFilename(),is,
"Imported via gcube CMS service ", sectionFolder));
log.debug("Registered "+registered);
registeredFiles.add(registered);
}catch(StorageHubException | IOException e){
throw new StorageException("Unable to store "+f,e);
}finally{
if(is!=null)
IOUtils.closeQuietly(is);
}
}
attributes.putIfAbsent(RegisteredFileSet.PAYLOADS,registeredFiles);
return Serialization.convert(attributes,RegisteredFileSet.class);
}
}