package org.gcube.application.geoportal.service.engine.providers.ucd; import lombok.*; import lombok.extern.slf4j.Slf4j; import org.gcube.application.cms.caches.AbstractScopedMap; import org.gcube.application.cms.caches.ObjectManager; import org.gcube.application.cms.implementations.ISInterface; import org.gcube.application.cms.implementations.ImplementationProvider; import org.gcube.application.cms.serialization.Serialization; import org.gcube.application.geoportal.common.model.document.filesets.RegisteredFile; import org.gcube.application.geoportal.common.model.rest.ConfigurationException; import org.gcube.application.geoportal.common.model.useCaseDescriptor.UseCaseDescriptor; import org.gcube.application.cms.implementations.WorkspaceManager; import org.gcube.common.resources.gcore.GenericResource; import org.gcube.common.storagehub.client.dsl.FolderContainer; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.*; import java.io.ByteArrayInputStream; import java.io.StringReader; import java.io.StringWriter; import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Scanner; /** * Retrieves available UCDs from a single Generic Resource * - secondary Type : "CMS" * - name : "UCDs" * * containing e.g. * * * * * * */ @Slf4j public class SingleISResourceUCDProvider extends AbstractScopedMap implements ObjectManager { private static final String GR_SECONDARY_TYPE="CMS"; private static final String GR_NAME="UCDs"; public SingleISResourceUCDProvider() { super("Single IS Resource UCD Provider"); setTTL(Duration.of(1, ChronoUnit.MICROS)); // no cache } @Override public UseCaseDescriptor insert(UseCaseDescriptor object) throws ConfigurationException{ try{ log.info("Registering UCID {}",object.getId()); log.debug("Full UCD {} ",object); // Register entity in WS WorkspaceManager manager =new WorkspaceManager(); WorkspaceManager.FolderOptions fo=new WorkspaceManager.FolderOptions("UCDs"); fo.setParent(manager.getAppBase()); fo.setFolderDescription("UCDs registered from server"); FolderContainer folder = manager.createFolder(fo); String jsonString = Serialization.write(object); WorkspaceManager.FileOptions fileOptions=new WorkspaceManager.FileOptions(object.getId(),new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8))); fileOptions.setFileDescription(object.getName()+", ID : "+object.getId()); fileOptions.setParent(folder); RegisteredFile f = manager.registerFile(fileOptions); log.info("Registered File {}",f); // Create / Update GR ISInterface is = ImplementationProvider.get().getProvidedObjectByClass(ISInterface.class); List l = is.getGenericResource(GR_SECONDARY_TYPE,GR_NAME); log.debug("Found {} resources ",l.size()); GenericResource toupdate = null; ISBean bean = new ISBean(); // If GR present, then use it try{ toupdate = l.get(0); log.debug("GR IS {}",toupdate); bean = read(toupdate.profile().bodyAsString()); }catch (Throwable t){ log.warn("Unable to read from selected GR, creating it..",t); toupdate = new GenericResource(); toupdate.newProfile().name(GR_NAME).type(GR_SECONDARY_TYPE). description("Generated by service at "+ LocalDateTime.now()); } if(bean.getRecords()==null) bean.setRecords(new ArrayList<>()); ISBean.Record record = new ISBean.Record(); record.setUcdUrl(f.getLink()); record.setLabel(object.getName()); bean.getRecords().add(record); log.debug("Inserting record {} ",record); String xml = write(bean).replaceFirst("<\\?.*\\?>",""); //remove processing instructions toupdate.profile().newBody(xml); log.info("Creating / Updating GR {} ",toupdate); is.createUpdateGR(toupdate); }catch(ConfigurationException e){ throw e; } catch (Throwable e) { throw new ConfigurationException(e); } return object; } @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "UCDs") @Data @EqualsAndHashCode public static class ISBean { @NoArgsConstructor @ToString @Getter @Setter @AllArgsConstructor @XmlAccessorType(XmlAccessType.FIELD) @EqualsAndHashCode public static class Record { @XmlAttribute private String label; @XmlAttribute private String ucdUrl; @XmlAttribute private String defaultForRegex; } @XmlElement(name = "record") private List records; } @Override public void init() { super.init(); try{ directInit(); }catch(Throwable t){ throw new RuntimeException("Unable to initialize. Can't read Use case descriptors ",t); } } static void directInit() throws JAXBException { jaxbContext = JAXBContext.newInstance(ISBean.class); marshaller = jaxbContext.createMarshaller(); unmarshaller = jaxbContext.createUnmarshaller(); } private static JAXBContext jaxbContext=null; private static Marshaller marshaller=null; private static Unmarshaller unmarshaller = null; static ISBean read(String xml) throws JAXBException { log.trace("Loading from xml {}",xml); if(xml == null || xml.isEmpty()) return new ISBean(); return (ISBean) unmarshaller.unmarshal(new StringReader(xml)); } static String write(ISBean obj) throws JAXBException { log.trace("Writing {} to xml",obj); StringWriter writer = new StringWriter(); marshaller.marshal(obj,writer); writer.flush(); return writer.toString(); } @Override protected ProfileMap retrieveObject(String key) throws ConfigurationException { try { log.info("Loading UCDs for context {} ",key); ISInterface is = ImplementationProvider.get().getProvidedObjectByClass(ISInterface.class); List l = is.getGenericResource(GR_SECONDARY_TYPE,GR_NAME); log.debug("Found {} resources ",l.size()); ProfileMap toReturn = new ProfileMap(); l.forEach(g->{ try { log.debug("Reading from ID {} ", g.id()); ISBean bean = read(g.profile().bodyAsString()); log.debug("Found {} records ", bean.getRecords().size()); int before = toReturn.size(); bean.getRecords().forEach(record -> { try { log.debug("Loading UCD From {} ", record); String json = new Scanner( new URL(record.getUcdUrl()).openStream(), "UTF-8").useDelimiter("\\A").next(); log.trace("JSON IS {} ",json); UseCaseDescriptor ucd = Serialization.read(json, UseCaseDescriptor.class); if(toReturn.containsKey(ucd.getId())) log.warn("DUPLICATE UCID found {} in resource {} ",ucd.getId(),g.id()); toReturn.put(ucd.getId(), ucd); }catch (Throwable t){ log.warn("Unable to read record {} from GR ID {} ",record,g.id(),t); } }); log.debug("Loaded {} from {} ",toReturn.size()-before,g.id()); }catch (Throwable t){ log.warn("Unable to read from GR {} ",g.id(),t); } }); return toReturn; }catch(Throwable t){ throw new ConfigurationException("Unable to load UCDs for "+key,t); } } }