/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.brooklyn.core.catalog.internal; import java.io.InputStream; import java.io.StringReader; import java.util.Collection; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.brooklyn.api.catalog.CatalogItem; import org.apache.brooklyn.util.collections.MutableList; import org.apache.brooklyn.util.collections.MutableMap; import org.apache.brooklyn.util.core.ResourceUtils; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.exceptions.PropagatedRuntimeException; import org.apache.brooklyn.util.stream.Streams; import com.google.common.annotations.Beta; import com.google.common.base.Objects; import com.google.common.collect.FluentIterable; import com.google.common.collect.Lists; @Beta public class CatalogDto { private static final Logger LOG = LoggerFactory.getLogger(CatalogDto.class); String id; String url; String contents; String contentsDescription; String name; String description; CatalogClasspathDto classpath; private List<CatalogItemDtoAbstract<?,?>> entries = null; // for thread-safety, any dynamic additions to this should be handled by a method // in this class which does copy-on-write List<CatalogDto> catalogs = null; public static CatalogDto newDefaultLocalScanningDto(CatalogClasspathDo.CatalogScanningModes scanMode) { CatalogDo result = new CatalogDo( newNamedInstance("Local Scanned Catalog", "All annotated Brooklyn entities detected in the default classpath", "scanning-local-classpath") ); result.setClasspathScanForEntities(scanMode); return result.dto; } /** @deprecated since 0.7.0 use {@link #newDtoFromXmlUrl(String)} if you must, but note the xml format itself is deprecated */ @Deprecated public static CatalogDto newDtoFromUrl(String url) { return newDtoFromXmlUrl(url); } /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */ @Deprecated public static CatalogDto newDtoFromXmlUrl(String url) { if (LOG.isDebugEnabled()) LOG.debug("Retrieving catalog from: {}", url); try { InputStream source = ResourceUtils.create().getResourceFromUrl(url); String contents = Streams.readFullyString(source); return newDtoFromXmlContents(contents, url); } catch (Throwable t) { Exceptions.propagateIfFatal(t); throw new PropagatedRuntimeException("Unable to retrieve catalog from " + url + ": " + t, t); } } /** @deprecated since 0.7.0 the xml format is deprecated; use YAML parse routines on BasicBrooklynCatalog */ @Deprecated public static CatalogDto newDtoFromXmlContents(String xmlContents, String originDescription) { CatalogDto result = (CatalogDto) new CatalogXmlSerializer().deserialize(new StringReader(xmlContents)); result.contentsDescription = originDescription; if (LOG.isDebugEnabled()) LOG.debug("Retrieved catalog from: {}", originDescription); return result; } /** * Creates a DTO. * <p> * The way contents is treated may change; thus this (and much of catalog) should be treated as beta. * * @param name * @param description * @param optionalContentsDescription optional description of contents; if null, we normally expect source 'contents' to be set later; * if the DTO has no 'contents' (ie XML source) then a description should be supplied so we know who is populating it * (e.g. manual additions); without this, warnings may be generated * * @return a new Catalog DTO */ public static CatalogDto newNamedInstance(String name, String description, String optionalContentsDescription) { CatalogDto result = new CatalogDto(); result.name = name; result.description = description; if (optionalContentsDescription!=null) result.contentsDescription = optionalContentsDescription; return result; } /** Used when caller wishes to create an explicitly empty catalog */ public static CatalogDto newEmptyInstance(String optionalContentsDescription) { CatalogDto result = new CatalogDto(); if (optionalContentsDescription!=null) result.contentsDescription = optionalContentsDescription; return result; } public static CatalogDto newLinkedInstance(String url) { CatalogDto result = new CatalogDto(); result.contentsDescription = url; result.contents = ResourceUtils.create().getResourceAsString(url); return result; } /** @deprecated since 0.7.0 use {@link #newDtoFromCatalogItems(Collection, String)}, supplying a description for tracking */ @Deprecated public static CatalogDto newDtoFromCatalogItems(Collection<CatalogItem<?, ?>> entries) { return newDtoFromCatalogItems(entries, null); } @SuppressWarnings({ "unchecked", "rawtypes" }) public static CatalogDto newDtoFromCatalogItems(Collection<CatalogItem<?, ?>> entries, String description) { CatalogDto result = new CatalogDto(); result.contentsDescription = description; // Weird casts because compiler does not seem to like // .copyInto(Lists.<CatalogItemDtoAbstract<?, ?>>newArrayListWithExpectedSize(entries.size())); result.entries = (List<CatalogItemDtoAbstract<?, ?>>) (List) FluentIterable.from(entries) .filter(CatalogItemDtoAbstract.class) .copyInto(Lists.newArrayListWithExpectedSize(entries.size())); return result; } void populate() { if (contents==null) { if (url != null) { contents = ResourceUtils.create().getResourceAsString(url); contentsDescription = url; } else if (contentsDescription==null) { LOG.debug("Catalog DTO has no contents and no description; ignoring call to populate it. Description should be set to suppress this message."); return; } else { LOG.trace("Nothing needs doing (no contents or URL) for catalog with contents described as "+contentsDescription+"."); return; } } CatalogDto remoteDto = newDtoFromXmlContents(contents, contentsDescription); try { copyFrom(remoteDto, true); } catch (Exception e) { Exceptions.propagate(e); } } /** * @throws NullPointerException If source is null (and !skipNulls) */ void copyFrom(CatalogDto source, boolean skipNulls) throws IllegalAccessException { if (source==null) { if (skipNulls) return; throw new NullPointerException("source DTO is null, when copying to "+this); } if (!skipNulls || source.id != null) id = source.id; if (!skipNulls || source.contentsDescription != null) contentsDescription = source.contentsDescription; if (!skipNulls || source.contents != null) contents = source.contents; if (!skipNulls || source.name != null) name = source.name; if (!skipNulls || source.description != null) description = source.description; if (!skipNulls || source.classpath != null) classpath = source.classpath; if (!skipNulls || source.entries != null) entries = source.entries; } @Override public String toString() { return Objects.toStringHelper(this) .omitNullValues() .add("name", name) .add("id", id) .add("contentsDescription", contentsDescription) .toString(); } // temporary fix for issue where entries might not be unique Iterable<CatalogItemDtoAbstract<?, ?>> getUniqueEntries() { if (entries==null) return null; Map<String, CatalogItemDtoAbstract<?, ?>> result = getEntriesMap(); return result.values(); } private Map<String, CatalogItemDtoAbstract<?, ?>> getEntriesMap() { if (entries==null) return null; Map<String,CatalogItemDtoAbstract<?, ?>> result = MutableMap.of(); for (CatalogItemDtoAbstract<?,?> entry: entries) { result.put(entry.getId(), entry); } return result; } void removeEntry(CatalogItemDtoAbstract<?, ?> entry) { if (entries!=null) entries.remove(entry); } void addEntry(CatalogItemDtoAbstract<?, ?> entry) { if (entries == null) entries = MutableList.of(); CatalogItemDtoAbstract<?, ?> oldEntry = getEntriesMap().get(entry.getId()); entries.add(entry); if (oldEntry!=null) removeEntry(oldEntry); } }