diff options
Diffstat (limited to 'winery/org.eclipse.winery.repository.client/src')
7 files changed, 1763 insertions, 0 deletions
diff --git a/winery/org.eclipse.winery.repository.client/src/main/java/META-INF/MANIFEST.MF b/winery/org.eclipse.winery.repository.client/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/winery/org.eclipse.winery.repository.client/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/IWineryRepositoryClient.java b/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/IWineryRepositoryClient.java new file mode 100644 index 0000000..f53aca9 --- /dev/null +++ b/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/IWineryRepositoryClient.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation + *******************************************************************************/ +package org.eclipse.winery.repository.client; + +import org.eclipse.winery.common.interfaces.IWineryRepository; + +public interface IWineryRepositoryClient extends IWineryRepository { + + /** + * Adds an URI to the list of known repositories + * + * SIDE EFFECT: If currently no primary repository is defined, the given + * repository is used as primary repository + * + * @param uri the URI of the repository + */ + void addRepository(String uri); + + /** + * Get the currently defined primary repository + */ + String getPrimaryRepository(); + + /** + * Sets the primary repository + * + * SIDE EFFECT: If the repository is not known as general repository (via + * addRepository), the given repository is added to the list of known + * repositories + * + * @param uri + */ + void setPrimaryRepository(String uri); + + /** + * Checks whether the primary repository is available to be used. Typically, + * this method should return "true". In case of network or server failures, + * the result is "false". Note that the availability may change over time + * and also a repository might become unavailable during querying it. + * + * This method also returns "false" if no primary repository is set. For + * instance, this is the case of no repository is registered at the client. + * + * @return true if the repository is reachable over network, false otherwise + */ + boolean primaryRepositoryAvailable(); +} diff --git a/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/WineryRepositoryClient.java b/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/WineryRepositoryClient.java new file mode 100644 index 0000000..216c623 --- /dev/null +++ b/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/WineryRepositoryClient.java @@ -0,0 +1,746 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation + *******************************************************************************/ +package org.eclipse.winery.repository.client; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; + +import org.eclipse.winery.common.Util; +import org.eclipse.winery.common.beans.NamespaceIdOptionalName; +import org.eclipse.winery.common.constants.MimeTypes; +import org.eclipse.winery.common.ids.GenericId; +import org.eclipse.winery.common.ids.IdUtil; +import org.eclipse.winery.common.ids.definitions.TOSCAComponentId; +import org.eclipse.winery.common.interfaces.QNameAlreadyExistsException; +import org.eclipse.winery.common.interfaces.QNameWithName; +import org.eclipse.winery.common.propertydefinitionkv.WinerysPropertiesDefinition; +import org.eclipse.winery.model.tosca.TDefinitions; +import org.eclipse.winery.model.tosca.TEntityType; +import org.eclipse.winery.model.tosca.TExtensibleElements; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.config.ClientConfig; +import com.sun.jersey.api.client.config.DefaultClientConfig; +import com.sun.jersey.client.urlconnection.HttpURLConnectionFactory; +import com.sun.jersey.client.urlconnection.URLConnectionClientHandler; +import com.sun.jersey.core.util.MultivaluedMapImpl; + +public final class WineryRepositoryClient implements IWineryRepositoryClient { + + private static final Logger logger = LoggerFactory.getLogger(WineryRepositoryClient.class); + + // switch off validation, currently causes more trouble than it brings + private static final boolean VALIDATING = false; + + private final Collection<String> knownURIs = new HashSet<String>(); + private final Collection<WebResource> repositoryResources = new HashSet<WebResource>(); + private final Client client; + private final ObjectMapper mapper = new ObjectMapper(); + + private final Map<Class<? extends TEntityType>, Map<QName, TEntityType>> entityTypeDataCache; + + private final Map<GenericId, String> nameCache; + private static final int MAX_NAME_CACHE_SIZE = 1000; + + private String primaryRepository = null; + private WebResource primaryWebResource = null; + + // thread-safe JAXB as inspired by https://jaxb.java.net/guide/Performance_and_thread_safety.html + // The other possibility: Each subclass sets JAXBContext.newInstance(theSubClass.class); in its static {} part. + // This seems to be more complicated than listing all subclasses in initContext + public final static JAXBContext context = WineryRepositoryClient.initContext(); + + // schema aware document builder + private final DocumentBuilder toscaDocumentBuilder; + + + // taken from http://stackoverflow.com/a/15253142/873282 + private static class ConnectionFactory implements HttpURLConnectionFactory { + + Proxy proxy; + + + private void initializeProxy() { + this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("localhost", 8888)); + } + + @Override + public HttpURLConnection getHttpURLConnection(URL url) throws IOException { + this.initializeProxy(); + return (HttpURLConnection) url.openConnection(this.proxy); + } + } + + + /** + * Creates the client without the use of any proxy + */ + public WineryRepositoryClient() { + this(false); + } + + /** + * @param useProxy if a debugging proxy should be used + * + * @throws IllegalStateException if DOM parser could not be created + */ + public WineryRepositoryClient(boolean useProxy) { + ClientConfig clientConfig = new DefaultClientConfig(); + clientConfig.getClasses().add(JacksonJsonProvider.class); + if (useProxy) { + URLConnectionClientHandler ch = new URLConnectionClientHandler(new ConnectionFactory()); + this.client = new Client(ch, clientConfig); + } else { + this.client = Client.create(clientConfig); + } + + this.entityTypeDataCache = new HashMap<>(); + this.nameCache = new HashMap<>(); + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + if (WineryRepositoryClient.VALIDATING) { + factory.setValidating(true); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema; + URL resource = this.getClass().getResource("/TOSCA-v1.0.xsd"); + try { + schema = schemaFactory.newSchema(resource); + } catch (SAXException e) { + throw new IllegalStateException("Schema could not be initalized", e); + } + factory.setSchema(schema); + } + try { + this.toscaDocumentBuilder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new IllegalStateException("document builder could not be initalized", e); + } + /* + TODO: include this somehow - in the case of VALIDATING + + Does not work with TTopolgoyTemplate as this is not allowed in the root of an XML document + this.toscaDocumentBuilder.setErrorHandler(new ErrorHandler() { + + @Override + public void warning(SAXParseException arg0) throws SAXException { + throw arg0; + } + + @Override + public void fatalError(SAXParseException arg0) throws SAXException { + throw arg0; + } + + @Override + public void error(SAXParseException arg0) throws SAXException { + throw arg0; + } + }); + */ + } + + private static JAXBContext initContext() { + // code copied+adapted from JAXBSupport + + JAXBContext context; + try { + // For winery classes, eventually the package+jaxb.index method could be better. See http://stackoverflow.com/a/3628525/873282 + // @formatter:off + context = JAXBContext.newInstance( + TDefinitions.class, + WinerysPropertiesDefinition.class); + // @formatter:on + } catch (JAXBException e) { + WineryRepositoryClient.logger.error("Could not initialize JAXBContext", e); + throw new IllegalStateException(e); + } + return context; + } + + /** + * Creates a marshaller + * + * @throws IllegalStateException if marshaller could not be instantiated + */ + private static Marshaller createMarshaller() { + // code copied+adapted from JAXBSupport + Marshaller m; + try { + m = WineryRepositoryClient.context.createMarshaller(); + // pretty printed output is required as the XML is sent 1:1 to the browser for editing + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + // not possible here: m.setProperty("com.sun.xml.bind.namespacePrefixMapper", JAXBSupport.prefixMapper); + } catch (JAXBException e) { + WineryRepositoryClient.logger.error("Could not instantiate marshaller", e); + throw new IllegalStateException(e); + } + return m; + } + + /** + * Creates a unmarshaller + * + * @throws IllegalStateException if unmarshaller could not be instantiated + */ + private static Unmarshaller createUnmarshaller() { + Unmarshaller um; + try { + um = WineryRepositoryClient.context.createUnmarshaller(); + } catch (JAXBException e) { + WineryRepositoryClient.logger.error("Could not instantiate unmarshaller", e); + throw new IllegalStateException(e); + } + return um; + } + + /*** methods directly from IWineryRepositoryClient ***/ + + /** + * {@inheritDoc} + */ + @Override + public void addRepository(String uri) { + if (this.knownURIs.add(uri)) { + // URI is not already known, add a new resource + WebResource wr = this.client.resource(uri); + this.repositoryResources.add(wr); + if (this.primaryRepository == null) { + this.primaryRepository = uri; + this.primaryWebResource = wr; + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public String getPrimaryRepository() { + return this.primaryRepository; + } + + /** + * {@inheritDoc} + */ + @Override + public void setPrimaryRepository(String uri) { + this.addRepository(uri); + // now we are sure that a web resource for the uri exists + this.primaryRepository = uri; + // Update the reference to the primaryWebResource + // The appropriate resource has been created via + // this.addRepository(uri); + for (WebResource wr : this.repositoryResources) { + if (wr.getURI().equals(uri)) { + this.primaryWebResource = wr; + break; + } + } + assert (this.primaryWebResource != null); + } + + /*** methods directly from IWineryRepository ***/ + + /** + * {@inheritDoc} + */ + @Override + public SortedSet<String> getNamespaces() { + SortedSet<String> res = new TreeSet<String>(); + for (WebResource wr : this.repositoryResources) { + WebResource namespacesResource = wr.path("admin").path("namespaces"); + + // this could be parsed using JAXB + // (http://jersey.java.net/nonav/documentation/latest/json.html), + // but we are short in time, so we do a quick hack + String nsList = namespacesResource.accept(MediaType.APPLICATION_JSON).get(String.class); + WineryRepositoryClient.logger.trace(nsList); + List<String> nsListList; + try { + nsListList = this.mapper.readValue(nsList, new TypeReference<List<String>>() { + }); + } catch (Exception e) { + WineryRepositoryClient.logger.error(e.getMessage(), e); + continue; + } + res.addAll(nsListList); + } + return res; + } + + /** + * Base method for getQNameListOfAllTypes and getAllTypes. + */ + private <T extends TExtensibleElements> Map<WebResource, List<NamespaceIdOptionalName>> getWRtoNamespaceAndIdListMapOfAllTypes(String path) { + Map<WebResource, List<NamespaceIdOptionalName>> res = new HashMap<WebResource, List<NamespaceIdOptionalName>>(); + for (WebResource wr : this.repositoryResources) { + WebResource componentListResource = wr.path(path); + + // this could be parsed using JAXB + // (http://jersey.java.net/nonav/documentation/latest/json.html), + // but we are short in time, so we do a quick hack + // The result also contains the optional name + String idList = componentListResource.accept(MediaType.APPLICATION_JSON).get(String.class); + WineryRepositoryClient.logger.trace(idList); + List<NamespaceIdOptionalName> nsAndIdList; + try { + nsAndIdList = this.mapper.readValue(idList, new TypeReference<List<NamespaceIdOptionalName>>() { + }); + } catch (Exception e) { + WineryRepositoryClient.logger.error(e.getMessage(), e); + continue; + } + res.put(wr, nsAndIdList); + } + return res; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName(GenericId id) { + if (this.nameCache.containsKey(id)) { + return this.nameCache.get(id); + } + + String name = null; + for (WebResource wr : this.repositoryResources) { + String pathFragment = IdUtil.getURLPathFragment(id); + WebResource resource = wr.path(pathFragment).path("name"); + ClientResponse response = resource.accept(MediaType.TEXT_PLAIN_TYPE).get(ClientResponse.class); + if (response.getClientResponseStatus() == ClientResponse.Status.OK) { + name = response.getEntity(String.class); + // break loop as the first match is the final result + break; + } + } + // if all resources did not return "OK", "null" is returned + + if (name != null) { + if (this.nameCache.size() > WineryRepositoryClient.MAX_NAME_CACHE_SIZE) { + // if cache grew too large, clear it. + this.nameCache.clear(); + } + this.nameCache.put(id, name); + } + + return name; + } + + /** + * {@inheritDoc} + */ + @Override + public <T extends TEntityType> List<QName> getQNameListOfAllTypes(Class<T> className) { + String path = Util.getURLpathFragmentForCollection(className); + Map<WebResource, List<NamespaceIdOptionalName>> wRtoNamespaceAndIdListMapOfAllTypes = this.getWRtoNamespaceAndIdListMapOfAllTypes(path); + Collection<List<NamespaceIdOptionalName>> namespaceAndIdListCollection = wRtoNamespaceAndIdListMapOfAllTypes.values(); + List<QName> res = new ArrayList<QName>(namespaceAndIdListCollection.size()); + for (List<NamespaceIdOptionalName> namespaceAndIdList : namespaceAndIdListCollection) { + for (NamespaceIdOptionalName namespaceAndId : namespaceAndIdList) { + QName qname = new QName(namespaceAndId.getNamespace(), namespaceAndId.getId()); + res.add(qname); + } + } + return res; + } + + /** + * Fetches java objects at a given URL + * + * @param path the path to use. E.g., "nodetypes" for node types, ... + * @param className the class of the expected return type. May be + * TDefinitions or TEntityType. TDefinitions the mode is that the + * import statement are recursively resolved and added to the + * returned Defintitions elment + */ + // we convert an object to T if it T is definitions + // does not work without compiler error + @SuppressWarnings("unchecked") + private <T extends TExtensibleElements> Collection<T> getAllTypes(String path, Class<T> className) { + Map<WebResource, List<NamespaceIdOptionalName>> wRtoNamespaceAndIdListMapOfAllTypes = this.getWRtoNamespaceAndIdListMapOfAllTypes(path); + // now we now all QNames. We have to fetch the full content now + + Collection<T> res = new LinkedList<T>(); + for (WebResource wr : wRtoNamespaceAndIdListMapOfAllTypes.keySet()) { + WebResource componentListResource = wr.path(path); + + // go through all ids and fetch detailed information on each + // type + + for (NamespaceIdOptionalName nsAndId : wRtoNamespaceAndIdListMapOfAllTypes.get(wr)) { + TDefinitions definitions = WineryRepositoryClient.getDefinitions(componentListResource, nsAndId.getNamespace(), nsAndId.getId()); + if (definitions == null) { + // try next one + continue; + } + + T result; + + if (TDefinitions.class.equals(className)) { + // mode: complete definitions + result = (T) definitions; + } else { + // mode: only the nested element + // convention: first element in list is the element we look for + if (definitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().isEmpty()) { + result = null; + WineryRepositoryClient.logger.error("Type {}/{} was found, but did not return any data", nsAndId.getNamespace(), nsAndId.getId()); + } else { + WineryRepositoryClient.logger.trace("Probably found valid data for {}/{}", nsAndId.getNamespace(), nsAndId.getId()); + result = (T) definitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0); + + this.cache((TEntityType) result, new QName(nsAndId.getNamespace(), nsAndId.getId())); + } + } + + // TODO: if multiple repositories are used, the new element + // should be put "sorted" into the list. This could be done by + // add(parsedResult, index), where index is calculated by + // incrementing index as long as the current element is smaller + // than the element to insert. + if (result != null) { + res.add(result); + } + } + } + return res; + } + + /** + * Caches the TEntityType data of a QName to avoid multiple get requests + * + * NOT thread safe + */ + private void cache(TEntityType et, QName qName) { + Map<QName, TEntityType> map; + if ((map = this.entityTypeDataCache.get(et.getClass())) == null) { + map = new HashMap<>(); + this.entityTypeDataCache.put(et.getClass(), map); + } else { + // quick hack to keep cache size small + if (map.size() > 1000) { + map.clear(); + } + } + map.put(qName, et); + } + + private static WebResource getTopologyTemplateWebResource(WebResource base, QName serviceTemplate) { + String nsEncoded = Util.DoubleURLencode(serviceTemplate.getNamespaceURI()); + String idEncoded = Util.DoubleURLencode(serviceTemplate.getLocalPart()); + WebResource res = base.path("servicetemplates").path(nsEncoded).path(idEncoded).path("topologytemplate"); + return res; + } + + /** + * {@inheritDoc} + */ + @Override + public Collection<QNameWithName> getListOfAllInstances(Class<? extends TOSCAComponentId> clazz) { + // inspired by getQNameListOfAllTypes + String path = Util.getRootPathFragment(clazz); + Map<WebResource, List<NamespaceIdOptionalName>> wRtoNamespaceAndIdListMapOfAllTypes = this.getWRtoNamespaceAndIdListMapOfAllTypes(path); + Collection<List<NamespaceIdOptionalName>> namespaceAndIdListCollection = wRtoNamespaceAndIdListMapOfAllTypes.values(); + List<QNameWithName> res = new ArrayList<QNameWithName>(namespaceAndIdListCollection.size()); + + for (List<NamespaceIdOptionalName> namespaceAndIdList : namespaceAndIdListCollection) { + for (NamespaceIdOptionalName namespaceAndId : namespaceAndIdList) { + QNameWithName qn = new QNameWithName(); + qn.qname = new QName(namespaceAndId.getNamespace(), namespaceAndId.getId()); + qn.name = namespaceAndId.getName(); + res.add(qn); + } + } + return res; + } + + /** + * {@inheritDoc} + */ + @Override + public <T extends TEntityType> Collection<T> getAllTypes(Class<T> c) { + String urlPathFragment = Util.getURLpathFragmentForCollection(c); + Collection<T> allTypes = this.getAllTypes(urlPathFragment, c); + return allTypes; + } + + @Override + @SuppressWarnings("unchecked") + public <T extends TEntityType> T getType(QName qname, Class<T> type) { + T res = null; + if (this.entityTypeDataCache.containsKey(type)) { + Map<QName, TEntityType> map = this.entityTypeDataCache.get(type); + if (map.containsKey(qname)) { + res = (T) map.get(qname); + } + } + + if (res == null) { + // not yet seen, try to fetch resource + + for (WebResource wr : this.repositoryResources) { + String path = Util.getURLpathFragmentForCollection(type); + + TDefinitions definitions = WineryRepositoryClient.getDefinitions(wr, path, qname.getNamespaceURI(), qname.getLocalPart()); + + if (definitions == null) { + // in case of an error, just try the next one + continue; + } + + res = (T) definitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0); + this.cache(res, qname); + break; + } + } + + return res; + } + + /** + * Tries to retrieve a TDefinitions from the given resource / encoded(ns) / + * encoded(localPart) + * + * @return null if 404 or other error + */ + private static TDefinitions getDefinitions(WebResource wr, String path, String ns, String localPart) { + WebResource componentListResource = wr.path(path); + return WineryRepositoryClient.getDefinitions(componentListResource, ns, localPart); + } + + /** + * Tries to retrieve a TDefinitions from the given resource / encoded(ns) / + * encoded(localPart) + * + * @return null if 404 or other error + */ + private static TDefinitions getDefinitions(WebResource componentListResource, String ns, String localPart) { + // we need double encoding as the client decodes the URL once + String nsEncoded = Util.DoubleURLencode(ns); + String idEncoded = Util.DoubleURLencode(localPart); + + WebResource instanceResource = componentListResource.path(nsEncoded).path(idEncoded); + + // TODO: org.eclipse.winery.repository.resources.AbstractComponentInstanceResource.getDefinitionsWithAssociatedThings() could be used to do the resolving at the server + + ClientResponse response = instanceResource.accept(MimeTypes.MIMETYPE_TOSCA_DEFINITIONS).get(ClientResponse.class); + if (response.getStatus() != 200) { + // also handles 404 + return null; + } + + TDefinitions definitions; + try { + Unmarshaller um = WineryRepositoryClient.createUnmarshaller(); + definitions = (TDefinitions) um.unmarshal(response.getEntityInputStream()); + } catch (JAXBException e) { + WineryRepositoryClient.logger.error("Could not umarshal TDefinitions", e); + // try next service + return null; + } + return definitions; + } + + /** + * {@inheritDoc} + */ + @Override + public <T extends TEntityType> Collection<TDefinitions> getAllTypesWithAssociatedElements(Class<T> c) { + String urlPathFragment = Util.getURLpathFragmentForCollection(c); + Collection<TDefinitions> allTypes = this.getAllTypes(urlPathFragment, TDefinitions.class); + return allTypes; + } + + /** + * + * @param stream the stream to parse + * @return null if document is invalid + */ + private Document parseAndValidateTOSCAXML(InputStream stream) { + Document document; + try { + document = this.toscaDocumentBuilder.parse(stream); + } catch (SAXException | IOException e) { + WineryRepositoryClient.logger.debug("Could not parse TOSCA file", e); + return null; + } + return document; + } + + /** + * {@inheritDoc} + */ + @Override + public TTopologyTemplate getTopologyTemplate(QName serviceTemplate) { + // we try all repositories until the first hit + for (WebResource wr : this.repositoryResources) { + WebResource r = WineryRepositoryClient.getTopologyTemplateWebResource(wr, serviceTemplate); + ClientResponse response = r.accept(MediaType.TEXT_XML).get(ClientResponse.class); + if (response.getClientResponseStatus() == ClientResponse.Status.OK) { + TTopologyTemplate topologyTemplate; + Document doc = this.parseAndValidateTOSCAXML(response.getEntityInputStream()); + if (doc == null) { + // no valid document + return null; + } + try { + topologyTemplate = WineryRepositoryClient.createUnmarshaller().unmarshal(doc.getDocumentElement(), TTopologyTemplate.class).getValue(); + } catch (JAXBException e) { + WineryRepositoryClient.logger.debug("Could not parse topology, returning null", e); + return null; + } + // first hit: immediately stop and return result + return topologyTemplate; + } + } + // nothing found + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void setTopologyTemplate(QName serviceTemplate, TTopologyTemplate topologyTemplate) throws Exception { + WebResource r = WineryRepositoryClient.getTopologyTemplateWebResource(this.primaryWebResource, serviceTemplate); + String xmlAsString = Util.getXMLAsString(TTopologyTemplate.class, topologyTemplate); + ClientResponse response = r.type(MediaType.TEXT_XML).put(ClientResponse.class, xmlAsString); + WineryRepositoryClient.logger.debug(response.toString()); + int status = response.getStatus(); + if ((status < 200) || (status >= 300)) { + throw new Exception(response.toString()); + } + } + + /** + * {@inheritDoc} + */ + @Override + public QName getArtifactTypeQNameForExtension(String extension) { + // we try all repositories until the first hit + for (WebResource wr : this.repositoryResources) { + WebResource artifactTypesResource = wr.path("artifacttypes").queryParam("extension", extension); + ClientResponse response = artifactTypesResource.accept(MediaType.TEXT_PLAIN).get(ClientResponse.class); + if (response.getClientResponseStatus() == ClientResponse.Status.OK) { + QName res = QName.valueOf(response.getEntity(String.class)); + return res; + } + } + return null; + } + + /** + * {@inheritDoc} + * + * Does NOT check for global QName uniqueness, only in the scope of all + * artifact templates + */ + @Override + public void createArtifactTemplate(QName qname, QName artifactType) throws QNameAlreadyExistsException { + WebResource artifactTemplates = this.primaryWebResource.path("artifacttemplates"); + MultivaluedMap<String, String> map = new MultivaluedMapImpl(); + map.putSingle("namespace", qname.getNamespaceURI()); + map.putSingle("name", qname.getLocalPart()); + map.putSingle("type", artifactType.toString()); + ClientResponse response = artifactTemplates.type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.TEXT_PLAIN).post(ClientResponse.class, map); + if (response.getClientResponseStatus() != ClientResponse.Status.CREATED) { + // TODO: pass ClientResponse.Status somehow + // TODO: more fine grained checking for error message. Not all + // failures are that the QName already exists + WineryRepositoryClient.logger.debug(String.format("Error %d when creating id %s from URI %s", response.getStatus(), qname.toString(), this.primaryWebResource.getURI().toString())); + throw new QNameAlreadyExistsException(); + } + // no further return is made + } + + /** + * {@inheritDoc} + */ + @Override + public void createComponent(QName qname, Class<? extends TOSCAComponentId> idClass) throws QNameAlreadyExistsException { + WebResource resource = this.primaryWebResource.path(Util.getRootPathFragment(idClass)); + MultivaluedMap<String, String> map = new MultivaluedMapImpl(); + map.putSingle("namespace", qname.getNamespaceURI()); + map.putSingle("name", qname.getLocalPart()); + ClientResponse response = resource.type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.TEXT_PLAIN).post(ClientResponse.class, map); + if (response.getClientResponseStatus() != ClientResponse.Status.CREATED) { + // TODO: pass ClientResponse.Status somehow + // TODO: more fine grained checking for error message. Not all failures are that the QName already exists + WineryRepositoryClient.logger.debug(String.format("Error %d when creating id %s from URI %s", response.getStatus(), qname.toString(), this.primaryWebResource.getURI().toString())); + throw new QNameAlreadyExistsException(); + } + // no further return is made + } + + @Override + public void forceDelete(GenericId id) throws IOException { + String pathFragment = IdUtil.getURLPathFragment(id); + for (WebResource wr : this.repositoryResources) { + ClientResponse response = wr.path(pathFragment).delete(ClientResponse.class); + if ((response.getClientResponseStatus() != ClientResponse.Status.NO_CONTENT) || (response.getClientResponseStatus() != ClientResponse.Status.NOT_FOUND)) { + WineryRepositoryClient.logger.debug(String.format("Error %d when deleting id %s from URI %s", response.getStatus(), id.toString(), wr.getURI().toString())); + } + } + } + + @Override + public boolean primaryRepositoryAvailable() { + if (this.primaryWebResource == null) { + return false; + } + + ClientResponse response = this.primaryWebResource.get(ClientResponse.class); + boolean res = (response.getClientResponseStatus() == ClientResponse.Status.OK); + return res; + } + +} diff --git a/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/WineryRepositoryClientFactory.java b/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/WineryRepositoryClientFactory.java new file mode 100644 index 0000000..59f0166 --- /dev/null +++ b/winery/org.eclipse.winery.repository.client/src/main/java/org/eclipse/winery/repository/client/WineryRepositoryClientFactory.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation + *******************************************************************************/ +package org.eclipse.winery.repository.client; + +public class WineryRepositoryClientFactory { + + public static IWineryRepositoryClient getWineryRepositoryClient() { + return new WineryRepositoryClient(); + } + +} diff --git a/winery/org.eclipse.winery.repository.client/src/main/resources/TOSCA-v1.0.xsd b/winery/org.eclipse.winery.repository.client/src/main/resources/TOSCA-v1.0.xsd new file mode 100644 index 0000000..8bed0e0 --- /dev/null +++ b/winery/org.eclipse.winery.repository.client/src/main/resources/TOSCA-v1.0.xsd @@ -0,0 +1,791 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Topology and Orchestration Specification for Cloud Applications Version 1.0 + Committee Specification Draft 08 + 09 May 2013 + Copyright (c) OASIS Open 2013. All rights reserved. + Source: http://docs.oasis-open.org/tosca/TOSCA/v1.0/csd08/schemas/ +--> +<xs:schema targetNamespace="http://docs.oasis-open.org/tosca/ns/2011/12" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns="http://docs.oasis-open.org/tosca/ns/2011/12" xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/> + <xs:element name="documentation" type="tDocumentation"/> + <xs:complexType name="tDocumentation" mixed="true"> + <xs:sequence> + <xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="source" type="xs:anyURI"/> + <xs:attribute ref="xml:lang"/> + </xs:complexType> + <xs:complexType name="tExtensibleElements"> + <xs:sequence> + <xs:element ref="documentation" minOccurs="0" maxOccurs="unbounded"/> + <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:anyAttribute namespace="##other" processContents="lax"/> + </xs:complexType> + <xs:complexType name="tImport"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:attribute name="namespace" type="xs:anyURI"/> + <xs:attribute name="location" type="xs:anyURI"/> + <xs:attribute name="importType" type="importedURI" use="required"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:element name="Definitions"> + <xs:complexType> + <xs:complexContent> + <xs:extension base="tDefinitions"/> + </xs:complexContent> + </xs:complexType> + </xs:element> + <xs:complexType name="tDefinitions"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Extensions" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Extension" type="tExtension" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="Import" type="tImport" minOccurs="0" maxOccurs="unbounded"/> + <xs:element name="Types" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:choice maxOccurs="unbounded"> + <xs:element name="ServiceTemplate" type="tServiceTemplate"/> + <xs:element name="NodeType" type="tNodeType"/> + <xs:element name="NodeTypeImplementation" type="tNodeTypeImplementation"/> + <xs:element name="RelationshipType" type="tRelationshipType"/> + <xs:element name="RelationshipTypeImplementation" type="tRelationshipTypeImplementation"/> + <xs:element name="RequirementType" type="tRequirementType"/> + <xs:element name="CapabilityType" type="tCapabilityType"/> + <xs:element name="ArtifactType" type="tArtifactType"/> + <xs:element name="ArtifactTemplate" type="tArtifactTemplate"/> + <xs:element name="PolicyType" type="tPolicyType"/> + <xs:element name="PolicyTemplate" type="tPolicyTemplate"/> + </xs:choice> + </xs:sequence> + <xs:attribute name="id" type="xs:ID" use="required"/> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:attribute name="targetNamespace" type="xs:anyURI" use="required"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tServiceTemplate"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Tags" type="tTags" minOccurs="0"/> + <xs:element name="BoundaryDefinitions" type="tBoundaryDefinitions" minOccurs="0"/> + <xs:element name="TopologyTemplate" type="tTopologyTemplate"/> + <xs:element name="Plans" type="tPlans" minOccurs="0"/> + </xs:sequence> + <xs:attribute name="id" type="xs:ID" use="required"/> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:attribute name="targetNamespace" type="xs:anyURI"/> + <xs:attribute name="substitutableNodeType" type="xs:QName" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tTags"> + <xs:sequence> + <xs:element name="Tag" type="tTag" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + <xs:complexType name="tTag"> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="value" type="xs:string" use="required"/> + </xs:complexType> + <xs:complexType name="tBoundaryDefinitions"> + <xs:sequence> + <xs:element name="Properties" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:any namespace="##other"/> + <xs:element name="PropertyMappings" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="PropertyMapping" type="tPropertyMapping" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="PropertyConstraints" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="PropertyConstraint" type="tPropertyConstraint" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="Requirements" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Requirement" type="tRequirementRef" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="Capabilities" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Capability" type="tCapabilityRef" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="Policies" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Policy" type="tPolicy" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="Interfaces" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Interface" type="tExportedInterface" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + <xs:complexType name="tPropertyMapping"> + <xs:attribute name="serviceTemplatePropertyRef" type="xs:string" use="required"/> + <xs:attribute name="targetObjectRef" type="xs:IDREF" use="required"/> + <xs:attribute name="targetPropertyRef" type="xs:string" use="required"/> + </xs:complexType> + <xs:complexType name="tRequirementRef"> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:attribute name="ref" type="xs:IDREF" use="required"/> + </xs:complexType> + <xs:complexType name="tCapabilityRef"> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:attribute name="ref" type="xs:IDREF" use="required"/> + </xs:complexType> + <xs:complexType name="tEntityType" abstract="true"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Tags" type="tTags" minOccurs="0"/> + <xs:element name="DerivedFrom" minOccurs="0"> + <xs:complexType> + <xs:attribute name="typeRef" type="xs:QName" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="PropertiesDefinition" minOccurs="0"> + <xs:complexType> + <xs:attribute name="element" type="xs:QName"/> + <xs:attribute name="type" type="xs:QName"/> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" type="xs:NCName" use="required"/> + <xs:attribute name="abstract" type="tBoolean" default="no"/> + <xs:attribute name="final" type="tBoolean" default="no"/> + <xs:attribute name="targetNamespace" type="xs:anyURI" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tEntityTemplate" abstract="true"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Properties" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:any namespace="##other" processContents="lax"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="PropertyConstraints" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="PropertyConstraint" type="tPropertyConstraint" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="id" type="xs:ID" use="required"/> + <xs:attribute name="type" type="xs:QName" use="required"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tNodeTemplate"> + <xs:complexContent> + <xs:extension base="tEntityTemplate"> + <xs:sequence> + <xs:element name="Requirements" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Requirement" type="tRequirement" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="Capabilities" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Capability" type="tCapability" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="Policies" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Policy" type="tPolicy" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="DeploymentArtifacts" type="tDeploymentArtifacts" minOccurs="0"/> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:attribute name="minInstances" type="xs:int" use="optional" default="1"/> + <xs:attribute name="maxInstances" use="optional" default="1"> + <xs:simpleType> + <xs:union> + <xs:simpleType> + <xs:restriction base="xs:nonNegativeInteger"> + <xs:pattern value="([1-9]+[0-9]*)"/> + </xs:restriction> + </xs:simpleType> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="unbounded"/> + </xs:restriction> + </xs:simpleType> + </xs:union> + </xs:simpleType> + </xs:attribute> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tTopologyTemplate"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:choice maxOccurs="unbounded"> + <xs:element name="NodeTemplate" type="tNodeTemplate"/> + <xs:element name="RelationshipTemplate" type="tRelationshipTemplate"/> + </xs:choice> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tRelationshipType"> + <xs:complexContent> + <xs:extension base="tEntityType"> + <xs:sequence> + <xs:element name="InstanceStates" type="tTopologyElementInstanceStates" minOccurs="0"/> + <xs:element name="SourceInterfaces" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Interface" type="tInterface" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="TargetInterfaces" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Interface" type="tInterface" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="ValidSource" minOccurs="0"> + <xs:complexType> + <xs:attribute name="typeRef" type="xs:QName" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="ValidTarget" minOccurs="0"> + <xs:complexType> + <xs:attribute name="typeRef" type="xs:QName" use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tRelationshipTypeImplementation"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Tags" type="tTags" minOccurs="0"/> + <xs:element name="DerivedFrom" minOccurs="0"> + <xs:complexType> + <xs:attribute name="relationshipTypeImplementationRef" type="xs:QName" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="RequiredContainerFeatures" type="tRequiredContainerFeatures" minOccurs="0"/> + <xs:element name="ImplementationArtifacts" type="tImplementationArtifacts" minOccurs="0"/> + </xs:sequence> + <xs:attribute name="name" type="xs:NCName" use="required"/> + <xs:attribute name="targetNamespace" type="xs:anyURI" use="optional"/> + <xs:attribute name="relationshipType" type="xs:QName" use="required"/> + <xs:attribute name="abstract" type="tBoolean" use="optional" default="no"/> + <xs:attribute name="final" type="tBoolean" use="optional" default="no"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tRelationshipTemplate"> + <xs:complexContent> + <xs:extension base="tEntityTemplate"> + <xs:sequence> + <xs:element name="SourceElement"> + <xs:complexType> + <xs:attribute name="ref" type="xs:IDREF" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="TargetElement"> + <xs:complexType> + <xs:attribute name="ref" type="xs:IDREF" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="RelationshipConstraints" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="RelationshipConstraint" maxOccurs="unbounded"> + <xs:complexType> + <xs:sequence> + <xs:any namespace="##other" processContents="lax" minOccurs="0"/> + </xs:sequence> + <xs:attribute name="constraintType" type="xs:anyURI" use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tNodeType"> + <xs:complexContent> + <xs:extension base="tEntityType"> + <xs:sequence> + <xs:element name="RequirementDefinitions" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="RequirementDefinition" type="tRequirementDefinition" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="CapabilityDefinitions" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="CapabilityDefinition" type="tCapabilityDefinition" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="InstanceStates" type="tTopologyElementInstanceStates" minOccurs="0"/> + <xs:element name="Interfaces" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Interface" type="tInterface" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tNodeTypeImplementation"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Tags" type="tTags" minOccurs="0"/> + <xs:element name="DerivedFrom" minOccurs="0"> + <xs:complexType> + <xs:attribute name="nodeTypeImplementationRef" type="xs:QName" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="RequiredContainerFeatures" type="tRequiredContainerFeatures" minOccurs="0"/> + <xs:element name="ImplementationArtifacts" type="tImplementationArtifacts" minOccurs="0"/> + <xs:element name="DeploymentArtifacts" type="tDeploymentArtifacts" minOccurs="0"/> + </xs:sequence> + <xs:attribute name="name" type="xs:NCName" use="required"/> + <xs:attribute name="targetNamespace" type="xs:anyURI" use="optional"/> + <xs:attribute name="nodeType" type="xs:QName" use="required"/> + <xs:attribute name="abstract" type="tBoolean" use="optional" default="no"/> + <xs:attribute name="final" type="tBoolean" use="optional" default="no"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tRequirementType"> + <xs:complexContent> + <xs:extension base="tEntityType"> + <xs:attribute name="requiredCapabilityType" type="xs:QName" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tRequirementDefinition"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Constraints" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Constraint" type="tConstraint" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="requirementType" type="xs:QName" use="required"/> + <xs:attribute name="lowerBound" type="xs:int" use="optional" default="1"/> + <xs:attribute name="upperBound" use="optional" default="1"> + <xs:simpleType> + <xs:union> + <xs:simpleType> + <xs:restriction base="xs:nonNegativeInteger"> + <xs:pattern value="([1-9]+[0-9]*)"/> + </xs:restriction> + </xs:simpleType> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="unbounded"/> + </xs:restriction> + </xs:simpleType> + </xs:union> + </xs:simpleType> + </xs:attribute> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tRequirement"> + <xs:complexContent> + <xs:extension base="tEntityTemplate"> + <xs:attribute name="name" type="xs:string" use="required"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tCapabilityType"> + <xs:complexContent> + <xs:extension base="tEntityType"/> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tCapabilityDefinition"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Constraints" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="Constraint" type="tConstraint" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="capabilityType" type="xs:QName" use="required"/> + <xs:attribute name="lowerBound" type="xs:int" use="optional" default="1"/> + <xs:attribute name="upperBound" use="optional" default="1"> + <xs:simpleType> + <xs:union> + <xs:simpleType> + <xs:restriction base="xs:nonNegativeInteger"> + <xs:pattern value="([1-9]+[0-9]*)"/> + </xs:restriction> + </xs:simpleType> + <xs:simpleType> + <xs:restriction base="xs:string"> + <xs:enumeration value="unbounded"/> + </xs:restriction> + </xs:simpleType> + </xs:union> + </xs:simpleType> + </xs:attribute> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tCapability"> + <xs:complexContent> + <xs:extension base="tEntityTemplate"> + <xs:attribute name="name" type="xs:string" use="required"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tArtifactType"> + <xs:complexContent> + <xs:extension base="tEntityType"/> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tArtifactTemplate"> + <xs:complexContent> + <xs:extension base="tEntityTemplate"> + <xs:sequence> + <xs:element name="ArtifactReferences" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="ArtifactReference" type="tArtifactReference" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" type="xs:string" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tDeploymentArtifacts"> + <xs:sequence> + <xs:element name="DeploymentArtifact" type="tDeploymentArtifact" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + <xs:complexType name="tDeploymentArtifact"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="artifactType" type="xs:QName" use="required"/> + <xs:attribute name="artifactRef" type="xs:QName" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tImplementationArtifacts"> + <xs:sequence> + <xs:element name="ImplementationArtifact" maxOccurs="unbounded"> + <xs:complexType> + <xs:complexContent> + <xs:extension base="tImplementationArtifact"/> + </xs:complexContent> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + <xs:complexType name="tImplementationArtifact"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:attribute name="interfaceName" type="xs:anyURI" use="optional"/> + <xs:attribute name="operationName" type="xs:NCName" use="optional"/> + <xs:attribute name="artifactType" type="xs:QName" use="required"/> + <xs:attribute name="artifactRef" type="xs:QName" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tPlans"> + <xs:sequence> + <xs:element name="Plan" type="tPlan" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="targetNamespace" type="xs:anyURI" use="optional"/> + </xs:complexType> + <xs:complexType name="tPlan"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Precondition" type="tCondition" minOccurs="0"/> + <xs:element name="InputParameters" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="InputParameter" type="tParameter" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="OutputParameters" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="OutputParameter" type="tParameter" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:choice> + <xs:element name="PlanModel"> + <xs:complexType> + <xs:sequence> + <xs:any namespace="##other" processContents="lax"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="PlanModelReference"> + <xs:complexType> + <xs:attribute name="reference" type="xs:anyURI" use="required"/> + </xs:complexType> + </xs:element> + </xs:choice> + </xs:sequence> + <xs:attribute name="id" type="xs:ID" use="required"/> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:attribute name="planType" type="xs:anyURI" use="required"/> + <xs:attribute name="planLanguage" type="xs:anyURI" use="required"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tPolicyType"> + <xs:complexContent> + <xs:extension base="tEntityType"> + <xs:sequence> + <xs:element name="AppliesTo" type="tAppliesTo" minOccurs="0"/> + </xs:sequence> + <xs:attribute name="policyLanguage" type="xs:anyURI" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tPolicyTemplate"> + <xs:complexContent> + <xs:extension base="tEntityTemplate"> + <xs:attribute name="name" type="xs:string" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tAppliesTo"> + <xs:sequence> + <xs:element name="NodeTypeReference" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="typeRef" type="xs:QName" use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + <xs:complexType name="tPolicy"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:attribute name="name" type="xs:string" use="optional"/> + <xs:attribute name="policyType" type="xs:QName" use="required"/> + <xs:attribute name="policyRef" type="xs:QName" use="optional"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tConstraint"> + <xs:sequence> + <xs:any namespace="##other" processContents="lax"/> + </xs:sequence> + <xs:attribute name="constraintType" type="xs:anyURI" use="required"/> + </xs:complexType> + <xs:complexType name="tPropertyConstraint"> + <xs:complexContent> + <xs:extension base="tConstraint"> + <xs:attribute name="property" type="xs:string" use="required"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tExtensions"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="Extension" type="tExtension" maxOccurs="unbounded"/> + </xs:sequence> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tExtension"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:attribute name="namespace" type="xs:anyURI" use="required"/> + <xs:attribute name="mustUnderstand" type="tBoolean" use="optional" default="yes"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tParameter"> + <xs:attribute name="name" type="xs:string" use="required"/> + <xs:attribute name="type" type="xs:string" use="required"/> + <xs:attribute name="required" type="tBoolean" use="optional" default="yes"/> + </xs:complexType> + <xs:complexType name="tInterface"> + <xs:sequence> + <xs:element name="Operation" type="tOperation" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="name" type="xs:anyURI" use="required"/> + </xs:complexType> + <xs:complexType name="tExportedInterface"> + <xs:sequence> + <xs:element name="Operation" type="tExportedOperation" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="name" type="xs:anyURI" use="required"/> + </xs:complexType> + <xs:complexType name="tOperation"> + <xs:complexContent> + <xs:extension base="tExtensibleElements"> + <xs:sequence> + <xs:element name="InputParameters" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="InputParameter" type="tParameter" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + <xs:element name="OutputParameters" minOccurs="0"> + <xs:complexType> + <xs:sequence> + <xs:element name="OutputParameter" type="tParameter" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + </xs:sequence> + <xs:attribute name="name" type="xs:NCName" use="required"/> + </xs:extension> + </xs:complexContent> + </xs:complexType> + <xs:complexType name="tExportedOperation"> + <xs:choice> + <xs:element name="NodeOperation"> + <xs:complexType> + <xs:attribute name="nodeRef" type="xs:IDREF" use="required"/> + <xs:attribute name="interfaceName" type="xs:anyURI" use="required"/> + <xs:attribute name="operationName" type="xs:NCName" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="RelationshipOperation"> + <xs:complexType> + <xs:attribute name="relationshipRef" type="xs:IDREF" use="required"/> + <xs:attribute name="interfaceName" type="xs:anyURI" use="required"/> + <xs:attribute name="operationName" type="xs:NCName" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="Plan"> + <xs:complexType> + <xs:attribute name="planRef" type="xs:IDREF" use="required"/> + </xs:complexType> + </xs:element> + </xs:choice> + <xs:attribute name="name" type="xs:NCName" use="required"/> + </xs:complexType> + <xs:complexType name="tCondition"> + <xs:sequence> + <xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:attribute name="expressionLanguage" type="xs:anyURI" use="required"/> + </xs:complexType> + <xs:complexType name="tTopologyElementInstanceStates"> + <xs:sequence> + <xs:element name="InstanceState" maxOccurs="unbounded"> + <xs:complexType> + <xs:attribute name="state" type="xs:anyURI" use="required"/> + </xs:complexType> + </xs:element> + </xs:sequence> + </xs:complexType> + <xs:complexType name="tArtifactReference"> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="Include"> + <xs:complexType> + <xs:attribute name="pattern" type="xs:string" use="required"/> + </xs:complexType> + </xs:element> + <xs:element name="Exclude"> + <xs:complexType> + <xs:attribute name="pattern" type="xs:string" use="required"/> + </xs:complexType> + </xs:element> + </xs:choice> + <xs:attribute name="reference" type="xs:anyURI" use="required"/> + </xs:complexType> + <xs:complexType name="tRequiredContainerFeatures"> + <xs:sequence> + <xs:element name="RequiredContainerFeature" type="tRequiredContainerFeature" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + <xs:complexType name="tRequiredContainerFeature"> + <xs:attribute name="feature" type="xs:anyURI" use="required"/> + </xs:complexType> + <xs:simpleType name="tBoolean"> + <xs:restriction base="xs:string"> + <xs:enumeration value="yes"/> + <xs:enumeration value="no"/> + </xs:restriction> + </xs:simpleType> + <xs:simpleType name="importedURI"> + <xs:restriction base="xs:anyURI"/> + </xs:simpleType> +</xs:schema> diff --git a/winery/org.eclipse.winery.repository.client/src/test/java/META-INF/MANIFEST.MF b/winery/org.eclipse.winery.repository.client/src/test/java/META-INF/MANIFEST.MF new file mode 100644 index 0000000..254272e --- /dev/null +++ b/winery/org.eclipse.winery.repository.client/src/test/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Class-Path: + diff --git a/winery/org.eclipse.winery.repository.client/src/test/java/org/eclipse/winery/repository/client/TestWineryRepositoryClient.java b/winery/org.eclipse.winery.repository.client/src/test/java/org/eclipse/winery/repository/client/TestWineryRepositoryClient.java new file mode 100644 index 0000000..77ab588 --- /dev/null +++ b/winery/org.eclipse.winery.repository.client/src/test/java/org/eclipse/winery/repository/client/TestWineryRepositoryClient.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2012-2013 University of Stuttgart. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and the Apache License 2.0 which both accompany this distribution, + * and are available at http://www.eclipse.org/legal/epl-v10.html + * and http://www.apache.org/licenses/LICENSE-2.0 + * + * Contributors: + * Oliver Kopp - initial API and implementation + *******************************************************************************/ +package org.eclipse.winery.repository.client; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.eclipse.winery.model.tosca.TDefinitions; +import org.eclipse.winery.model.tosca.TEntityTemplate; +import org.eclipse.winery.model.tosca.TNodeType; +import org.eclipse.winery.model.tosca.TRelationshipType; +import org.eclipse.winery.model.tosca.TTopologyTemplate; +import org.eclipse.winery.common.ids.definitions.ArtifactTemplateId; +import org.eclipse.winery.common.interfaces.QNameAlreadyExistsException; + +/** + * Tests client methods with a pre-configured client stored in a local static + * field. + * + * Client creation and multiple repositories are not tested. This should be + * subject to other test classes. + * + * TODO: This class expects things to be existent in the namespace "test". This + * should be enforced in a preload. + */ +@RunWith(JUnit4.class) +public class TestWineryRepositoryClient { + + // private final String repositoryURI = "http://2471.de:8080/wineydev"; + private static final String repositoryURI = "http://localhost:8080/winery"; + + private static final boolean USE_PROXY = true; + + private static final IWineryRepositoryClient client = new WineryRepositoryClient(TestWineryRepositoryClient.USE_PROXY); + static { + TestWineryRepositoryClient.client.addRepository(TestWineryRepositoryClient.repositoryURI); + } + + /** + * The namespace to put new things in. <br /> + * TODO: Is deleted completely after testing + */ + private static final String namespaceForNewArtifacts = "http://www.example.org/test/wineryclient/"; + + + @Test + public void getAllNodeTypes() { + Collection<TNodeType> allTypes = TestWineryRepositoryClient.client.getAllTypes(TNodeType.class); + for (TNodeType type : allTypes) { + Assert.assertNotNull("name is null", type.getName()); + Assert.assertNotNull("target namespace is null", type.getTargetNamespace()); + } + } + + @Test + public void getAllRelationshipTypes() { + Collection<TRelationshipType> allTypes = TestWineryRepositoryClient.client.getAllTypes(TRelationshipType.class); + for (TRelationshipType type : allTypes) { + Assert.assertNotNull("name is null", type.getName()); + Assert.assertNotNull("target namespace is null", type.getTargetNamespace()); + } + } + + @Test + public void getAllNodeTypesWithAssociatedElements() { + Collection<TDefinitions> allTypes = TestWineryRepositoryClient.client.getAllTypesWithAssociatedElements(TNodeType.class); + Assert.assertNotNull(allTypes); + } + + @Test + public void getAllRelationshipTypesWithAssociatedElements() { + Collection<TDefinitions> allTypes = TestWineryRepositoryClient.client.getAllTypesWithAssociatedElements(TRelationshipType.class); + Assert.assertNotNull(allTypes); + } + + @Test + public void getPropertiesOfAllNodeTypes() { + // TODO + } + + @Test + public void getPropertiesOfAllRelationshipTypes() { + // TODO + } + + @Test + public void getTestTopologyTemplate() { + QName serviceTemplate = new QName("test", "test"); + TTopologyTemplate topologyTemplate = TestWineryRepositoryClient.client.getTopologyTemplate(serviceTemplate); + Assert.assertNotNull(topologyTemplate); + } + + @Test + public void getPropertiesOfTestTopologyTemplate() { + QName serviceTemplate = new QName("test", "test"); + TTopologyTemplate topologyTemplate = TestWineryRepositoryClient.client.getTopologyTemplate(serviceTemplate); + Assert.assertNotNull(topologyTemplate); + List<TEntityTemplate> allTemplates = topologyTemplate.getNodeTemplateOrRelationshipTemplate(); + for (TEntityTemplate e : allTemplates) { + // TODO + } + } + + @Test + public void artifactTypeForWARfiles() { + QName artifactType = TestWineryRepositoryClient.client.getArtifactTypeQNameForExtension("war"); + Assert.assertNotNull("Artifact Type for .war does not exist", artifactType); + } + + @Test + public void createArtifactTemplate() throws IOException, QNameAlreadyExistsException { + // assure that the artifact type exists + QName artifactTypeQName = TestWineryRepositoryClient.client.getArtifactTypeQNameForExtension("war"); + Assert.assertNotNull("Artifact Type for .war does not exist", artifactTypeQName); + + // assure that the artifact template does not yet exist + // one possibility is to delete the artifact template, the other + // possibility is to + + QName artifactTemplateQName = new QName(TestWineryRepositoryClient.namespaceForNewArtifacts, "artifactTemplate"); + ArtifactTemplateId atId = new ArtifactTemplateId(artifactTemplateQName); + + // ensure that the template does not exist yet + TestWineryRepositoryClient.client.forceDelete(atId); + + TestWineryRepositoryClient.client.createArtifactTemplate(artifactTemplateQName, artifactTypeQName); + } +} |