/*
* 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.rest.transform;
import java.net.URI;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.brooklyn.api.catalog.CatalogItem;
import org.apache.brooklyn.api.catalog.CatalogItem.CatalogItemType;
import org.apache.brooklyn.api.effector.Effector;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.entity.EntityType;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.location.LocationSpec;
import org.apache.brooklyn.api.objs.SpecParameter;
import org.apache.brooklyn.api.policy.Policy;
import org.apache.brooklyn.api.policy.PolicySpec;
import org.apache.brooklyn.api.sensor.Sensor;
import org.apache.brooklyn.core.entity.EntityDynamicType;
import org.apache.brooklyn.core.mgmt.BrooklynTags;
import org.apache.brooklyn.core.objs.BrooklynTypes;
import org.apache.brooklyn.rest.domain.CatalogEntitySummary;
import org.apache.brooklyn.rest.domain.CatalogItemSummary;
import org.apache.brooklyn.rest.domain.CatalogLocationSummary;
import org.apache.brooklyn.rest.domain.CatalogPolicySummary;
import org.apache.brooklyn.rest.domain.EffectorSummary;
import org.apache.brooklyn.rest.domain.EntityConfigSummary;
import org.apache.brooklyn.rest.domain.LocationConfigSummary;
import org.apache.brooklyn.rest.domain.PolicyConfigSummary;
import org.apache.brooklyn.rest.domain.SensorSummary;
import org.apache.brooklyn.rest.domain.SummaryComparators;
import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.collections.MutableSet;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.javalang.Reflections;
import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
public class CatalogTransformer {
private static final org.slf4j.Logger log = LoggerFactory.getLogger(CatalogTransformer.class);
public static <T extends Entity> CatalogEntitySummary catalogEntitySummary(BrooklynRestResourceUtils b, CatalogItem<T,EntitySpec<? extends T>> item) {
Set<EntityConfigSummary> config = Sets.newLinkedHashSet();
Set<SensorSummary> sensors = Sets.newTreeSet(SummaryComparators.nameComparator());
Set<EffectorSummary> effectors = Sets.newTreeSet(SummaryComparators.nameComparator());
EntitySpec<?> spec = null;
try {
@SuppressWarnings({ "unchecked", "rawtypes" })
// the raw type isn't needed according to eclipse IDE, but jenkins maven fails without it;
// must be a java version or compiler thing. don't remove even though it looks okay without it!
EntitySpec<?> specRaw = (EntitySpec<?>) b.getCatalog().createSpec((CatalogItem) item);
spec = specRaw;
EntityDynamicType typeMap = BrooklynTypes.getDefinedEntityType(spec.getType());
EntityType type = typeMap.getSnapshot();
AtomicInteger paramPriorityCnt = new AtomicInteger();
for (SpecParameter<?> input: spec.getParameters()) {
config.add(EntityTransformer.entityConfigSummary(input, paramPriorityCnt));
if (input.getSensor()!=null)
sensors.add(SensorTransformer.sensorSummaryForCatalog(input.getSensor()));
}
for (Sensor<?> x: type.getSensors())
sensors.add(SensorTransformer.sensorSummaryForCatalog(x));
for (Effector<?> x: type.getEffectors())
effectors.add(EffectorTransformer.effectorSummaryForCatalog(x));
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
// templates with multiple entities can't have spec created in the manner above; just ignore
if (item.getCatalogItemType()==CatalogItemType.ENTITY) {
log.warn("Unable to create spec for "+item+": "+e, e);
}
if (log.isTraceEnabled()) {
log.trace("Unable to create spec for "+item+": "+e, e);
}
}
return new CatalogEntitySummary(item.getSymbolicName(), item.getVersion(), item.getDisplayName(),
item.getJavaType(), item.getPlanYaml(),
item.getDescription(), tidyIconLink(b, item, item.getIconUrl()),
makeTags(spec, item), config, sensors, effectors,
item.isDeprecated(), makeLinks(item));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static CatalogItemSummary catalogItemSummary(BrooklynRestResourceUtils b, CatalogItem item) {
try {
switch (item.getCatalogItemType()) {
case TEMPLATE:
case ENTITY:
return catalogEntitySummary(b, item);
case POLICY:
return catalogPolicySummary(b, item);
case LOCATION:
return catalogLocationSummary(b, item);
default:
log.warn("Unexpected catalog item type when getting self link (supplying generic item): "+item.getCatalogItemType()+" "+item);
}
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
log.warn("Invalid item in catalog when converting REST summaries (supplying generic item), at "+item+": "+e, e);
}
return new CatalogItemSummary(item.getSymbolicName(), item.getVersion(), item.getDisplayName(),
item.getJavaType(), item.getPlanYaml(),
item.getDescription(), tidyIconLink(b, item, item.getIconUrl()), item.tags().getTags(), item.isDeprecated(), makeLinks(item));
}
public static CatalogPolicySummary catalogPolicySummary(BrooklynRestResourceUtils b, CatalogItem<? extends Policy,PolicySpec<?>> item) {
Set<PolicyConfigSummary> config = ImmutableSet.of();
return new CatalogPolicySummary(item.getSymbolicName(), item.getVersion(), item.getDisplayName(),
item.getJavaType(), item.getPlanYaml(),
item.getDescription(), tidyIconLink(b, item, item.getIconUrl()), config,
item.tags().getTags(), item.isDeprecated(), makeLinks(item));
}
public static CatalogLocationSummary catalogLocationSummary(BrooklynRestResourceUtils b, CatalogItem<? extends Location,LocationSpec<?>> item) {
Set<LocationConfigSummary> config = ImmutableSet.of();
return new CatalogLocationSummary(item.getSymbolicName(), item.getVersion(), item.getDisplayName(),
item.getJavaType(), item.getPlanYaml(),
item.getDescription(), tidyIconLink(b, item, item.getIconUrl()), config,
item.tags().getTags(), item.isDeprecated(), makeLinks(item));
}
protected static Map<String, URI> makeLinks(CatalogItem<?,?> item) {
return MutableMap.<String, URI>of().addIfNotNull("self", URI.create(getSelfLink(item)));
}
protected static String getSelfLink(CatalogItem<?,?> item) {
String itemId = item.getId();
switch (item.getCatalogItemType()) {
case TEMPLATE:
return "/v1/applications/" + itemId + "/" + item.getVersion();
case ENTITY:
return "/v1/entities/" + itemId + "/" + item.getVersion();
case POLICY:
return "/v1/policies/" + itemId + "/" + item.getVersion();
case LOCATION:
return "/v1/locations/" + itemId + "/" + item.getVersion();
default:
log.warn("Unexpected catalog item type when getting self link (not supplying self link): "+item.getCatalogItemType()+" "+item);
return null;
}
}
private static String tidyIconLink(BrooklynRestResourceUtils b, CatalogItem<?,?> item, String iconUrl) {
if (b.isUrlServerSideAndSafe(iconUrl))
return "/v1/catalog/icon/"+item.getSymbolicName() + "/" + item.getVersion();
return iconUrl;
}
private static Set<Object> makeTags(EntitySpec<?> spec, CatalogItem<?, ?> item) {
// Combine tags on item with an InterfacesTag.
Set<Object> tags = MutableSet.copyOf(item.tags().getTags());
if (spec != null) {
Class<?> type;
if (spec.getImplementation() != null) {
type = spec.getImplementation();
} else {
type = spec.getType();
}
if (type != null) {
tags.add(new BrooklynTags.TraitsTag(Reflections.getAllInterfaces(type)));
}
}
return tags;
}
}