gFeed/gCat-Controller/src/main/java/org/gcube/data/publishing/gCataFeeder/catalogues/gCat/GCatController.java

372 lines
11 KiB
Java

package org.gcube.data.publishing.gCataFeeder.catalogues.gCat;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import javax.ws.rs.WebApplicationException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
import org.gcube.data.publishing.gCatFeeder.catalogues.CatalogueController;
import org.gcube.data.publishing.gCatFeeder.catalogues.model.PublishReport;
import org.gcube.data.publishing.gCatFeeder.catalogues.model.faults.CatalogueInteractionException;
import org.gcube.data.publishing.gCatFeeder.catalogues.model.faults.ControllerInstantiationFault;
import org.gcube.data.publishing.gCatFeeder.catalogues.model.faults.PublicationException;
import org.gcube.data.publishing.gCatFeeder.catalogues.model.faults.WrongObjectFormatException;
import org.gcube.data.publishing.gCatFeeder.model.CatalogueFormatData;
import org.gcube.data.publishing.gCatFeeder.model.CatalogueInstanceDescriptor;
import org.gcube.data.publishing.gCatFeeder.model.ControllerConfiguration;
import org.gcube.data.publishing.gCatFeeder.model.ControllerConfiguration.PublishingPolicy;
import org.gcube.gcat.client.Item;
import org.gcube.gcat.client.Profile;
import org.gcube.gcat.client.Resource;
import org.w3c.dom.Document;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class GCatController implements CatalogueController{
private static ObjectMapper mapper;
private static DocumentBuilderFactory factory;
private static DocumentBuilder builder;
private static XPathFactory xPathfactory;
private static XPath xpath;
private static XPathExpression expr;
static {
mapper=new ObjectMapper();
factory= DocumentBuilderFactory.newInstance();
xPathfactory = XPathFactory.newInstance();
xpath = xPathfactory.newXPath();
try {
builder= factory.newDocumentBuilder();
expr= xpath.compile("string(/bookstore/book/title/@lang)");
} catch (ParserConfigurationException | XPathExpressionException e) {
throw new RuntimeException("Unable to initialize Controller");
}
}
private CatalogueInstanceDescriptor desc;
private String callerToken=null;
private ControllerConfiguration config;
public GCatController(CatalogueInstanceDescriptor instance) throws ControllerInstantiationFault {
try{
this.desc=instance;
if(isCustomToken())
setCustomToken();
checkInstance();
if(isCustomToken())
resetToken();
}catch(ControllerInstantiationFault e) {
throw e;
}
}
@Override
public void configure(ControllerConfiguration config) {
this.config = config;
}
/**
* Expected structure
*
* {
* "profile" : "<xml_profile>",
* "item" : "<json_item>",
* "resources" : [
* "<json_resource>",
* "<json_resource>",
* ....]
* }
*
* NB serialized resources are updated with "package_id" set as the published item id
*
*
*/
@Override
public PublishReport publishItem(CatalogueFormatData arg0) throws WrongObjectFormatException,CatalogueInteractionException,PublicationException{
log.debug("Publishing {} ",arg0);
String serialized=arg0.toCatalogueFormat();
try {
if(isCustomToken())
setCustomToken();
log.debug("Parsing data..");
JsonNode node=mapper.readTree(serialized);
if(!node.hasNonNull("item")) throw new WrongObjectFormatException("No \"item\" specified in serialized object");
JsonNode itemNode=node.path("item");
if(node.hasNonNull("profile")) {
log.debug("Publishing profile..");
String xmlProfile=node.path("profile").asText();
String profileName=getProfileName(xmlProfile);
createProfile(profileName, xmlProfile);
}
log.debug("Publishing Item..");
String itemResp=createItem(itemNode.asText());
String itemId = getId(itemResp);
log.debug("Pubilshed Item with ID {} ",itemId);
if(node.hasNonNull("resources")) {
log.debug("Pubilshing resources for {} ",itemId);
Iterator<JsonNode> resIterator=node.path("resources").elements();
while(resIterator.hasNext()) {
JsonNode resNode=resIterator.next();
String resourceName=resNode.path("name").asText();
createResource(itemId, resourceName, resNode.asText());
}
}
return new PublishReport(true, itemId);
} catch(WrongObjectFormatException | PublicationException e) {
throw e;
} catch (Throwable e) {
throw new WrongObjectFormatException("Unable to read format ",e);
}finally{
if(isCustomToken())
resetToken();
}
}
private String createProfile(String profileName,String xmlProfile) throws WrongObjectFormatException, PublicationException{
Profile profile=null;
try{
if(isForcedUrl())
profile = new Profile(getForcedUrl());
else profile=new Profile();
// check if exists
boolean exists=false;
try {
profile.read(profileName);
exists=true;
}catch(Throwable t) {
exists=false;
}
switch(getOnClash()) {
case SKIP : return null;
case FAIL : {
if(exists) throw new PublicationException("Profile "+profileName+" already existing");
break;
}
case UPDATE : {
if(exists) return profile.update(profileName, xmlProfile);
break;
}
}
return profile.create(profileName, xmlProfile);
}catch(WebApplicationException e) {
handleWebException(e);
return null;
}catch(MalformedURLException e) {
throw new CatalogueInteractionException(e);
}
}
private String createResource(String itemId,String resourceName,String toCreate) throws WrongObjectFormatException, PublicationException{
Resource resource=null;
try{
if(isForcedUrl())
resource = new Resource(getForcedUrl());
else resource=new Resource();
// Try to create
try {
log.debug("Trying to create resource {} for package {} ",resourceName,itemId);
return resource.create(itemId, toCreate);
}catch(WebApplicationException e) {
// Conflict = resource already existing
if(e.getResponse().getStatus()==409) {
switch(getOnClash()) {
case SKIP : return null;
case FAIL : throw new PublicationException("Resource "+resourceName+" already existing");
case UPDATE : {
// Look for resource id
log.debug("Looking for resource with name {} under item {} ",resourceName,itemId);
String resourceList=resource.list(itemId);
JsonNode listNode=mapper.readTree(resourceList);
Iterator<JsonNode> iterator=listNode.elements();
String resourceId=null;
while(iterator.hasNext()) {
JsonNode res=iterator.next();
if(res.path("name").asText().equals(resourceName)) {
resourceId=res.path("id").asText();
break;
}
}
log.debug("Found id {} for resource Name {} ",resourceId,resourceName);
return resource.update(itemId, resourceId, toCreate);
}
default : return null;
}
}else throw e;
}
}catch(WebApplicationException e) {
handleWebException(e);
return null;
}catch(MalformedURLException e) {
throw new CatalogueInteractionException(e);
}catch(IOException e) {
throw new CatalogueInteractionException(e);
}
}
private String createItem(String toCreate) throws WrongObjectFormatException, PublicationException{
Item item=null;
try{
if(isForcedUrl())
item = new Item(getForcedUrl());
else item=new Item();
return item.create(toCreate);
}catch(WebApplicationException e) {
handleWebException(e);
return null;
}catch(MalformedURLException e) {
throw new CatalogueInteractionException(e);
}
}
// instance utils
private PublishingPolicy getOnClash() {
return config.getOnClash();
}
private boolean isForcedUrl() {
return desc.getCustomToken()==null&&desc.getUrl()!=null;
}
private URL getForcedUrl() throws MalformedURLException {
return new URL(desc.getUrl());
}
private boolean isCustomToken() {
return desc.getCustomToken()!=null;
}
private void setCustomToken() {
callerToken=SecurityTokenProvider.instance.get();
SecurityTokenProvider.instance.set(desc.getCustomToken());
}
private void resetToken() {
SecurityTokenProvider.instance.set(callerToken);
}
private void checkInstance() throws ControllerInstantiationFault{
Item item=null;
try{
if(isForcedUrl())
item=new Item(getForcedUrl());
else item=new Item();
item.list(10, 0);
}catch(Throwable t) {
String msg=String.format("Unable to contact gCat with configuration %1$s ",desc);
log.error(msg,t);
throw new ControllerInstantiationFault(msg,t);
}
}
// Static utils from catalogue-ws
private static String getProfileName(String xmlProfile) throws WrongObjectFormatException {
try{
Document doc = builder.parse(xmlProfile);
return (String) expr.evaluate(doc, XPathConstants.STRING);
}catch(Throwable t) {
throw new WrongObjectFormatException("Unable to parse profile. ",t);
}
}
private static String getId(String publishResponse) {
try {
return mapper.readTree(publishResponse).path("result").path("id").asText();
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException("FAILED Parsing of "+publishResponse);
}
}
private static String getPublishedUrl(String publishResponse) {
try {
Iterator<JsonNode> iterator=mapper.readTree(publishResponse).path("result").path("extras").elements();
while(iterator.hasNext()) {
JsonNode node=iterator.next();
if(node.path("key").asText().equals("Item URL"))
return node.path("value").asText();
}
return "N/A";
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException("FAILED Parsing of "+publishResponse);
}
}
private static void handleWebException(WebApplicationException e) throws WrongObjectFormatException, PublicationException {
log.debug("Received Web Exception {} ",e);
String msg=e.getResponse().readEntity(String.class);
switch(e.getResponse().getStatus()) {
case 400 : throw new WrongObjectFormatException("BAD Request : "+msg,e);
case 401 : throw new CatalogueInteractionException("Unauthorized : "+msg,e);
case 404 : throw new CatalogueInteractionException("NOT FOUND : "+msg,e);
case 405 : throw new CatalogueInteractionException("Method Not Allowed : "+msg,e);
case 409 : throw new CatalogueInteractionException("Conflict : "+msg,e);
case 500 : throw new CatalogueInteractionException("Remote Error : "+msg,e);
default : throw new PublicationException("Unexpected error code : "+msg,e);
}
}
}