/* * Copyright 2012 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.artificer.atom; import org.artificer.common.ArtifactType; import org.artificer.common.ArtificerConstants; import org.artificer.common.MediaType; import org.artificer.common.ontology.ArtificerOntology; import org.artificer.common.query.ArtifactSummary; import org.jboss.downloads.artificer._2013.auditing.AuditEntry; import org.jboss.resteasy.plugins.providers.atom.Category; import org.jboss.resteasy.plugins.providers.atom.Content; import org.jboss.resteasy.plugins.providers.atom.Entry; import org.jboss.resteasy.plugins.providers.atom.Link; import org.jboss.resteasy.plugins.providers.atom.Person; import org.jboss.resteasy.plugins.providers.atom.Source; import org.jboss.resteasy.plugins.providers.jaxb.JAXBContextFinder; import org.jboss.resteasy.plugins.providers.jaxb.XmlJAXBContextFinder; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.Artifact; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactEnum; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.ExtendedArtifactType; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.ExtendedDocument; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.StoredQuery; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.StoredQueryData; import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.XmlDocument; import org.w3._1999._02._22_rdf_syntax_ns_.RDF; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.bind.JAXBException; import javax.xml.namespace.QName; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.util.List; /** * Some useful static utils for users of the s-ramp client. * * @author eric.wittmann@redhat.com * @author Brett Meyer */ public final class ArtificerAtomUtils { private static final XmlJAXBContextFinder finder = new XmlJAXBContextFinder(); /** * Private constructor. */ private ArtificerAtomUtils() { } /** * Unwraps the specific {@link BaseArtifactType} from the S-RAMP Artifact wrapper * element. This method requires the artifact's type. * @param artifactType the s-ramp artifact type * @param artifact the s-ramp wrapper {@link Artifact} * @return a {@link BaseArtifactType} */ public static BaseArtifactType unwrapSrampArtifact(ArtifactType artifactType, Artifact artifact) { return artifactType.unwrap(artifact); } /** * Unwraps a specific {@link BaseArtifactType} from the Atom {@link Entry} containing it. This * method grabs the {@link Artifact} child from the Atom {@link Entry} and then unwraps the * {@link BaseArtifactType} from that. * @param entry an Atom {@link Entry} * @return a {@link BaseArtifactType} */ public static BaseArtifactType unwrapSrampArtifact(Entry entry) { ArtifactType artifactType = getArtifactType(entry); return unwrapSrampArtifact(artifactType, entry); } /** * Unwraps a specific {@link BaseArtifactType} from the Atom {@link Entry} containing it. This * method grabs the {@link Artifact} child from the Atom {@link Entry} and then unwraps the * {@link BaseArtifactType} from that. * @param artifactType the s-ramp artifact type * @param entry an Atom {@link Entry} * @return a {@link BaseArtifactType} */ public static BaseArtifactType unwrapSrampArtifact(ArtifactType artifactType, Entry entry) { try { Artifact artifact = getArtifactWrapper(entry); if (artifact != null) { // Don't trust the extended types - there is some ambiguity there. if (artifactType.isExtendedType()) { artifactType = disambiguateExtendedType(entry, artifactType); } return unwrapSrampArtifact(artifactType, artifact); } else { return null; } } catch (JAXBException e) { // This is unlikely to happen. throw new RuntimeException(e); } } /** * Gets the {@link Artifact} jaxb object from the {@link Entry}. * @param entry * @throws JAXBException */ private static Artifact getArtifactWrapper(Entry entry) throws JAXBException { return unwrap(entry, Artifact.class); } /** * Wraps the given s-ramp artifact in an Atom {@link Entry}. * @param artifact * @throws URISyntaxException * @throws InvocationTargetException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws NoSuchMethodException * @throws SecurityException */ public static Entry wrapSrampArtifact(BaseArtifactType artifact) throws URISyntaxException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException { // TODO leverage the artifact->entry visitors here Entry entry = new Entry(); if (artifact.getUuid() != null) entry.setId(new URI("urn:uuid:" + artifact.getUuid())); if (artifact.getLastModifiedTimestamp() != null) entry.setUpdated(artifact.getLastModifiedTimestamp().toGregorianCalendar().getTime()); if (artifact.getName() != null) entry.setTitle(artifact.getName()); if (artifact.getCreatedTimestamp() != null) entry.setPublished(artifact.getCreatedTimestamp().toGregorianCalendar().getTime()); if (artifact.getCreatedBy() != null) entry.getAuthors().add(new Person(artifact.getCreatedBy())); if (artifact.getDescription() != null) entry.setSummary(artifact.getDescription()); if (artifact instanceof ExtendedArtifactType) { entry.getExtensionAttributes().put(ArtificerConstants.SRAMP_EXTENDED_TYPE_QNAME, ((ExtendedArtifactType) artifact).getExtendedType()); } if (artifact instanceof ExtendedDocument) { entry.getExtensionAttributes().put(ArtificerConstants.SRAMP_EXTENDED_TYPE_QNAME, ((ExtendedDocument) artifact).getExtendedType()); } Artifact srampArty = new Artifact(); Method method = Artifact.class.getMethod("set" + artifact.getClass().getSimpleName(), artifact.getClass()); method.invoke(srampArty, artifact); entry.setAnyOtherJAXBObject(srampArty); return entry; } public static Entry wrapArtifactSummary(ArtifactSummary artifact) throws URISyntaxException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException { Entry entry = new Entry(); if (artifact.getUuid() != null) entry.setId(new URI("urn:uuid:" + artifact.getUuid())); if (artifact.getLastModifiedTimestamp() != null) entry.setUpdated(artifact.getLastModifiedTimestamp().getTime()); if (artifact.getName() != null) entry.setTitle(artifact.getName()); if (artifact.getCreatedTimestamp() != null) entry.setPublished(artifact.getCreatedTimestamp().getTime()); if (artifact.getCreatedBy() != null) entry.getAuthors().add(new Person(artifact.getCreatedBy())); if (artifact.getDescription() != null) entry.setSummary(artifact.getDescription()); Category category = new Category(); category.setScheme(ArtificerAtomConstants.X_S_RAMP_TYPE_URN); category.setTerm(artifact.getType()); entry.getCategories().add(category); entry.getExtensionAttributes().put(ArtificerConstants.SRAMP_DERIVED_QNAME, String.valueOf(artifact.isDerived())); entry.getExtensionAttributes().put(ArtificerConstants.ARTIFICER_EXPANDED_FROM_ARCHIVE_QNAME, String.valueOf(artifact.isExpandedFromArchive())); return entry; } public static Entry wrapStoredQuery(StoredQuery storedQuery) throws Exception { Entry entry = new Entry(); entry.setId(new URI("urn:uuid:" + storedQuery.getQueryName())); entry.setTitle("Stored Query: " + storedQuery.getQueryName()); // TODO: This is really stupid. Going to push back on this w/ the TC. The Atom binding spec should simply // reuse the core model's StoredQuery. StoredQueryData data = new StoredQueryData(); data.setQueryName(storedQuery.getQueryName()); data.setQueryString(storedQuery.getQueryExpression()); data.getPropertyName().addAll(storedQuery.getPropertyName()); entry.setAnyOtherJAXBObject(data); Content content = new Content(); content.setText("Stored Query Entry"); entry.setContent(content); Category category = new Category(); category.setTerm("query"); category.setLabel("Stored Query Entry"); category.setScheme(ArtificerAtomConstants.X_S_RAMP_TYPE_URN); entry.getCategories().add(category); return entry; } public static Entry wrapOntology(ArtificerOntology ontology, RDF rdf) throws Exception { Entry entry = new Entry(); entry.setId(new URI("urn:uuid:" + ontology.getUuid())); entry.setPublished(ontology.getCreatedOn()); entry.setUpdated(ontology.getLastModifiedOn()); entry.getAuthors().add(new Person(ontology.getCreatedBy())); entry.setTitle(ontology.getLabel()); entry.setSummary(ontology.getAnnotation()); Source source = new Source(); source.setBase(new URI(ontology.getBase())); source.setId(new URI(ontology.getId())); entry.setSource(source); if (rdf != null) { entry.setAnyOtherJAXBObject(rdf); } Content content = new Content(); content.setText("Classification Entry"); entry.setContent(content); Category category = new Category(); category.setTerm("classification"); category.setLabel("Classification Entry"); category.setScheme(ArtificerAtomConstants.X_S_RAMP_TYPE_URN); entry.getCategories().add(category); return entry; } /** * Figures out the S-RAMP artifact type for the given {@link Entry}. * * @param entry */ public static ArtifactType getArtifactType(Entry entry) { ArtifactType type = getArtifactTypeFromEntry(entry); // TODO: This whole if block seems wrong. 'type' will already have the extended type set from getArtifactTypeFromEntry... if (type.isExtendedType()) { boolean derived = "true".equals(entry.getExtensionAttributes().get(ArtificerConstants.SRAMP_DERIVED_QNAME)); // String extendedType = (String) entry.getExtensionAttributes().get(ArtificerConstants.SRAMP_EXTENDED_TYPE_QNAME); type.setExtendedDerivedType(derived); // type.setExtendedType(extendedType); } return type; } /** * Figures out the S-RAMP artifact type for the given {@link Entry}. There are a number of * ways we can do this. We'll try them all: * * 1) check the 'self' link, parsing it for the type and model information * 2) check the Atom Category - there should be one for the artifact type * 3) unwrap the Entry's {@link Artifact} and get the artifactType value (xml attribute) * * @param entry */ protected static ArtifactType getArtifactTypeFromEntry(Entry entry) { // Take a look at what's in the JAXB artifact wrapper (if one exists). try { Artifact artifactWrapper = getArtifactWrapper(entry); if (artifactWrapper != null) { String hint = null; Element wrapperNode = getArtifactWrapperNode(entry); if (wrapperNode != null) { hint = getArtifactWrappedElementName(wrapperNode); } return ArtifactType.valueOf(artifactWrapper, hint); } } catch (JAXBException e) { } // Try the Category List<Category> categories = entry.getCategories(); for (Category cat : categories) { if (ArtificerAtomConstants.X_S_RAMP_TYPE.equals(cat.getScheme().toString())) { String atype = cat.getTerm(); ArtifactType artifactType = ArtifactType.valueOf(atype); if (artifactType.isExtendedType()) { artifactType = disambiguateExtendedType(entry, artifactType); } return artifactType; } } // Check the 'self' link Link link = entry.getLinkByRel("self"); if (link != null) { URI href = link.getHref(); String path = href.getPath(); String [] split = path.split("/"); String atype = split[split.length - 2]; //String amodel = split[split.length - 3]; ArtifactType artifactType = ArtifactType.valueOf(atype); if (artifactType.isExtendedType()) { artifactType = disambiguateExtendedType(entry, artifactType); } return artifactType; } // If all else fails! return ArtifactType.valueOf("Document"); } /** * Attempts to figure out whether we're dealing with an {@link ExtendedArtifactType} or a * {@link ExtendedDocument} by looking for clues in the {@link Entry}. * @param entry * @param artifactType */ protected static ArtifactType disambiguateExtendedType(Entry entry, ArtifactType artifactType) { String et = artifactType.getExtendedType(); boolean convertToDocument = false; // Dis-ambiguate the extended types (or try to) if (entry.getContent() != null) { convertToDocument = true; } else { Element node = getArtifactWrapperNode(entry); if (node != null) { String type = getArtifactWrappedElementName(node); if (ExtendedDocument.class.getSimpleName().equals(type)) { convertToDocument = true; } } } if (convertToDocument) { artifactType = ArtifactType.valueOf(BaseArtifactEnum.EXTENDED_DOCUMENT); artifactType.setExtendedType(et); } return artifactType; } /** * Unwrap some other jaxb object from its Atom Entry wrapper. * @param entry * @param clazz * @throws JAXBException */ @SuppressWarnings("unchecked") public static <T> T unwrap(Entry entry, Class<T> clazz) throws JAXBException { setFinder(entry); if (entry.getAnyOtherElement() == null && entry.getAnyOther().isEmpty()) return null; T object = entry.getAnyOtherJAXBObject(clazz); if (object == null) { for (Object anyOther : entry.getAnyOther()) { if (anyOther != null && anyOther.getClass().equals(clazz)) { object = (T) anyOther; break; } } } return object; } /** * TODO: remove this once we fix RestEasy to set the finder automatically on the Entry during unmarshaling * @param entry */ private static void setFinder(Entry entry) { // Eat any exception we might encounter - if this fails it'll just revert to creating // a new JAXBContext each time. This is slow but works. try { Method method = entry.getClass().getDeclaredMethod("setFinder", JAXBContextFinder.class); method.setAccessible(true); method.invoke(entry, finder); } catch (SecurityException e) { } catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } catch (NoSuchMethodException e) { } } /** * Gets the XML node for the wrapper 'artifact' element from the {@link Entry}. * @param entry */ protected static Element getArtifactWrapperNode(Entry entry) { Element element = entry.getAnyOtherElement(); if (isWrapperElement(element)) { return element; } for (Object anyOther : entry.getAnyOther()) { if (anyOther instanceof Element && isWrapperElement((Element) anyOther)) { return (Element) anyOther; } } return null; } /** * @param artifactWrapperElement * @return the local name of the artifact that is wrapped by the {@link Artifact} wrapper */ protected static String getArtifactWrappedElementName(Element artifactWrapperElement) { NodeList nodes = artifactWrapperElement.getChildNodes(); for (int i = 0; i < nodes.getLength(); i++) { Node item = nodes.item(i); if (item.getNodeType() == Node.ELEMENT_NODE) { return item.getLocalName(); } } return null; } /** * @param element * @return true if the {@link Element} is the artifact wrapper element */ private static boolean isWrapperElement(Element element) { if (element == null) return false; QName qname = new QName(element.getNamespaceURI(), element.getLocalName()); return qname.equals(ArtificerConstants.S_RAMP_WRAPPER_ELEM); } /** * Unwraps the Ontology from the Atom Entry. * @param entry */ public static RDF unwrapRDF(Entry entry) { try { return unwrap(entry, RDF.class); } catch (JAXBException e) { throw new RuntimeException(e); } } /** * Unwraps the audit entry from the Atom Entry. * @param entry */ public static AuditEntry unwrapAuditEntry(Entry entry) { try { return unwrap(entry, AuditEntry.class); } catch (JAXBException e) { throw new RuntimeException(e); } } /** * Unwraps the stored query entry from the Atom Entry. * @param entry */ public static StoredQuery unwrapStoredQuery(Entry entry) { try { // TODO: Again, this is stupid. StoredQuery and StoredQueryData should be combined. StoredQueryData data = unwrap(entry, StoredQueryData.class); StoredQuery storedQuery = new StoredQuery(); storedQuery.setQueryExpression(data.getQueryString()); storedQuery.setQueryName(data.getQueryName()); storedQuery.getPropertyName().addAll(data.getPropertyName()); return storedQuery; } catch (JAXBException e) { throw new RuntimeException(e); } } public static void setXmlContentType(BaseArtifactType artifact, String src, Entry entry) { if (artifact instanceof XmlDocument) { XmlDocument xmlDocument = (XmlDocument) artifact; Content content = new Content(); // Required by spec. #getContentEncoding guaranteed to be set by XmlArtifactBuilder content.setRawType(MediaType.APPLICATION_XML_TYPE + "; charset=" + xmlDocument.getContentEncoding()); content.setSrc(URI.create(src)); entry.setContent(content); } } }