http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java deleted file mode 100644 index 829b7cb..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtils.java +++ /dev/null @@ -1,608 +0,0 @@ -/* - * 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.util; - -import static com.google.common.collect.Iterables.transform; -import static org.apache.brooklyn.rest.util.WebResourceUtils.notFound; - -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.concurrent.Future; - -import javax.ws.rs.core.MediaType; - -import org.apache.brooklyn.api.catalog.BrooklynCatalog; -import org.apache.brooklyn.api.catalog.CatalogItem; -import org.apache.brooklyn.api.entity.Application; -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.location.LocationRegistry; -import org.apache.brooklyn.api.mgmt.ManagementContext; -import org.apache.brooklyn.api.mgmt.Task; -import org.apache.brooklyn.api.policy.Policy; -import org.apache.brooklyn.api.typereg.RegisteredType; -import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants; -import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent; -import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.catalog.CatalogPredicates; -import org.apache.brooklyn.core.catalog.internal.CatalogItemComparator; -import org.apache.brooklyn.core.catalog.internal.CatalogUtils; -import org.apache.brooklyn.core.entity.Attributes; -import org.apache.brooklyn.core.entity.Entities; -import org.apache.brooklyn.core.entity.EntityInternal; -import org.apache.brooklyn.core.entity.trait.Startable; -import org.apache.brooklyn.core.mgmt.BrooklynTaskTags; -import org.apache.brooklyn.core.mgmt.entitlement.Entitlements; -import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.StringAndArgument; -import org.apache.brooklyn.core.objs.BrooklynTypes; -import org.apache.brooklyn.core.typereg.RegisteredTypes; -import org.apache.brooklyn.enricher.stock.Enrichers; -import org.apache.brooklyn.entity.stock.BasicApplication; -import org.apache.brooklyn.rest.domain.ApplicationSpec; -import org.apache.brooklyn.rest.domain.EntitySpec; -import org.apache.brooklyn.util.collections.MutableMap; -import org.apache.brooklyn.util.collections.MutableSet; -import org.apache.brooklyn.util.core.flags.TypeCoercions; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.javalang.Reflections; -import org.apache.brooklyn.util.net.Urls; -import org.apache.brooklyn.util.text.Strings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Function; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSortedSet; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.io.Files; - -public class BrooklynRestResourceUtils { - - private static final Logger log = LoggerFactory.getLogger(BrooklynRestResourceUtils.class); - - private final ManagementContext mgmt; - - public BrooklynRestResourceUtils(ManagementContext mgmt) { - Preconditions.checkNotNull(mgmt, "mgmt"); - this.mgmt = mgmt; - } - - public BrooklynCatalog getCatalog() { - return mgmt.getCatalog(); - } - - public ClassLoader getCatalogClassLoader() { - return mgmt.getCatalogClassLoader(); - } - - public LocationRegistry getLocationRegistry() { - return mgmt.getLocationRegistry(); - } - - /** finds the policy indicated by the given ID or name. - * @see {@link #getEntity(String,String)}; it then searches the policies of that - * entity for one whose ID or name matches that given. - * <p> - * - * @throws 404 or 412 (unless input is null in which case output is null) */ - public Policy getPolicy(String application, String entity, String policy) { - return getPolicy(getEntity(application, entity), policy); - } - - /** finds the policy indicated by the given ID or name. - * @see {@link #getPolicy(String,String,String)}. - * <p> - * - * @throws 404 or 412 (unless input is null in which case output is null) */ - public Policy getPolicy(Entity entity, String policy) { - if (policy==null) return null; - - for (Policy p: entity.policies()) { - if (policy.equals(p.getId())) return p; - } - for (Policy p: entity.policies()) { - if (policy.equals(p.getDisplayName())) return p; - } - - throw WebResourceUtils.notFound("Cannot find policy '%s' in entity '%s'", policy, entity); - } - - /** finds the entity indicated by the given ID or name - * <p> - * prefers ID based lookup in which case appId is optional, and if supplied will be enforced. - * optionally the name can be supplied, for cases when paths should work across versions, - * in which case names will be searched recursively (and the application is required). - * - * @throws 404 or 412 (unless input is null in which case output is null) */ - public Entity getEntity(String application, String entity) { - if (entity==null) return null; - Application app = application!=null ? getApplication(application) : null; - Entity e = mgmt.getEntityManager().getEntity(entity); - - if (e!=null) { - if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, e)) { - throw WebResourceUtils.notFound("Cannot find entity '%s': no known ID and application not supplied for searching", entity); - } - - if (app==null || app.equals(findTopLevelApplication(e))) return e; - throw WebResourceUtils.preconditionFailed("Application '%s' specified does not match application '%s' to which entity '%s' (%s) is associated", - application, e.getApplication()==null ? null : e.getApplication().getId(), entity, e); - } - if (application==null) - throw WebResourceUtils.notFound("Cannot find entity '%s': no known ID and application not supplied for searching", entity); - - assert app!=null : "null app should not be returned from getApplication"; - e = searchForEntityNamed(app, entity); - if (e!=null) return e; - throw WebResourceUtils.notFound("Cannot find entity '%s' in application '%s' (%s)", entity, application, app); - } - - private Application findTopLevelApplication(Entity e) { - // For nested apps, e.getApplication() can return its direct parent-app rather than the root app - // (particularly if e.getApplication() was called before the parent-app was wired up to its parent, - // because that call causes the application to be cached). - // Therefore we continue to walk the hierarchy until we find an "orphaned" application at the top. - - Application app = e.getApplication(); - while (app != null && !app.equals(app.getApplication())) { - app = app.getApplication(); - } - return app; - } - - /** looks for the given application instance, first by ID then by name - * - * @throws 404 if not found, or not entitled - */ - public Application getApplication(String application) { - Entity e = mgmt.getEntityManager().getEntity(application); - if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.SEE_ENTITY, e)) { - throw notFound("Application '%s' not found", application); - } - - if (e != null && e instanceof Application) return (Application) e; - for (Application app : mgmt.getApplications()) { - if (app.getId().equals(application)) return app; - if (application.equalsIgnoreCase(app.getDisplayName())) return app; - } - - throw notFound("Application '%s' not found", application); - } - - /** walks the hierarchy (depth-first) at root (often an Application) looking for - * an entity matching the given ID or name; returns the first such entity, or null if none found - **/ - public Entity searchForEntityNamed(Entity root, String entity) { - if (root.getId().equals(entity) || entity.equals(root.getDisplayName())) return root; - for (Entity child: root.getChildren()) { - Entity result = searchForEntityNamed(child, entity); - if (result!=null) return result; - } - return null; - } - - private class FindItemAndClass { - String catalogItemId; - Class<? extends Entity> clazz; - - @SuppressWarnings({ "unchecked" }) - private FindItemAndClass inferFrom(String type) { - RegisteredType item = mgmt.getTypeRegistry().get(type); - if (item==null) { - // deprecated attempt to load an item not in the type registry - - // although the method called was deprecated in 0.7.0, its use here was not warned until 0.9.0; - // therefore this behaviour should not be changed until after 0.9.0; - // at which point it should try a pojo load (see below) - item = getCatalogItemForType(type); - if (item!=null) { - log.warn("Creating application for requested type `"+type+" using item "+item+"; " - + "the registered type name ("+item.getSymbolicName()+") should be used from the spec instead, " - + "or the type registered under its own name. " - + "Future versions will likely change semantics to attempt a POJO load of the type instead."); - } - } - - if (item != null) { - return setAs( - mgmt.getTypeRegistry().createSpec(item, null, org.apache.brooklyn.api.entity.EntitySpec.class).getType(), - item.getId()); - } else { - try { - setAs( - (Class<? extends Entity>) getCatalog().getRootClassLoader().loadClass(type), - null); - log.info("Catalog does not contain item for type {}; loaded class directly instead", type); - return this; - } catch (ClassNotFoundException e2) { - log.warn("No catalog item for type {}, and could not load class directly; rethrowing", type); - throw new NoSuchElementException("Unable to find catalog item for type "+type); - } - } - } - - private FindItemAndClass setAs(Class<? extends Entity> clazz, String catalogItemId) { - this.clazz = clazz; - this.catalogItemId = catalogItemId; - return this; - } - - @Deprecated // see caller - private RegisteredType getCatalogItemForType(String typeName) { - final RegisteredType resultI; - if (CatalogUtils.looksLikeVersionedId(typeName)) { - //All catalog identifiers of the form xxxx:yyyy are composed of symbolicName+version. - //No javaType is allowed as part of the identifier. - resultI = mgmt.getTypeRegistry().get(typeName); - } else { - //Usually for catalog items with javaType (that is items from catalog.xml) - //the symbolicName and javaType match because symbolicName (was ID) - //is not specified explicitly. But could be the case that there is an item - //whose symbolicName is explicitly set to be different from the javaType. - //Note that in the XML the attribute is called registeredTypeName. - Iterable<CatalogItem<Object,Object>> resultL = mgmt.getCatalog().getCatalogItems(CatalogPredicates.javaType(Predicates.equalTo(typeName))); - if (!Iterables.isEmpty(resultL)) { - //Push newer versions in front of the list (not that there should - //be more than one considering the items are coming from catalog.xml). - resultI = RegisteredTypes.of(sortVersionsDesc(resultL).iterator().next()); - if (log.isDebugEnabled() && Iterables.size(resultL)>1) { - log.debug("Found "+Iterables.size(resultL)+" matches in catalog for type "+typeName+"; returning the result with preferred version, "+resultI); - } - } else { - //As a last resort try searching for items with the same symbolicName supposedly - //different from the javaType. - resultI = mgmt.getTypeRegistry().get(typeName, BrooklynCatalog.DEFAULT_VERSION); - if (resultI != null) { - if (resultI.getSuperTypes().isEmpty()) { - //Catalog items scanned from the classpath (using reflection and annotations) now - //get yaml spec rather than a java type. Can't use those when creating apps from - //the legacy app spec format. - log.warn("Unable to find catalog item for type "+typeName + - ". There is an existing catalog item with ID " + resultI.getId() + - " but it doesn't define a class type."); - return null; - } - } - } - } - return resultI; - } - private <T,SpecT> Collection<CatalogItem<T,SpecT>> sortVersionsDesc(Iterable<CatalogItem<T,SpecT>> versions) { - return ImmutableSortedSet.orderedBy(CatalogItemComparator.<T,SpecT>getInstance()).addAll(versions).build(); - } - } - - @SuppressWarnings({ "deprecation" }) - public Application create(ApplicationSpec spec) { - log.warn("Using deprecated functionality (as of 0.9.0), ApplicationSpec style (pre CAMP plans). " + - "Transition to actively supported spec plans."); - log.debug("REST creating application instance for {}", spec); - - if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, spec)) { - throw WebResourceUtils.forbidden("User '%s' is not authorized to deploy application %s", - Entitlements.getEntitlementContext().user(), spec); - } - - final String type = spec.getType(); - final String name = spec.getName(); - final Map<String,String> configO = spec.getConfig(); - final Set<EntitySpec> entities = (spec.getEntities() == null) ? ImmutableSet.<EntitySpec>of() : spec.getEntities(); - - final Application instance; - - // Load the class; first try to use the appropriate catalog item; but then allow anything that is on the classpath - FindItemAndClass itemAndClass; - if (Strings.isEmpty(type)) { - itemAndClass = new FindItemAndClass().setAs(BasicApplication.class, null); - } else { - itemAndClass = new FindItemAndClass().inferFrom(type); - } - - if (!Entitlements.isEntitled(mgmt.getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, null)) { - throw WebResourceUtils.forbidden("User '%s' is not authorized to create application from applicationSpec %s", - Entitlements.getEntitlementContext().user(), spec); - } - - try { - if (org.apache.brooklyn.core.entity.factory.ApplicationBuilder.class.isAssignableFrom(itemAndClass.clazz)) { - // warning only added in 0.9.0 - log.warn("Using deprecated ApplicationBuilder "+itemAndClass.clazz+"; callers must migrate to use of Application"); - Constructor<?> constructor = itemAndClass.clazz.getConstructor(); - org.apache.brooklyn.core.entity.factory.ApplicationBuilder appBuilder = (org.apache.brooklyn.core.entity.factory.ApplicationBuilder) constructor.newInstance(); - if (!Strings.isEmpty(name)) appBuilder.appDisplayName(name); - if (entities.size() > 0) - log.warn("Cannot supply additional entities when using an ApplicationBuilder; ignoring in spec {}", spec); - - log.info("REST placing '{}' under management", spec.getName()); - appBuilder.configure(convertFlagsToKeys(appBuilder.getType(), configO)); - configureRenderingMetadata(spec, appBuilder); - instance = appBuilder.manage(mgmt); - - } else if (Application.class.isAssignableFrom(itemAndClass.clazz)) { - org.apache.brooklyn.api.entity.EntitySpec<?> coreSpec = toCoreEntitySpec(itemAndClass.clazz, name, configO, itemAndClass.catalogItemId); - configureRenderingMetadata(spec, coreSpec); - for (EntitySpec entitySpec : entities) { - log.info("REST creating instance for entity {}", entitySpec.getType()); - coreSpec.child(toCoreEntitySpec(entitySpec)); - } - - log.info("REST placing '{}' under management", spec.getName() != null ? spec.getName() : spec); - instance = (Application) mgmt.getEntityManager().createEntity(coreSpec); - - } else if (Entity.class.isAssignableFrom(itemAndClass.clazz)) { - if (entities.size() > 0) - log.warn("Cannot supply additional entities when using a non-application entity; ignoring in spec {}", spec); - - org.apache.brooklyn.api.entity.EntitySpec<?> coreSpec = toCoreEntitySpec(BasicApplication.class, name, configO, itemAndClass.catalogItemId); - configureRenderingMetadata(spec, coreSpec); - - coreSpec.child(toCoreEntitySpec(itemAndClass.clazz, name, configO, itemAndClass.catalogItemId) - .configure(BrooklynCampConstants.PLAN_ID, "soleChildId")); - coreSpec.enricher(Enrichers.builder() - .propagatingAllBut(Attributes.SERVICE_UP, Attributes.SERVICE_NOT_UP_INDICATORS, - Attributes.SERVICE_STATE_ACTUAL, Attributes.SERVICE_STATE_EXPECTED, - Attributes.SERVICE_PROBLEMS) - .from(new DslComponent(Scope.CHILD, "soleChildId").newTask()) - .build()); - - log.info("REST placing '{}' under management", spec.getName()); - instance = (Application) mgmt.getEntityManager().createEntity(coreSpec); - - } else { - throw new IllegalArgumentException("Class " + itemAndClass.clazz + " must extend one of ApplicationBuilder, Application or Entity"); - } - - return instance; - - } catch (Exception e) { - log.error("REST failed to create application: " + e, e); - throw Exceptions.propagate(e); - } - } - - public Task<?> start(Application app, ApplicationSpec spec) { - return start(app, getLocations(spec)); - } - - public Task<?> start(Application app, List<? extends Location> locations) { - return Entities.invokeEffector(app, app, Startable.START, - MutableMap.of("locations", locations)); - } - - public List<Location> getLocations(ApplicationSpec spec) { - // Start all the managed entities by asking the app instance to start in background - Function<String, Location> buildLocationFromId = new Function<String, Location>() { - @Override - public Location apply(String id) { - id = fixLocation(id); - return getLocationRegistry().resolve(id); - } - }; - - ArrayList<Location> locations = Lists.newArrayList(transform(spec.getLocations(), buildLocationFromId)); - return locations; - } - - private org.apache.brooklyn.api.entity.EntitySpec<? extends Entity> toCoreEntitySpec(org.apache.brooklyn.rest.domain.EntitySpec spec) { - String type = spec.getType(); - String name = spec.getName(); - Map<String, String> config = (spec.getConfig() == null) ? Maps.<String,String>newLinkedHashMap() : Maps.newLinkedHashMap(spec.getConfig()); - - FindItemAndClass itemAndClass = new FindItemAndClass().inferFrom(type); - - final Class<? extends Entity> clazz = itemAndClass.clazz; - org.apache.brooklyn.api.entity.EntitySpec<? extends Entity> result; - if (clazz.isInterface()) { - result = org.apache.brooklyn.api.entity.EntitySpec.create(clazz); - } else { - result = org.apache.brooklyn.api.entity.EntitySpec.create(Entity.class).impl(clazz).additionalInterfaces(Reflections.getAllInterfaces(clazz)); - } - result.catalogItemId(itemAndClass.catalogItemId); - if (!Strings.isEmpty(name)) result.displayName(name); - result.configure( convertFlagsToKeys(result.getType(), config) ); - configureRenderingMetadata(spec, result); - return result; - } - - @SuppressWarnings("deprecation") - protected void configureRenderingMetadata(ApplicationSpec spec, org.apache.brooklyn.core.entity.factory.ApplicationBuilder appBuilder) { - appBuilder.configure(getRenderingConfigurationFor(spec.getType())); - } - - protected void configureRenderingMetadata(ApplicationSpec input, org.apache.brooklyn.api.entity.EntitySpec<?> entity) { - entity.configure(getRenderingConfigurationFor(input.getType())); - } - - protected void configureRenderingMetadata(EntitySpec input, org.apache.brooklyn.api.entity.EntitySpec<?> entity) { - entity.configure(getRenderingConfigurationFor(input.getType())); - } - - protected Map<?, ?> getRenderingConfigurationFor(String catalogId) { - MutableMap<Object, Object> result = MutableMap.of(); - RegisteredType item = mgmt.getTypeRegistry().get(catalogId); - if (item==null) return result; - - result.addIfNotNull("iconUrl", item.getIconUrl()); - return result; - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private <T extends Entity> org.apache.brooklyn.api.entity.EntitySpec<?> toCoreEntitySpec(Class<T> clazz, String name, Map<String,String> configO, String catalogItemId) { - Map<String, String> config = (configO == null) ? Maps.<String,String>newLinkedHashMap() : Maps.newLinkedHashMap(configO); - - org.apache.brooklyn.api.entity.EntitySpec<? extends Entity> result; - if (clazz.isInterface()) { - result = org.apache.brooklyn.api.entity.EntitySpec.create(clazz); - } else { - // If this is a concrete class, particularly for an Application class, we want the proxy - // to expose all interfaces it implements. - Class interfaceclazz = (Application.class.isAssignableFrom(clazz)) ? Application.class : Entity.class; - Set<Class<?>> additionalInterfaceClazzes = Reflections.getInterfacesIncludingClassAncestors(clazz); - result = org.apache.brooklyn.api.entity.EntitySpec.create(interfaceclazz).impl(clazz).additionalInterfaces(additionalInterfaceClazzes); - } - - result.catalogItemId(catalogItemId); - if (!Strings.isEmpty(name)) result.displayName(name); - result.configure( convertFlagsToKeys(result.getImplementation(), config) ); - return result; - } - - private Map<?,?> convertFlagsToKeys(Class<? extends Entity> javaType, Map<?, ?> config) { - if (config==null || config.isEmpty() || javaType==null) return config; - - Map<String, ConfigKey<?>> configKeys = BrooklynTypes.getDefinedConfigKeys(javaType); - Map<Object,Object> result = new LinkedHashMap<Object,Object>(); - for (Map.Entry<?,?> entry: config.entrySet()) { - log.debug("Setting key {} to {} for REST creation of {}", new Object[] { entry.getKey(), entry.getValue(), javaType}); - Object key = configKeys.get(entry.getKey()); - if (key==null) { - log.warn("Unrecognised config key {} passed to {}; will be treated as flag (and likely ignored)", entry.getKey(), javaType); - key = entry.getKey(); - } - result.put(key, entry.getValue()); - } - return result; - } - - public Task<?> destroy(final Application application) { - return mgmt.getExecutionManager().submit( - MutableMap.of("displayName", "destroying "+application, - "description", "REST call to destroy application "+application.getDisplayName()+" ("+application+")"), - new Runnable() { - @Override - public void run() { - ((EntityInternal)application).destroy(); - mgmt.getEntityManager().unmanage(application); - } - }); - } - - public Task<?> expunge(final Entity entity, final boolean release) { - if (mgmt.getEntitlementManager().isEntitled(Entitlements.getEntitlementContext(), - Entitlements.INVOKE_EFFECTOR, Entitlements.EntityAndItem.of(entity, - StringAndArgument.of("expunge", MutableMap.of("release", release))))) { - Map<String, Object> flags = MutableMap.<String, Object>of("displayName", "expunging " + entity, "description", "REST call to expunge entity " - + entity.getDisplayName() + " (" + entity + ")"); - if (Entitlements.getEntitlementContext() != null) { - flags.put("tags", MutableSet.of(BrooklynTaskTags.tagForEntitlement(Entitlements.getEntitlementContext()))); - } - return mgmt.getExecutionManager().submit( - flags, new Runnable() { - @Override - public void run() { - if (release) - Entities.destroyCatching(entity); - else - mgmt.getEntityManager().unmanage(entity); - } - }); - } - throw WebResourceUtils.forbidden("User '%s' is not authorized to expunge entity %s", - Entitlements.getEntitlementContext().user(), entity); - } - - @Deprecated - public static String fixLocation(String locationId) { - if (locationId.startsWith("/v1/locations/")) { - log.warn("REST API using legacy URI syntax for location: "+locationId); - locationId = Strings.removeFromStart(locationId, "/v1/locations/"); - } - return locationId; - } - - public Object getObjectValueForDisplay(Object value) { - if (value==null) return null; - // currently everything converted to string, expanded if it is a "done" future - if (value instanceof Future) { - if (((Future<?>)value).isDone()) { - try { - value = ((Future<?>)value).get(); - } catch (Exception e) { - value = ""+value+" (error evaluating: "+e+")"; - } - } - } - - if (TypeCoercions.isPrimitiveOrBoxer(value.getClass())) return value; - return value.toString(); - } - - // currently everything converted to string, expanded if it is a "done" future - public String getStringValueForDisplay(Object value) { - if (value==null) return null; - return ""+getObjectValueForDisplay(value); - } - - /** true if the URL points to content which must be resolved on the server-side (i.e. classpath) - * and which is safe to do so (currently just images, though in future perhaps also javascript and html plugins) - * <p> - * note we do not let caller access classpath through this mechanism, - * just those which are supplied by the platform administrator e.g. as an icon url */ - public boolean isUrlServerSideAndSafe(String url) { - if (Strings.isEmpty(url)) return false; - String ext = Files.getFileExtension(url); - if (Strings.isEmpty(ext)) return false; - MediaType mime = WebResourceUtils.getImageMediaTypeFromExtension(ext); - if (mime==null) return false; - - return !Urls.isUrlWithProtocol(url) || url.startsWith("classpath:"); - } - - - public Iterable<Entity> descendantsOfAnyType(String application, String entity) { - List<Entity> result = Lists.newArrayList(); - Entity e = getEntity(application, entity); - gatherAllDescendants(e, result); - return result; - } - - private static void gatherAllDescendants(Entity e, List<Entity> result) { - if (result.add(e)) { - for (Entity ee: e.getChildren()) - gatherAllDescendants(ee, result); - } - } - - public Iterable<Entity> descendantsOfType(String application, String entity, final String typeRegex) { - Iterable<Entity> result = descendantsOfAnyType(application, entity); - return Iterables.filter(result, new Predicate<Entity>() { - @Override - public boolean apply(Entity entity) { - if (entity==null) return false; - return (entity.getEntityType().getName().matches(typeRegex)); - } - }); - } - - public void reloadBrooklynProperties() { - mgmt.reloadBrooklynProperties(); - } -}
http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java deleted file mode 100644 index 978755b..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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.util; - -import java.util.Set; - -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.yaml.snakeyaml.error.YAMLException; -import org.apache.brooklyn.core.mgmt.entitlement.Entitlements; -import org.apache.brooklyn.rest.domain.ApiError; -import org.apache.brooklyn.rest.domain.ApiError.Builder; -import org.apache.brooklyn.util.collections.MutableSet; -import org.apache.brooklyn.util.core.flags.ClassCoercionException; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.exceptions.UserFacingException; -import org.apache.brooklyn.util.text.Strings; - -@Provider -public class DefaultExceptionMapper implements ExceptionMapper<Throwable> { - - private static final Logger LOG = LoggerFactory.getLogger(DefaultExceptionMapper.class); - - static Set<Class<?>> warnedUnknownExceptions = MutableSet.of(); - - /** - * Maps a throwable to a response. - * <p/> - * Returns {@link WebApplicationException#getResponse} if the exception is an instance of - * {@link WebApplicationException}. Otherwise maps known exceptions to responses. If no - * mapping is found a {@link Status#INTERNAL_SERVER_ERROR} is assumed. - */ - @Override - public Response toResponse(Throwable throwable1) { - - LOG.debug("REST request running as {} threw: {}", Entitlements.getEntitlementContext(), - Exceptions.collapse(throwable1)); - if (LOG.isTraceEnabled()) { - LOG.trace("Full details of "+Entitlements.getEntitlementContext()+" "+throwable1, throwable1); - } - - Throwable throwable2 = Exceptions.getFirstInteresting(throwable1); - // Some methods will throw this, which gets converted automatically - if (throwable2 instanceof WebApplicationException) { - WebApplicationException wae = (WebApplicationException) throwable2; - return wae.getResponse(); - } - - // The nicest way for methods to provide errors, wrap as this, and the stack trace will be suppressed - if (throwable2 instanceof UserFacingException) { - return ApiError.of(throwable2.getMessage()).asBadRequestResponseJson(); - } - - // For everything else, a trace is supplied - - // Assume ClassCoercionExceptions are caused by TypeCoercions from input parameters gone wrong - // And IllegalArgumentException for malformed input parameters. - if (throwable2 instanceof ClassCoercionException || throwable2 instanceof IllegalArgumentException) { - return ApiError.of(throwable2).asBadRequestResponseJson(); - } - - // YAML exception - if (throwable2 instanceof YAMLException) { - return ApiError.builder().message(throwable2.getMessage()).prefixMessage("Invalid YAML").build().asBadRequestResponseJson(); - } - - if (!Exceptions.isPrefixBoring(throwable2)) { - if ( warnedUnknownExceptions.add( throwable2.getClass() )) { - LOG.warn("REST call generated exception type "+throwable2.getClass()+" unrecognized in "+getClass()+" (subsequent occurrences will be logged debug only): " + throwable2, throwable2); - } - } - - Throwable throwable3 = Exceptions.collapse(throwable2); - Builder rb = ApiError.builderFromThrowable(throwable3); - if (Strings.isBlank(rb.getMessage())) - rb.message("Internal error. Contact server administrator to consult logs for more details."); - if (Exceptions.isPrefixImportant(throwable3)) - rb.message(Exceptions.getPrefixText(throwable3)+": "+rb.getMessage()); - return rb.build().asResponse(Status.INTERNAL_SERVER_ERROR, MediaType.APPLICATION_JSON_TYPE); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/EntityLocationUtils.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/EntityLocationUtils.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/EntityLocationUtils.java deleted file mode 100644 index 32bb66d..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/EntityLocationUtils.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.util; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.mgmt.ManagementContext; -import org.apache.brooklyn.core.location.LocationConfigKeys; - -public class EntityLocationUtils { - - protected final ManagementContext context; - - public EntityLocationUtils(ManagementContext ctx) { - this.context = ctx; - } - - /* Returns the number of entites at each location for which the geographic coordinates are known. */ - public Map<Location, Integer> countLeafEntitiesByLocatedLocations() { - Map<Location, Integer> result = new LinkedHashMap<Location, Integer>(); - for (Entity e: context.getApplications()) { - countLeafEntitiesByLocatedLocations(e, null, result); - } - return result; - } - - protected void countLeafEntitiesByLocatedLocations(Entity target, Entity locatedParent, Map<Location, Integer> result) { - if (isLocatedLocation(target)) - locatedParent = target; - if (!target.getChildren().isEmpty()) { - // non-leaf - inspect children - for (Entity child: target.getChildren()) - countLeafEntitiesByLocatedLocations(child, locatedParent, result); - } else { - // leaf node - increment location count - if (locatedParent!=null) { - for (Location l: locatedParent.getLocations()) { - Location ll = getMostGeneralLocatedLocation(l); - if (ll!=null) { - Integer count = result.get(ll); - if (count==null) count = 1; - else count++; - result.put(ll, count); - } - } - } - } - } - - protected Location getMostGeneralLocatedLocation(Location l) { - if (l==null) return null; - if (!isLocatedLocation(l)) return null; - Location ll = getMostGeneralLocatedLocation(l.getParent()); - if (ll!=null) return ll; - return l; - } - - protected boolean isLocatedLocation(Entity target) { - for (Location l: target.getLocations()) - if (isLocatedLocation(l)) return true; - return false; - } - protected boolean isLocatedLocation(Location l) { - return l.getConfig(LocationConfigKeys.LATITUDE)!=null && l.getConfig(LocationConfigKeys.LONGITUDE)!=null; - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/FormMapProvider.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/FormMapProvider.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/FormMapProvider.java deleted file mode 100644 index 2b5c19b..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/FormMapProvider.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.util; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.List; -import java.util.Map; -import javax.ws.rs.Consumes; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.ext.MessageBodyReader; -import javax.ws.rs.ext.Provider; - -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.sun.jersey.core.impl.provider.entity.FormMultivaluedMapProvider; -import com.sun.jersey.core.util.MultivaluedMapImpl; - -/** - * A MessageBodyReader producing a <code>Map<String, Object></code>, where Object - * is either a <code>String</code>, a <code>List<String></code> or null. - */ -@Provider -@Consumes(MediaType.APPLICATION_FORM_URLENCODED) -public class FormMapProvider implements MessageBodyReader<Map<String, Object>> { - - @Override - public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { - if (!Map.class.equals(type) || !(genericType instanceof ParameterizedType)) { - return false; - } - ParameterizedType parameterized = (ParameterizedType) genericType; - return parameterized.getActualTypeArguments().length == 2 && - parameterized.getActualTypeArguments()[0] == String.class && - parameterized.getActualTypeArguments()[1] == Object.class; - } - - @Override - public Map<String, Object> readFrom(Class<Map<String, Object>> type, Type genericType, Annotation[] annotations, - MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) - throws IOException, WebApplicationException { - FormMultivaluedMapProvider delegate = new FormMultivaluedMapProvider(); - MultivaluedMap<String, String> multi = new MultivaluedMapImpl(); - multi = delegate.readFrom(multi, mediaType, entityStream); - - Map<String, Object> map = Maps.newHashMapWithExpectedSize(multi.keySet().size()); - for (String key : multi.keySet()) { - List<String> value = multi.get(key); - if (value.size() > 1) { - map.put(key, Lists.newArrayList(value)); - } else if (value.size() == 1) { - map.put(key, Iterables.getOnlyElement(value)); - } else { - map.put(key, null); - } - } - return map; - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ManagementContextProvider.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ManagementContextProvider.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ManagementContextProvider.java deleted file mode 100644 index 1e15f44..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ManagementContextProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.util; - -import javax.ws.rs.core.Context; - -import org.apache.brooklyn.api.mgmt.ManagementContext; - -import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider; - -public class ManagementContextProvider extends SingletonTypeInjectableProvider<Context, ManagementContext> { - - public ManagementContextProvider(ManagementContext instance) { - super(ManagementContext.class, instance); - } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/OsgiCompat.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/OsgiCompat.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/OsgiCompat.java deleted file mode 100644 index 1570efc..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/OsgiCompat.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2015 The Apache Software Foundation. - * - * Licensed 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.util; - -import javax.servlet.ServletContext; -import org.apache.brooklyn.api.mgmt.ManagementContext; -import org.apache.brooklyn.core.server.BrooklynServiceAttributes; -import org.apache.brooklyn.util.core.osgi.Compat; -import org.eclipse.jetty.server.handler.ContextHandler; - -/** - * Compatibility methods between karaf launcher and monolithic launcher. - * - * @todo Remove after transition to karaf launcher. - */ -public class OsgiCompat { - - public static ManagementContext getManagementContext(ServletContext servletContext) { - ManagementContext managementContext = Compat.getInstance().getManagementContext(); - if (managementContext == null && servletContext != null) { - managementContext = (ManagementContext) servletContext.getAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT); - } - return managementContext; - } - - public static ManagementContext getManagementContext(ContextHandler jettyServerHandler) { - ManagementContext managementContext = Compat.getInstance().getManagementContext(); - if (managementContext == null && jettyServerHandler != null) { - managementContext = (ManagementContext) jettyServerHandler.getAttribute(BrooklynServiceAttributes.BROOKLYN_MANAGEMENT_CONTEXT); - } - return managementContext; - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandler.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandler.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandler.java deleted file mode 100644 index e573bf6..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandler.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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.util; - -public interface ShutdownHandler { - void onShutdownRequest(); -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandlerProvider.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandlerProvider.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandlerProvider.java deleted file mode 100644 index f499ca0..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/ShutdownHandlerProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.util; - -import javax.annotation.Nullable; -import javax.ws.rs.core.Context; - -import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider; - -public class ShutdownHandlerProvider extends SingletonTypeInjectableProvider<Context, ShutdownHandler> { - public ShutdownHandlerProvider(@Nullable ShutdownHandler instance) { - super(ShutdownHandler.class, instance); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/URLParamEncoder.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/URLParamEncoder.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/URLParamEncoder.java deleted file mode 100644 index 8c25fda..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/URLParamEncoder.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.util; - - -/** - * @deprecated since 0.7.0 use {@link org.apache.brooklyn.util.net.URLParamEncoder} - */ -public class URLParamEncoder extends org.apache.brooklyn.util.net.URLParamEncoder { - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java deleted file mode 100644 index da72c6f..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/WebResourceUtils.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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.util; - -import java.io.IOException; -import java.util.Map; - -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.apache.brooklyn.core.catalog.internal.CatalogUtils; -import org.apache.brooklyn.rest.domain.ApiError; -import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.net.Urls; -import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableMap; -import com.sun.jersey.spi.container.ContainerResponse; - -public class WebResourceUtils { - - private static final Logger log = LoggerFactory.getLogger(WebResourceUtils.class); - - /** @throws WebApplicationException with an ApiError as its body and the given status as its response code. */ - public static WebApplicationException throwWebApplicationException(Response.Status status, String format, Object... args) { - String msg = String.format(format, args); - if (log.isDebugEnabled()) { - log.debug("responding {} {} ({})", - new Object[]{status.getStatusCode(), status.getReasonPhrase(), msg}); - } - ApiError apiError = ApiError.builder().message(msg).errorCode(status).build(); - // including a Throwable is the only way to include a message with the WebApplicationException - ugly! - throw new WebApplicationException(new Throwable(apiError.toString()), apiError.asJsonResponse()); - } - - /** @throws WebApplicationException With code 500 internal server error */ - public static WebApplicationException serverError(String format, Object... args) { - return throwWebApplicationException(Response.Status.INTERNAL_SERVER_ERROR, format, args); - } - - /** @throws WebApplicationException With code 400 bad request */ - public static WebApplicationException badRequest(String format, Object... args) { - return throwWebApplicationException(Response.Status.BAD_REQUEST, format, args); - } - - /** @throws WebApplicationException With code 401 unauthorized */ - public static WebApplicationException unauthorized(String format, Object... args) { - return throwWebApplicationException(Response.Status.UNAUTHORIZED, format, args); - } - - /** @throws WebApplicationException With code 403 forbidden */ - public static WebApplicationException forbidden(String format, Object... args) { - return throwWebApplicationException(Response.Status.FORBIDDEN, format, args); - } - - /** @throws WebApplicationException With code 404 not found */ - public static WebApplicationException notFound(String format, Object... args) { - return throwWebApplicationException(Response.Status.NOT_FOUND, format, args); - } - - /** @throws WebApplicationException With code 412 precondition failed */ - public static WebApplicationException preconditionFailed(String format, Object... args) { - return throwWebApplicationException(Response.Status.PRECONDITION_FAILED, format, args); - } - - public final static Map<String,com.google.common.net.MediaType> IMAGE_FORMAT_MIME_TYPES = ImmutableMap.<String, com.google.common.net.MediaType>builder() - .put("jpg", com.google.common.net.MediaType.JPEG) - .put("jpeg", com.google.common.net.MediaType.JPEG) - .put("png", com.google.common.net.MediaType.PNG) - .put("gif", com.google.common.net.MediaType.GIF) - .put("svg", com.google.common.net.MediaType.SVG_UTF_8) - .build(); - - public static MediaType getImageMediaTypeFromExtension(String extension) { - com.google.common.net.MediaType mime = IMAGE_FORMAT_MIME_TYPES.get(extension.toLowerCase()); - if (mime==null) return null; - try { - return MediaType.valueOf(mime.toString()); - } catch (Exception e) { - log.warn("Unparseable MIME type "+mime+"; ignoring ("+e+")"); - Exceptions.propagateIfFatal(e); - return null; - } - } - - /** as {@link #getValueForDisplay(ObjectMapper, Object, boolean, boolean)} with no mapper - * (so will only handle a subset of types) */ - public static Object getValueForDisplay(Object value, boolean preferJson, boolean isJerseyReturnValue) { - return getValueForDisplay(null, value, preferJson, isJerseyReturnValue); - } - - /** returns an object which jersey will handle nicely, converting to json, - * sometimes wrapping in quotes if needed (for outermost json return types); - * if json is not preferred, this simply applies a toString-style rendering */ - public static Object getValueForDisplay(ObjectMapper mapper, Object value, boolean preferJson, boolean isJerseyReturnValue) { - if (preferJson) { - if (value==null) return null; - Object result = value; - // no serialization checks required, with new smart-mapper which does toString - // (note there is more sophisticated logic in git history however) - result = value; - - if (isJerseyReturnValue) { - if (result instanceof String) { - // Jersey does not do json encoding if the return type is a string, - // expecting the returner to do the json encoding himself - // cf discussion at https://github.com/dropwizard/dropwizard/issues/231 - result = JavaStringEscapes.wrapJavaString((String)result); - } - } - - return result; - } else { - if (value==null) return ""; - return value.toString(); - } - } - - public static String getPathFromVersionedId(String versionedId) { - if (CatalogUtils.looksLikeVersionedId(versionedId)) { - String symbolicName = CatalogUtils.getSymbolicNameFromVersionedId(versionedId); - String version = CatalogUtils.getVersionFromVersionedId(versionedId); - return Urls.encode(symbolicName) + "/" + Urls.encode(version); - } else { - return Urls.encode(versionedId); - } - } - - /** Sets the {@link HttpServletResponse} target (last argument) from the given source {@link Response}; - * useful in filters where we might have a {@link Response} and need to set up an {@link HttpServletResponse}. - * Similar to {@link ContainerResponse#setResponse(Response)}; nothing like that seems to be available for {@link HttpServletResponse}. */ - public static void applyJsonResponse(ServletContext servletContext, Response source, HttpServletResponse target) throws IOException { - target.setStatus(source.getStatus()); - target.setContentType(MediaType.APPLICATION_JSON); - target.setCharacterEncoding("UTF-8"); - target.getWriter().write(BrooklynJacksonJsonProvider.findAnyObjectMapper(servletContext, null).writeValueAsString(source.getEntity())); - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java deleted file mode 100644 index 93cae3f..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BidiSerialization.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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.util.json; - -import java.io.IOException; -import java.util.Map; - -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.api.location.Location; -import org.apache.brooklyn.api.mgmt.ManagementContext; -import org.apache.brooklyn.api.objs.BrooklynObject; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.module.SimpleModule; - -public class BidiSerialization { - - protected final static ThreadLocal<Boolean> STRICT_SERIALIZATION = new ThreadLocal<Boolean>(); - - /** - * Sets strict serialization on, or off (the default), for the current thread. - * Recommended to be used in a <code>try { ... } finally { ... }</code> block - * with {@link #clearStrictSerialization()} at the end. - * <p> - * With strict serialization, classes must have public fields or annotated fields, else they will not be serialized. - */ - public static void setStrictSerialization(Boolean value) { - STRICT_SERIALIZATION.set(value); - } - - public static void clearStrictSerialization() { - STRICT_SERIALIZATION.remove(); - } - - public static boolean isStrictSerialization() { - Boolean result = STRICT_SERIALIZATION.get(); - if (result!=null) return result; - return false; - } - - - public abstract static class AbstractWithManagementContextSerialization<T> { - - protected class Serializer extends JsonSerializer<T> { - @Override - public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - AbstractWithManagementContextSerialization.this.serialize(value, jgen, provider); - } - } - - protected class Deserializer extends JsonDeserializer<T> { - @Override - public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { - return AbstractWithManagementContextSerialization.this.deserialize(jp, ctxt); - } - } - - protected final Serializer serializer = new Serializer(); - protected final Deserializer deserializer = new Deserializer(); - protected final Class<T> type; - protected final ManagementContext mgmt; - - public AbstractWithManagementContextSerialization(Class<T> type, ManagementContext mgmt) { - this.type = type; - this.mgmt = mgmt; - } - - public JsonSerializer<T> getSerializer() { - return serializer; - } - - public JsonDeserializer<T> getDeserializer() { - return deserializer; - } - - public void serialize(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeStartObject(); - writeBody(value, jgen, provider); - jgen.writeEndObject(); - } - - protected void writeBody(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeStringField("type", value.getClass().getCanonicalName()); - customWriteBody(value, jgen, provider); - } - - public abstract void customWriteBody(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException; - - public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { - @SuppressWarnings("unchecked") - Map<Object,Object> values = jp.readValueAs(Map.class); - String type = (String) values.get("type"); - return customReadBody(type, values, jp, ctxt); - } - - protected abstract T customReadBody(String type, Map<Object, Object> values, JsonParser jp, DeserializationContext ctxt) throws IOException; - - public void install(SimpleModule module) { - module.addSerializer(type, serializer); - module.addDeserializer(type, deserializer); - } - } - - public static class ManagementContextSerialization extends AbstractWithManagementContextSerialization<ManagementContext> { - public ManagementContextSerialization(ManagementContext mgmt) { super(ManagementContext.class, mgmt); } - @Override - public void customWriteBody(ManagementContext value, JsonGenerator jgen, SerializerProvider provider) throws IOException {} - @Override - protected ManagementContext customReadBody(String type, Map<Object, Object> values, JsonParser jp, DeserializationContext ctxt) throws IOException { - return mgmt; - } - } - - public abstract static class AbstractBrooklynObjectSerialization<T extends BrooklynObject> extends AbstractWithManagementContextSerialization<T> { - public AbstractBrooklynObjectSerialization(Class<T> type, ManagementContext mgmt) { - super(type, mgmt); - } - @Override - protected void writeBody(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeStringField("type", type.getCanonicalName()); - customWriteBody(value, jgen, provider); - } - @Override - public void customWriteBody(T value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeStringField("id", value.getId()); - } - @Override - protected T customReadBody(String type, Map<Object, Object> values, JsonParser jp, DeserializationContext ctxt) throws IOException { - return getInstanceFromId((String) values.get("id")); - } - protected abstract T getInstanceFromId(String id); - } - - public static class EntitySerialization extends AbstractBrooklynObjectSerialization<Entity> { - public EntitySerialization(ManagementContext mgmt) { super(Entity.class, mgmt); } - @Override protected Entity getInstanceFromId(String id) { return mgmt.getEntityManager().getEntity(id); } - } - public static class LocationSerialization extends AbstractBrooklynObjectSerialization<Location> { - public LocationSerialization(ManagementContext mgmt) { super(Location.class, mgmt); } - @Override protected Location getInstanceFromId(String id) { return mgmt.getLocationManager().getLocation(id); } - } - // TODO how to look up policies and enrichers? (not essential...) -// public static class PolicySerialization extends AbstractBrooklynObjectSerialization<Policy> { -// public EntitySerialization(ManagementContext mgmt) { super(Policy.class, mgmt); } -// @Override protected Policy getKind(String id) { return mgmt.getEntityManager().getEntity(id); } -// } -// public static class EnricherSerialization extends AbstractBrooklynObjectSerialization<Enricher> { -// public EntitySerialization(ManagementContext mgmt) { super(Entity.class, mgmt); } -// @Override protected Enricher getKind(String id) { return mgmt.getEntityManager().getEntity(id); } -// } - -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java deleted file mode 100644 index 08265c7..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonJsonProvider.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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.util.json; - -import javax.servlet.ServletContext; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; - -import org.apache.brooklyn.api.mgmt.ManagementContext; -import org.apache.brooklyn.config.ConfigKey; -import org.apache.brooklyn.core.config.ConfigKeys; -import org.apache.brooklyn.core.internal.BrooklynProperties; -import org.apache.brooklyn.core.mgmt.ManagementContextInjectable; -import org.apache.brooklyn.core.server.BrooklynServiceAttributes; -import org.apache.brooklyn.rest.util.OsgiCompat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.Version; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; - -public class BrooklynJacksonJsonProvider extends JacksonJsonProvider implements ManagementContextInjectable { - - private static final Logger log = LoggerFactory.getLogger(BrooklynJacksonJsonProvider.class); - - public static final String BROOKLYN_REST_OBJECT_MAPPER = BrooklynServiceAttributes.BROOKLYN_REST_OBJECT_MAPPER; - - @Context protected ServletContext servletContext; - - protected ObjectMapper ourMapper; - protected boolean notFound = false; - - private ManagementContext mgmt; - - public ObjectMapper locateMapper(Class<?> type, MediaType mediaType) { - if (ourMapper != null) - return ourMapper; - - findSharedMapper(); - - if (ourMapper != null) - return ourMapper; - - if (!notFound) { - log.warn("Management context not available; using default ObjectMapper in "+this); - notFound = true; - } - - return super.locateMapper(Object.class, MediaType.APPLICATION_JSON_TYPE); - } - - protected synchronized ObjectMapper findSharedMapper() { - if (ourMapper != null || notFound) - return ourMapper; - - ourMapper = findSharedObjectMapper(servletContext, mgmt); - if (ourMapper == null) return null; - - if (notFound) { - notFound = false; - } - log.debug("Found mapper "+ourMapper+" for "+this+", creating custom Brooklyn mapper"); - - return ourMapper; - } - - /** - * Finds a shared {@link ObjectMapper} or makes a new one, stored against the servlet context; - * returns null if a shared instance cannot be created. - */ - public static ObjectMapper findSharedObjectMapper(ServletContext servletContext, ManagementContext mgmt) { - if (servletContext != null) { - synchronized (servletContext) { - ObjectMapper mapper = (ObjectMapper) servletContext.getAttribute(BROOKLYN_REST_OBJECT_MAPPER); - if (mapper != null) return mapper; - - mapper = newPrivateObjectMapper(getManagementContext(servletContext)); - servletContext.setAttribute(BROOKLYN_REST_OBJECT_MAPPER, mapper); - return mapper; - } - } - if (mgmt != null) { - synchronized (mgmt) { - ConfigKey<ObjectMapper> key = ConfigKeys.newConfigKey(ObjectMapper.class, BROOKLYN_REST_OBJECT_MAPPER); - ObjectMapper mapper = mgmt.getConfig().getConfig(key); - if (mapper != null) return mapper; - - mapper = newPrivateObjectMapper(mgmt); - log.debug("Storing new ObjectMapper against "+mgmt+" because no ServletContext available: "+mapper); - ((BrooklynProperties)mgmt.getConfig()).put(key, mapper); - return mapper; - } - } - return null; - } - - /** - * Like {@link #findSharedObjectMapper(ServletContext, ManagementContext)} but will create a private - * ObjectMapper if it can, from the servlet context and/or the management context, or else fail - */ - public static ObjectMapper findAnyObjectMapper(ServletContext servletContext, ManagementContext mgmt) { - ObjectMapper mapper = findSharedObjectMapper(servletContext, mgmt); - if (mapper != null) return mapper; - - if (mgmt == null && servletContext != null) { - mgmt = getManagementContext(servletContext); - } - return newPrivateObjectMapper(mgmt); - } - - /** - * @return A new Brooklyn-specific ObjectMapper. - * Normally {@link #findSharedObjectMapper(ServletContext, ManagementContext)} is preferred - */ - public static ObjectMapper newPrivateObjectMapper(ManagementContext mgmt) { - if (mgmt == null) { - throw new IllegalStateException("No management context available for creating ObjectMapper"); - } - - ConfigurableSerializerProvider sp = new ConfigurableSerializerProvider(); - sp.setUnknownTypeSerializer(new ErrorAndToStringUnknownTypeSerializer()); - - ObjectMapper mapper = new ObjectMapper(); - mapper.setSerializerProvider(sp); - mapper.setVisibilityChecker(new PossiblyStrictPreferringFieldsVisibilityChecker()); - - SimpleModule mapperModule = new SimpleModule("Brooklyn", new Version(0, 0, 0, "ignored")); - - new BidiSerialization.ManagementContextSerialization(mgmt).install(mapperModule); - new BidiSerialization.EntitySerialization(mgmt).install(mapperModule); - new BidiSerialization.LocationSerialization(mgmt).install(mapperModule); - - mapperModule.addSerializer(new MultimapSerializer()); - mapper.registerModule(mapperModule); - - return mapper; - } - - public static ManagementContext getManagementContext(ServletContext servletContext) { - return OsgiCompat.getManagementContext(servletContext); - } - - @Override - public void setManagementContext(ManagementContext mgmt) { - this.mgmt = mgmt; - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java deleted file mode 100644 index a84c695..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ConfigurableSerializerProvider.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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.util.json; - -import java.io.IOException; - -import org.apache.brooklyn.util.exceptions.Exceptions; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonStreamContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializationConfig; -import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; -import com.fasterxml.jackson.databind.ser.SerializerFactory; - -/** allows the serializer-of-last-resort to be customized, ie used for unknown-types */ -final class ConfigurableSerializerProvider extends DefaultSerializerProvider { - - protected JsonSerializer<Object> unknownTypeSerializer; - - public ConfigurableSerializerProvider() {} - - @Override - public DefaultSerializerProvider createInstance(SerializationConfig config, SerializerFactory jsf) { - return new ConfigurableSerializerProvider(config, this, jsf); - } - - public ConfigurableSerializerProvider(SerializationConfig config, ConfigurableSerializerProvider src, SerializerFactory jsf) { - super(src, config, jsf); - unknownTypeSerializer = src.unknownTypeSerializer; - } - - public JsonSerializer<Object> getUnknownTypeSerializer(Class<?> unknownType) { - if (unknownTypeSerializer!=null) return unknownTypeSerializer; - return super.getUnknownTypeSerializer(unknownType); - } - - public void setUnknownTypeSerializer(JsonSerializer<Object> unknownTypeSerializer) { - this.unknownTypeSerializer = unknownTypeSerializer; - } - - @Override - public void serializeValue(JsonGenerator jgen, Object value) throws IOException { - JsonStreamContext ctxt = jgen.getOutputContext(); - try { - super.serializeValue(jgen, value); - } catch (Exception e) { - onSerializationException(ctxt, jgen, value, e); - } - } - - @Override - public void serializeValue(JsonGenerator jgen, Object value, JavaType rootType) throws IOException { - JsonStreamContext ctxt = jgen.getOutputContext(); - try { - super.serializeValue(jgen, value, rootType); - } catch (Exception e) { - onSerializationException(ctxt, jgen, value, e); - } - } - - protected void onSerializationException(JsonStreamContext ctxt, JsonGenerator jgen, Object value, Exception e) throws IOException { - Exceptions.propagateIfFatal(e); - - JsonSerializer<Object> unknownTypeSerializer = getUnknownTypeSerializer(value.getClass()); - if (unknownTypeSerializer instanceof ErrorAndToStringUnknownTypeSerializer) { - ((ErrorAndToStringUnknownTypeSerializer)unknownTypeSerializer).serializeFromError(ctxt, e, value, jgen, this); - } else { - unknownTypeSerializer.serialize(value, jgen, this); - } - } -} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/6f624c78/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java ---------------------------------------------------------------------- diff --git a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java b/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java deleted file mode 100644 index e529ec9..0000000 --- a/rest/rest-server/src/main/java/org/apache/brooklyn/rest/util/json/ErrorAndToStringUnknownTypeSerializer.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.util.json; - -import java.io.IOException; -import java.io.NotSerializableException; -import java.util.Collections; -import java.util.Set; - -import javax.annotation.Nullable; - -import org.apache.brooklyn.util.collections.MutableSet; -import org.apache.brooklyn.util.javalang.Reflections; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonStreamContext; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.impl.UnknownSerializer; - -/** - * for non-json-serializable classes (quite a lot of them!) simply provide a sensible error message and a toString. - * TODO maybe we want to attempt to serialize fields instead? (but being careful not to be self-referential!) - */ -public class ErrorAndToStringUnknownTypeSerializer extends UnknownSerializer { - - private static final Logger log = LoggerFactory.getLogger(ErrorAndToStringUnknownTypeSerializer.class); - private static Set<String> WARNED_CLASSES = Collections.synchronizedSet(MutableSet.<String>of()); - - @Override - public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - if (BidiSerialization.isStrictSerialization()) - throw new JsonMappingException("Cannot serialize object containing "+value.getClass().getName()+" when strict serialization requested"); - - serializeFromError(jgen.getOutputContext(), null, value, jgen, provider); - } - - public void serializeFromError(JsonStreamContext ctxt, @Nullable Exception error, Object value, JsonGenerator jgen, SerializerProvider configurableSerializerProvider) throws IOException { - if (log.isDebugEnabled()) - log.debug("Recovering from json serialization error, serializing "+value+": "+error); - - if (BidiSerialization.isStrictSerialization()) - throw new JsonMappingException("Cannot serialize " - + (ctxt!=null && !ctxt.inRoot() ? "object containing " : "") - + value.getClass().getName()+" when strict serialization requested"); - - if (WARNED_CLASSES.add(value.getClass().getCanonicalName())) { - log.warn("Standard serialization not possible for "+value.getClass()+" ("+value+")", error); - } - JsonStreamContext newCtxt = jgen.getOutputContext(); - - // very odd, but flush seems necessary when working with large objects; presumably a buffer which is allowed to clear itself? - // without this, when serializing the large (1.5M) Server json object from BrooklynJacksonSerializerTest creates invalid json, - // containing: "foo":false,"{"error":true,... - jgen.flush(); - - boolean createObject = !newCtxt.inObject() || newCtxt.getCurrentName()!=null; - if (createObject) { - jgen.writeStartObject(); - } - - if (allowEmpty(value.getClass())) { - // write nothing - } else { - - jgen.writeFieldName("error"); - jgen.writeBoolean(true); - - jgen.writeFieldName("errorType"); - jgen.writeString(NotSerializableException.class.getCanonicalName()); - - jgen.writeFieldName("type"); - jgen.writeString(value.getClass().getCanonicalName()); - - jgen.writeFieldName("toString"); - jgen.writeString(value.toString()); - - if (error!=null) { - jgen.writeFieldName("causedByError"); - jgen.writeString(error.toString()); - } - - } - - if (createObject) { - jgen.writeEndObject(); - } - - while (newCtxt!=null && !newCtxt.equals(ctxt)) { - if (jgen.getOutputContext().inArray()) { jgen.writeEndArray(); continue; } - if (jgen.getOutputContext().inObject()) { jgen.writeEndObject(); continue; } - break; - } - - } - - protected boolean allowEmpty(Class<? extends Object> clazz) { - if (clazz.getAnnotation(JsonSerialize.class)!=null && Reflections.hasNoNonObjectFields(clazz)) { - return true; - } else { - return false; - } - } -}