Java tutorial
/* * 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 brooklyn.rest.resources; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import javax.annotation.Nullable; import javax.ws.rs.Consumes; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brooklyn.catalog.CatalogItem; import brooklyn.catalog.CatalogPredicates; import brooklyn.catalog.internal.BasicBrooklynCatalog; import brooklyn.catalog.internal.CatalogDto; import brooklyn.catalog.internal.CatalogItemComparator; import brooklyn.catalog.internal.CatalogUtils; import brooklyn.entity.Entity; import brooklyn.entity.proxying.EntitySpec; import brooklyn.management.entitlement.Entitlements; import brooklyn.management.entitlement.Entitlements.StringAndArgument; import brooklyn.policy.Policy; import brooklyn.policy.PolicySpec; import brooklyn.rest.api.CatalogApi; import brooklyn.rest.domain.ApiError; import brooklyn.rest.domain.CatalogEntitySummary; import brooklyn.rest.domain.CatalogItemSummary; import brooklyn.rest.transform.CatalogTransformer; import brooklyn.rest.util.WebResourceUtils; import brooklyn.util.ResourceUtils; import brooklyn.util.collections.MutableSet; import brooklyn.util.exceptions.Exceptions; import brooklyn.util.stream.Streams; import brooklyn.util.text.StringPredicates; import brooklyn.util.text.Strings; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.io.Files; import com.sun.jersey.core.header.FormDataContentDisposition; public class CatalogResource extends AbstractBrooklynRestResource implements CatalogApi { private static final Logger log = LoggerFactory.getLogger(CatalogResource.class); @SuppressWarnings("rawtypes") private final Function<CatalogItem, CatalogItemSummary> TO_CATALOG_ITEM_SUMMARY = new Function<CatalogItem, CatalogItemSummary>() { @Override public CatalogItemSummary apply(@Nullable CatalogItem input) { return CatalogTransformer.catalogItemSummary(brooklyn(), input); } }; @Override @Consumes(MediaType.MULTIPART_FORM_DATA) public Response createFromMultipart(InputStream uploadedInputStream, FormDataContentDisposition fileDetail) { return create(Streams.readFullyString(uploadedInputStream)); } static Set<String> missingIcons = MutableSet.of(); @SuppressWarnings("unchecked") @Override public Response create(String yaml) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ADD_CATALOG_ITEM, yaml)) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to add catalog item", Entitlements.getEntitlementContext().user()); } CatalogItem<?, ?> item; try { item = brooklyn().getCatalog().addItem(yaml); } catch (IllegalArgumentException e) { return Response.status(Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON).entity(ApiError.of(e)) .build(); } String itemId = item.getId(); log.info("REST created catalog item: " + item); // FIXME configurations/ not supported switch (item.getCatalogItemType()) { case TEMPLATE: return Response.created(URI.create("applications/" + itemId + "/" + item.getVersion())) .entity(CatalogTransformer.catalogEntitySummary(brooklyn(), (CatalogItem<? extends Entity, EntitySpec<?>>) item)) .build(); case ENTITY: return Response .created(URI.create("entities/" + itemId + "/" + item.getVersion())).entity(CatalogTransformer .catalogEntitySummary(brooklyn(), (CatalogItem<? extends Entity, EntitySpec<?>>) item)) .build(); case POLICY: return Response .created(URI.create("policies/" + itemId)).entity(CatalogTransformer .catalogPolicySummary(brooklyn(), (CatalogItem<? extends Policy, PolicySpec<?>>) item)) .build(); default: throw new IllegalStateException( "Unsupported catalog item type " + item.getCatalogItemType() + ": " + item); } } @Override public Response resetXml(String xml) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, null) || !Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ADD_CATALOG_ITEM, null)) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog", Entitlements.getEntitlementContext().user()); } ((BasicBrooklynCatalog) mgmt().getCatalog()).reset(CatalogDto.newDtoFromXmlContents(xml, "REST reset")); return Response.ok().build(); } @Override @Deprecated public void deleteEntity(String entityId) throws Exception { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(entityId, "delete"))) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog", Entitlements.getEntitlementContext().user()); } try { CatalogItem<?, ?> item = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), entityId); if (item == null) throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId); brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion()); } catch (NoSuchElementException e) { throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId); } } @Override public void deleteEntity(String entityId, String version) throws Exception { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(entityId + (Strings.isBlank(version) ? "" : ":" + version), "delete"))) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to modify catalog", Entitlements.getEntitlementContext().user()); } try { brooklyn().getCatalog().deleteCatalogItem(entityId, version); } catch (NoSuchElementException e) { throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", entityId, version); } } @Override public List<CatalogItemSummary> listEntities(String regex, String fragment) { return getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_ENTITY, regex, fragment); } @Override public List<CatalogItemSummary> listApplications(String regex, String fragment) { return getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_TEMPLATE, regex, fragment); } @Override @Deprecated public CatalogEntitySummary getEntity(String entityId) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, entityId)) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry", Entitlements.getEntitlementContext().user()); } CatalogItem<? extends Entity, EntitySpec<?>> result = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), Entity.class, entityId); if (result == null) { throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId); } return CatalogTransformer.catalogEntitySummary(brooklyn(), result); } @Override public CatalogEntitySummary getEntity(String entityId, String version) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, entityId + (Strings.isBlank(version) ? "" : ":" + version))) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry", Entitlements.getEntitlementContext().user()); } //TODO These casts are not pretty, we could just provide separate get methods for the different types? //Or we could provide asEntity/asPolicy cast methods on the CataloItem doing a safety check internally @SuppressWarnings("unchecked") CatalogItem<? extends Entity, EntitySpec<?>> result = (CatalogItem<? extends Entity, EntitySpec<?>>) brooklyn() .getCatalog().getCatalogItem(entityId, version); if (result == null) { throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", entityId, version); } return CatalogTransformer.catalogEntitySummary(brooklyn(), result); } @Override @Deprecated public CatalogEntitySummary getApplication(String entityId) throws Exception { return getEntity(entityId); } @Override public CatalogEntitySummary getApplication(String applicationId, String version) { return getEntity(applicationId, version); } @Override public List<CatalogItemSummary> listPolicies(String regex, String fragment) { return getCatalogItemSummariesMatchingRegexFragment(CatalogPredicates.IS_POLICY, regex, fragment); } @Override @Deprecated public CatalogItemSummary getPolicy(String policyId) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, policyId)) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry", Entitlements.getEntitlementContext().user()); } CatalogItem<? extends Policy, PolicySpec<?>> result = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), Policy.class, policyId); if (result == null) { throw WebResourceUtils.notFound("Policy with id '%s' not found", policyId); } return CatalogTransformer.catalogPolicySummary(brooklyn(), result); } @Override public CatalogItemSummary getPolicy(String policyId, String version) throws Exception { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, policyId + (Strings.isBlank(version) ? "" : ":" + version))) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry", Entitlements.getEntitlementContext().user()); } @SuppressWarnings("unchecked") CatalogItem<? extends Policy, PolicySpec<?>> result = (CatalogItem<? extends Policy, PolicySpec<?>>) brooklyn() .getCatalog().getCatalogItem(policyId, version); if (result == null) { throw WebResourceUtils.notFound("Policy with id '%s:%s' not found", policyId, version); } return CatalogTransformer.catalogPolicySummary(brooklyn(), result); } @SuppressWarnings({ "unchecked", "rawtypes" }) private <T, SpecT> List<CatalogItemSummary> getCatalogItemSummariesMatchingRegexFragment( Predicate<CatalogItem<T, SpecT>> type, String regex, String fragment) { List filters = new ArrayList(); filters.add(type); if (Strings.isNonEmpty(regex)) filters.add(CatalogPredicates.xml(StringPredicates.containsRegex(regex))); if (Strings.isNonEmpty(fragment)) filters.add(CatalogPredicates.xml(StringPredicates.containsLiteralIgnoreCase(fragment))); filters.add(CatalogPredicates.entitledToSee(mgmt())); ImmutableList<CatalogItem<Object, Object>> sortedItems = FluentIterable .from(brooklyn().getCatalog().getCatalogItems()).filter(Predicates.and(filters)) .toSortedList(CatalogItemComparator.getInstance()); return Lists.transform(sortedItems, TO_CATALOG_ITEM_SUMMARY); } @Override @Deprecated public Response getIcon(String itemId) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, itemId)) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry", Entitlements.getEntitlementContext().user()); } CatalogItem<?, ?> result = CatalogUtils.getCatalogItemOptionalVersion(mgmt(), itemId); return getCatalogItemIcon(result); } @Override public Response getIcon(String itemId, String version) { if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, itemId + (Strings.isBlank(version) ? "" : ":" + version))) { throw WebResourceUtils.unauthorized("User '%s' is not authorized to see catalog entry", Entitlements.getEntitlementContext().user()); } CatalogItem<?, ?> result = brooklyn().getCatalog().getCatalogItem(itemId, version); return getCatalogItemIcon(result); } private Response getCatalogItemIcon(CatalogItem<?, ?> result) { String url = result.getIconUrl(); if (url == null) { log.debug("No icon available for " + result + "; returning " + Status.NO_CONTENT); return Response.status(Status.NO_CONTENT).build(); } if (brooklyn().isUrlServerSideAndSafe(url)) { // classpath URL's we will serve IF they end with a recognised image format; // paths (ie non-protocol) and // NB, for security, file URL's are NOT served log.debug("Loading and returning " + url + " as icon for " + result); MediaType mime = WebResourceUtils.getImageMediaTypeFromExtension(Files.getFileExtension(url)); try { Object content = ResourceUtils.create(CatalogUtils.newClassLoadingContext(mgmt(), result)) .getResourceFromUrl(url); return Response.ok(content, mime).build(); } catch (Exception e) { Exceptions.propagateIfFatal(e); synchronized (missingIcons) { if (missingIcons.add(url)) { // note: this can be quite common when running from an IDE, as resources may not be copied; // a mvn build should sort it out (the IDE will then find the resources, until you clean or maybe refresh...) log.warn("Missing icon data for " + result.getId() + ", expected at: " + url + " (subsequent messages will log debug only)"); log.debug("Trace for missing icon data at " + url + ": " + e, e); } else { log.debug("Missing icon data for " + result.getId() + ", expected at: " + url + " (already logged WARN and error details)"); } } throw WebResourceUtils.notFound("Icon unavailable for %s", result.getId()); } } log.debug("Returning redirect to " + url + " as icon for " + result); // for anything else we do a redirect (e.g. http / https; perhaps ftp) return Response.temporaryRedirect(URI.create(url)).build(); } }