blob: 1a7ef611e99a185c270dadfb7d49c9e672162261 [file] [log] [blame]
/*
* 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.resources;
import static com.google.common.base.Preconditions.checkNotNull;
import static javax.ws.rs.core.Response.created;
import static javax.ws.rs.core.Response.status;
import static javax.ws.rs.core.Response.Status.ACCEPTED;
import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceAbsoluteUriBuilder;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;
import org.apache.brooklyn.api.entity.Application;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.entity.Group;
import org.apache.brooklyn.api.location.Location;
import org.apache.brooklyn.api.mgmt.Task;
import org.apache.brooklyn.api.objs.BrooklynObject;
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.api.sensor.Sensor;
import org.apache.brooklyn.api.typereg.RegisteredType;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigPredicates;
import org.apache.brooklyn.core.config.ConstraintViolationException;
import org.apache.brooklyn.core.entity.Attributes;
import org.apache.brooklyn.core.entity.EntityInternal;
import org.apache.brooklyn.core.entity.EntityPredicates;
import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
import org.apache.brooklyn.core.entity.trait.Startable;
import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
import org.apache.brooklyn.core.mgmt.EntityManagementUtils.CreationResult;
import org.apache.brooklyn.core.mgmt.entitlement.EntitlementPredicates;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.StringAndArgument;
import org.apache.brooklyn.core.mgmt.internal.IdAlreadyExistsException;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
import org.apache.brooklyn.core.typereg.RegisteredTypes;
import org.apache.brooklyn.entity.group.AbstractGroup;
import org.apache.brooklyn.rest.api.ApplicationApi;
import org.apache.brooklyn.rest.domain.ApplicationSpec;
import org.apache.brooklyn.rest.domain.ApplicationSummary;
import org.apache.brooklyn.rest.domain.EntityDetail;
import org.apache.brooklyn.rest.domain.EntitySummary;
import org.apache.brooklyn.rest.domain.TaskSummary;
import org.apache.brooklyn.rest.filter.HaHotStateRequired;
import org.apache.brooklyn.rest.transform.ApplicationTransformer;
import org.apache.brooklyn.rest.transform.EntityTransformer;
import org.apache.brooklyn.rest.transform.TaskTransformer;
import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
import org.apache.brooklyn.rest.util.WebResourceUtils;
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.UserFacingException;
import org.apache.brooklyn.util.guava.Maybe;
import org.apache.brooklyn.util.javalang.JavaClassNames;
import org.apache.brooklyn.util.net.Urls;
import org.apache.brooklyn.util.repeat.Repeater;
import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
import org.apache.brooklyn.util.text.Strings;
import org.apache.brooklyn.util.text.WildcardGlobs;
import org.apache.brooklyn.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@HaHotStateRequired
public class ApplicationResource extends AbstractBrooklynRestResource implements ApplicationApi {
private static final Logger log = LoggerFactory.getLogger(ApplicationResource.class);
@Context
private UriInfo uriInfo;
/** depth 0 means no detail even at root; negative means infinite; positive means include details for that many levels
* (ie 1 means this entity but no details of descendants) */
private EntitySummary fromEntity(Entity entity, boolean includeTags, int detailDepth, List<String> extraSensorGlobs, List<String> extraConfigGlobs) {
if (detailDepth==0) {
return new EntitySummary(
entity.getId(),
entity.getDisplayName(),
entity.getEntityType().getName(),
entity.getCatalogItemId(),
MutableMap.of("self", EntityTransformer.entityUri(entity, ui.getBaseUriBuilder())) );
}
Boolean serviceUp = entity.getAttribute(Attributes.SERVICE_UP);
Lifecycle serviceState = entity.getAttribute(Attributes.SERVICE_STATE_ACTUAL);
String iconUrl = RegisteredTypes.getIconUrl(entity);
if (iconUrl!=null) {
if (brooklyn().isUrlServerSideAndSafe(iconUrl))
// route to server if it is a server-side url
iconUrl = EntityTransformer.entityUri(entity, ui.getBaseUriBuilder())+"/icon";
}
List<EntitySummary> children = Lists.newArrayList();
if (!entity.getChildren().isEmpty()) {
for (Entity child : entity.getChildren()) {
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, child)) {
children.add(fromEntity(child, includeTags, detailDepth-1, extraSensorGlobs, extraConfigGlobs));
}
}
}
String parentId = null;
if (entity.getParent()!= null) {
parentId = entity.getParent().getId();
}
List<String> groupIds = Lists.newArrayList();
if (!entity.groups().isEmpty()) {
groupIds.addAll(entitiesIdAsArray(entity.groups()));
}
List<Map<String, String>> members = Lists.newArrayList();
if (entity instanceof Group) {
// use attribute instead of method in case it is read-only
Collection<Entity> memberEntities = entity.getAttribute(AbstractGroup.GROUP_MEMBERS);
if (memberEntities != null && !memberEntities.isEmpty())
members.addAll(entitiesIdAndNameAsList(memberEntities));
}
EntityDetail result = new EntityDetail(
entity.getApplicationId(),
entity.getId(),
parentId,
entity.getDisplayName(),
entity.getEntityType().getName(),
serviceUp,
serviceState,
iconUrl,
entity.getCatalogItemId(),
children,
groupIds,
members,
MutableMap.of("self", EntityTransformer.entityUri(entity, ui.getBaseUriBuilder())) );
if (includeTags) {
result.setExtraField("tags", resolving(MutableList.copyOf(entity.tags().getTags())).preferJson(true).resolve() );
}
result.setExtraField("creationTimeUtc", entity.getCreationTime());
addSensorsByGlobs(result, entity, extraSensorGlobs);
addConfigByGlobs(result, entity, extraConfigGlobs);
return result;
}
private List<Map<String, String>> entitiesIdAndNameAsList(Collection<? extends Entity> entities) {
List<Map<String, String>> members = Lists.newArrayList();
for (Entity entity : entities) {
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
members.add(ImmutableMap.of("id", entity.getId(), "name", entity.getDisplayName()));
}
}
return members;
}
private List<String> entitiesIdAsArray(Iterable<? extends Entity> entities) {
List<String> ids = Lists.newArrayList();
for (Entity entity : entities) {
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
ids.add(entity.getId());
}
}
return ids;
}
@Override
public List<EntityDetail> fetch(String entityIds, String extraSensorsS) {
List<String> extraSensorNames = JavaStringEscapes.unwrapOptionallyQuotedJavaStringList(extraSensorsS);
List<AttributeSensor<?>> extraSensors = extraSensorNames.stream().map((s) -> Sensors.newSensor(Object.class, s)).collect(Collectors.toList());
List<EntityDetail> entitySummaries = Lists.newArrayList();
for (Entity application : mgmt().getApplications()) {
entitySummaries.add(addSensorsByName((EntityDetail)fromEntity(application, false, -1, null, null), application, extraSensors));
}
if (Strings.isNonBlank(entityIds)) {
List<String> extraEntities = JavaStringEscapes.unwrapOptionallyQuotedJavaStringList(entityIds);
for (String entityId: extraEntities) {
Entity entity = mgmt().getEntityManager().getEntity(entityId.trim());
while (entity != null && entity.getParent() != null) {
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
entitySummaries.add(addSensorsByName((EntityDetail)fromEntity(entity, false, -1, null, null), entity, extraSensors));
}
entity = entity.getParent();
}
}
}
return entitySummaries;
}
@Override
public List<EntitySummary> details(String entityIds, boolean includeAllApps, String extraSensorsGlobsS, String extraConfigGlobsS, int depth) {
List<String> extraSensorGlobs = JavaStringEscapes.unwrapOptionallyQuotedJavaStringList(extraSensorsGlobsS);
List<String> extraConfigGlobs = JavaStringEscapes.unwrapOptionallyQuotedJavaStringList(extraConfigGlobsS);
Map<String, EntitySummary> entitySummaries = MutableMap.of();
if (includeAllApps) {
for (Entity application : mgmt().getApplications()) {
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, application)) {
entitySummaries.put(application.getId(), fromEntity(application, true, depth, extraSensorGlobs, extraConfigGlobs));
}
}
}
if (Strings.isNonBlank(entityIds)) {
List<String> extraEntities = JavaStringEscapes.unwrapOptionallyQuotedJavaStringList(entityIds);
for (String entityId: extraEntities) {
Entity entity = mgmt().getEntityManager().getEntity(entityId.trim());
while (entity != null && !entitySummaries.containsKey(entity.getId())) {
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
entitySummaries.put(entity.getId(), fromEntity(entity, true, depth, extraSensorGlobs, extraConfigGlobs));
}
entity = entity.getParent();
}
}
}
return MutableList.copyOf(entitySummaries.values());
}
private EntityDetail addSensorsByName(EntityDetail result, Entity entity, List<AttributeSensor<?>> extraSensors) {
if (extraSensors!=null && !extraSensors.isEmpty()) {
Object sensorsO = result.getExtraFields().get("sensors");
if (sensorsO==null) {
sensorsO = MutableMap.of();
result.setExtraField("sensors", sensorsO);
}
if (!(sensorsO instanceof Map)) {
throw new IllegalStateException("sensors field in result for "+entity+" should be a Map; found: "+sensorsO);
}
@SuppressWarnings("unchecked")
Map<String,Object> sensors = (Map<String, Object>) sensorsO;
for (AttributeSensor<?> s: extraSensors) {
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_SENSOR, new EntityAndItem<String>(entity, s.getName()))) {
Object sv = entity.sensors().get(s);
if (sv!=null) {
sv = resolving(sv).preferJson(true).asJerseyOutermostReturnValue(false).raw(true).context(entity).immediately(true).timeout(Duration.ZERO).resolve();
sensors.put(s.getName(), sv);
}
}
}
}
return result;
}
private EntitySummary addSensorsByGlobs(EntitySummary result, Entity entity, List<String> extraSensorGlobs) {
if (extraSensorGlobs!=null && !extraSensorGlobs.isEmpty()) {
Object sensorsO = result.getExtraFields().get("sensors");
if (sensorsO==null) {
sensorsO = MutableMap.of();
result.setExtraField("sensors", sensorsO);
}
if (!(sensorsO instanceof Map)) {
throw new IllegalStateException("sensors field in result for "+entity+" should be a Map; found: "+sensorsO);
}
@SuppressWarnings("unchecked")
Map<String,Object> sensors = (Map<String, Object>) sensorsO;
Map<AttributeSensor<?>, Object> svs = entity.sensors().getAll();
svs.entrySet().stream().forEach(kv -> {
Object sv = kv.getValue();
if (sv!=null) {
String name = kv.getKey().getName();
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_SENSOR, new EntityAndItem<String>(entity, name))) {
if (extraSensorGlobs.stream().anyMatch(sn -> WildcardGlobs.isGlobMatched(sn, name))) {
sv = resolving(sv).preferJson(true).asJerseyOutermostReturnValue(false).raw(true).context(entity).immediately(true).timeout(Duration.ZERO).resolve();
sensors.put(name, sv);
}
}
}
});
}
return result;
}
private EntitySummary addConfigByGlobs(EntitySummary result, Entity entity, List<String> extraConfigGlobs) {
if (extraConfigGlobs!=null && !extraConfigGlobs.isEmpty()) {
Object configO = result.getExtraFields().get("config");
if (configO==null) {
configO = MutableMap.of();
result.setExtraField("config", configO);
}
if (!(configO instanceof Map)) {
throw new IllegalStateException("config field in result for "+entity+" should be a Map; found: "+configO);
}
@SuppressWarnings("unchecked")
Map<String,Object> configs = (Map<String, Object>) configO;
List<Predicate<ConfigKey<?>>> extraConfigPreds = MutableList.of();
extraConfigGlobs.stream().forEach(g -> { extraConfigPreds.add( ConfigPredicates.nameMatchesGlob(g) ); });
entity.config().findKeysDeclared(Predicates.or(extraConfigPreds)).forEach(key -> {
if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CONFIG, new EntityAndItem<String>(entity, key.getName()))) {
Maybe<Object> vRaw = ((EntityInternal)entity).config().getRaw(key);
Object v = vRaw.isPresent() ? vRaw.get() : entity.config().get(key);
if (v!=null) {
v = resolving(v, mgmt()).preferJson(true).asJerseyOutermostReturnValue(false).raw(true).context(entity).immediately(true).timeout(Duration.ZERO).resolve();
configs.put(key.getName(), v);
}
}
});
}
return result;
}
@Override
public List<ApplicationSummary> list(String typeRegex) {
if (Strings.isBlank(typeRegex)) {
typeRegex = ".*";
}
return FluentIterable
.from(mgmt().getApplications())
.filter(EntitlementPredicates.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY))
.filter(EntityPredicates.hasInterfaceMatching(typeRegex))
.transform(ApplicationTransformer.fromApplication(ui.getBaseUriBuilder()))
.toList();
}
@Override
public ApplicationSummary get(String application) {
return ApplicationTransformer.summaryFromApplication(brooklyn().getApplication(application), ui.getBaseUriBuilder());
}
/** @deprecated since 0.7.0 see #create */ @Deprecated
protected Response createFromAppSpec(ApplicationSpec applicationSpec) {
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, applicationSpec)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to start application %s",
Entitlements.getEntitlementContext().user(), applicationSpec);
}
checkApplicationTypesAreValid(applicationSpec);
checkLocationsAreValid(applicationSpec);
List<Location> locations = brooklyn().getLocationsManaged(applicationSpec);
Application app = brooklyn().create(applicationSpec);
Task<?> t = brooklyn().start(app, locations);
waitForStart(app, Duration.millis(100));
TaskSummary ts = TaskTransformer.fromTask(ui.getBaseUriBuilder()).apply(t);
URI ref = serviceAbsoluteUriBuilder(uriInfo.getBaseUriBuilder(), ApplicationApi.class, "get")
.build(app.getApplicationId());
return created(ref).entity(ts).build();
}
@Override
public Response createFromYaml(String yaml) {
return createFromYaml(yaml, Optional.absent());
}
@Override
public Response createFromYamlWithAppId(String yaml, String appId) {
return createFromYaml(yaml, Optional.of(appId));
}
protected Response createFromYaml(String yaml, Optional<String> appId) {
// First of all, see if it's a URL
Preconditions.checkNotNull(yaml, "Blueprint must not be null");
URI uri = null;
try {
String yamlUrl = yaml.trim();
if (Urls.isUrlWithProtocol(yamlUrl)) {
uri = new URI(yamlUrl);
}
} catch (URISyntaxException e) {
// It's not a URI then...
uri = null;
}
if (uri != null) {
log.debug("Create app called with URI; retrieving contents: {}", uri);
try {
yaml = ResourceUtils.create(mgmt()).getResourceAsString(uri.toString());
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
throw new UserFacingException("Cannot resolve URL: "+uri, e);
}
}
log.debug("Creating app from yaml:\n{}", yaml);
EntitySpec<? extends Application> spec;
try {
spec = createEntitySpecForApplication(yaml);
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
log.warn("Failed REST deployment, could not create spec: "+e);
UserFacingException userFacing = Exceptions.getFirstThrowableOfType(e, UserFacingException.class);
if (userFacing!=null) {
log.debug("Throwing "+userFacing+", wrapped in "+e);
throw userFacing;
}
throw WebResourceUtils.badRequest(e, "Error in blueprint");
}
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, spec)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to start application %s",
Entitlements.getEntitlementContext().user(), yaml);
}
try {
return launch(yaml, spec, appId);
} catch (IdAlreadyExistsException e) {
log.warn("Failed REST deployment launching "+spec+": "+e);
throw WebResourceUtils.throwWebApplicationException(Response.Status.CONFLICT, e, "Error launching blueprint, id already exists");
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
log.warn("Failed REST deployment launching "+spec+": "+e);
throw WebResourceUtils.badRequest(e, "Error launching blueprint");
}
}
private Response launch(String yaml, EntitySpec<? extends Application> spec, Optional<String> entityId) {
try {
Application app = EntityManagementUtils.createUnstarted(mgmt(), spec, entityId);
boolean isEntitled = Entitlements.isEntitled(
mgmt().getEntitlementManager(),
Entitlements.INVOKE_EFFECTOR,
EntityAndItem.of(app, StringAndArgument.of(Startable.START.getName(), null)));
if (!isEntitled) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to start application %s",
Entitlements.getEntitlementContext().user(), spec.getType());
}
CreationResult<Application,Void> result = EntityManagementUtils.start(app);
waitForStart(app, Duration.millis(100));
log.info("Launched from YAML: " + yaml + " -> " + app + " (" + result.task() + ")");
URI ref = serviceAbsoluteUriBuilder(ui.getBaseUriBuilder(), ApplicationApi.class, "get").build(app.getApplicationId());
ResponseBuilder response = created(ref);
if (result.task() != null)
response.entity(TaskTransformer.fromTask(ui.getBaseUriBuilder()).apply(result.task()));
return response.build();
} catch (ConstraintViolationException e) {
throw new UserFacingException(e);
} catch (Exception e) {
throw Exceptions.propagate(e);
}
}
private void waitForStart(final Application app, Duration timeout) {
// without this UI shows "stopped" sometimes esp if brooklyn has just started,
// because app goes to stopped state if it sees unstarted children,
// and that gets returned too quickly, before the start has taken effect
Repeater.create("wait a bit for app start").every(Duration.millis(10)).limitTimeTo(timeout).until(
new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
Lifecycle state = app.getAttribute(Attributes.SERVICE_STATE_ACTUAL);
if (state==Lifecycle.CREATED || state==Lifecycle.STOPPED) return false;
return true;
}
}).run();
}
@Override
public Response createPoly(byte[] inputToAutodetectType) {
log.debug("Creating app from autodetecting input");
boolean looksLikeLegacy = false;
Exception legacyFormatException = null;
// attempt legacy format
try {
ApplicationSpec appSpec = mapper().readValue(inputToAutodetectType, ApplicationSpec.class);
if (appSpec.getType() != null || appSpec.getEntities() != null) {
looksLikeLegacy = true;
}
return createFromAppSpec(appSpec);
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
legacyFormatException = e;
log.debug("Input is not legacy ApplicationSpec JSON (will try others)");
}
//TODO infer encoding from request
String potentialYaml = new String(inputToAutodetectType);
EntitySpec<? extends Application> spec;
try {
spec = createEntitySpecForApplication(potentialYaml);
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
log.warn("Failed REST deployment, could not create spec (autodetecting): "+e);
// TODO if not yaml/json - try ZIP, etc
throw WebResourceUtils.badRequest(e, "Error in blueprint");
}
if (spec != null) {
try {
return launch(potentialYaml, spec, Optional.absent());
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
log.warn("Failed REST deployment launching "+spec+": "+e);
throw WebResourceUtils.badRequest(e, "Error launching blueprint (autodetecting)");
}
} else if (looksLikeLegacy) {
throw Exceptions.propagate(legacyFormatException);
} else {
return Response.serverError().entity("Unsupported format; not able to autodetect.").build();
}
}
@Override
public Response createFromForm(String contents) {
log.debug("Creating app from form");
return createPoly(contents.getBytes());
}
@Override
public Response delete(String application) {
Application app = brooklyn().getApplication(application);
if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, Entitlements.EntityAndItem.of(app,
StringAndArgument.of(Entitlements.LifecycleEffectors.DELETE, null)))) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to delete application %s",
Entitlements.getEntitlementContext().user(), app);
}
Task<?> t = brooklyn().destroy(app);
TaskSummary ts = TaskTransformer.fromTask(ui.getBaseUriBuilder()).apply(t);
return status(ACCEPTED).entity(ts).build();
}
private EntitySpec<? extends Application> createEntitySpecForApplication(String potentialYaml) {
return EntityManagementUtils.createEntitySpecForApplication(mgmt(), potentialYaml);
}
private void checkApplicationTypesAreValid(ApplicationSpec applicationSpec) {
String appType = applicationSpec.getType();
if (appType != null) {
checkEntityTypeIsValid(appType);
if (applicationSpec.getEntities() != null) {
throw WebResourceUtils.preconditionFailed("Application given explicit type '%s' must not define entities", appType);
}
return;
}
for (org.apache.brooklyn.rest.domain.EntitySpec entitySpec : applicationSpec.getEntities()) {
String entityType = entitySpec.getType();
checkEntityTypeIsValid(checkNotNull(entityType, "entityType"));
}
}
private void checkSpecTypeIsValid(String type, Class<? extends BrooklynObject> subType) {
Maybe<RegisteredType> typeV = RegisteredTypes.tryValidate(mgmt().getTypeRegistry().get(type), RegisteredTypeLoadingContexts.spec(subType));
if (!typeV.isNull()) {
// found, throw if any problem
typeV.get();
return;
}
// not found, try classloading
try {
brooklyn().getCatalogClassLoader().loadClass(type);
} catch (ClassNotFoundException e) {
log.debug("Class not found for type '" + type + "'; reporting 404", e);
throw WebResourceUtils.notFound("Undefined type '%s'", type);
}
log.info(JavaClassNames.simpleClassName(subType)+" type '{}' not defined in catalog but is on classpath; continuing", type);
}
private void checkEntityTypeIsValid(String type) {
checkSpecTypeIsValid(type, Entity.class);
}
@SuppressWarnings("deprecation")
private void checkLocationsAreValid(ApplicationSpec applicationSpec) {
for (String locationId : applicationSpec.getLocations()) {
locationId = BrooklynRestResourceUtils.fixLocation(locationId);
if (brooklyn().getLocationRegistry().getLocationSpec(locationId).isAbsent() && brooklyn().getLocationRegistry().getDefinedLocationById(locationId)==null) {
throw WebResourceUtils.notFound("Undefined location '%s'", locationId);
}
}
}
@Override
public List<EntitySummary> getDescendants(String application, String typeRegex) {
Entity entity =brooklyn().getApplication(application);
if (entity != null && !Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to see the descendants of entity '%s'",
Entitlements.getEntitlementContext().user(), entity);
}
return EntityTransformer.entitySummaries(brooklyn().descendantsOfType(application, application, typeRegex), ui.getBaseUriBuilder());
}
@Override
public Map<String, Object> getDescendantsSensor(String application, String sensor, String typeRegex) {
Entity entity =brooklyn().getApplication(application);
if (entity != null && !Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
throw WebResourceUtils.forbidden("User '%s' is not authorized to see the value of sensor '%s' for descendants of entity '%s'",
Entitlements.getEntitlementContext().user(), sensor, entity);
}
Iterable<Entity> descs = brooklyn().descendantsOfType(application, application, typeRegex);
return getSensorMap(sensor, descs);
}
public static Map<String, Object> getSensorMap(String sensor, Iterable<Entity> descs) {
if (Iterables.isEmpty(descs))
return Collections.emptyMap();
Map<String, Object> result = MutableMap.of();
Iterator<Entity> di = descs.iterator();
Sensor<?> s = null;
while (di.hasNext()) {
Entity potentialSource = di.next();
s = potentialSource.getEntityType().getSensor(sensor);
if (s!=null) break;
}
if (s==null)
s = Sensors.newSensor(Object.class, sensor);
if (!(s instanceof AttributeSensor<?>)) {
log.warn("Cannot retrieve non-attribute sensor "+s+" for entities; returning empty map");
return result;
}
for (Entity e: descs) {
Object v = null;
try {
v = e.getAttribute((AttributeSensor<?>)s);
} catch (Exception exc) {
Exceptions.propagateIfFatal(exc);
log.warn("Error retrieving sensor "+s+" for "+e+" (ignoring): "+exc);
}
if (v!=null)
result.put(e.getId(), v);
}
return result;
}
}