brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [29/34] brooklyn-server git commit: This closes #15
Date Thu, 18 Feb 2016 15:47:47 GMT
This closes #15

simple merge conflicts resolved (imports, and extra param/code in places)


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/79b98c67
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/79b98c67
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/79b98c67

Branch: refs/heads/master
Commit: 79b98c679b97cd34464abf4a78ddb93cc809b195
Parents: a5103bb abd2d5f
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Thu Feb 18 10:54:31 2016 +0000
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Thu Feb 18 10:56:30 2016 +0000

----------------------------------------------------------------------
 camp/camp-server-jersey/pom.xml                 | 225 ++++++
 .../brooklyn/camp/server/RestApiSetup.java      |  53 ++
 .../rest/resource/ApiListingResource.java       | 260 +++++++
 .../rest/resource/ApidocRestResource.java       |  32 +
 .../src/main/webapp/WEB-INF/web.xml             | 142 ++++
 camp/camp-server/pom.xml                        |  21 +-
 .../brooklyn/camp/server/CampRestApp.java       |  36 +
 .../brooklyn/camp/server/RestApiSetup.java      |  41 ++
 .../brooklyn/camp/server/rest/CampServer.java   |  32 +-
 .../rest/resource/ApidocRestResource.java       |   2 +-
 camp/pom.xml                                    |   1 +
 karaf/apache-brooklyn/pom.xml                   |   2 +-
 .../main/resources/etc/org.apache.cxf.osgi.cfg  |  20 +
 karaf/features/src/main/feature/feature.xml     |  30 +-
 karaf/itest/pom.xml                             |  13 +
 .../java/org/apache/brooklyn/AssemblyTest.java  |   4 +-
 launcher/pom.xml                                |  33 +-
 .../org/apache/brooklyn/launcher/Activator.java |   2 +
 .../brooklyn/launcher/BrooklynWebServer.java    |  95 +--
 .../camp/BrooklynCampPlatformLauncher.java      |   1 +
 .../jsgui/BrooklynJavascriptGuiLauncher.java    |   2 +-
 .../BrooklynJavascriptGuiLauncherTest.java      |  18 +-
 locations/jclouds/pom.xml                       |  12 +
 parent/pom.xml                                  | 194 +++--
 pom.xml                                         |   8 +-
 rest/rest-api/pom.xml                           |  21 +-
 .../org/apache/brooklyn/rest/api/AccessApi.java |   2 +-
 .../apache/brooklyn/rest/api/ActivityApi.java   |   2 +-
 .../brooklyn/rest/api/ApplicationApi.java       |   2 +-
 .../apache/brooklyn/rest/api/CatalogApi.java    |  28 +-
 .../apache/brooklyn/rest/api/EffectorApi.java   |   2 +-
 .../org/apache/brooklyn/rest/api/EntityApi.java |   4 +-
 .../brooklyn/rest/api/EntityConfigApi.java      |   4 +-
 .../apache/brooklyn/rest/api/LocationApi.java   |   2 +-
 .../org/apache/brooklyn/rest/api/PolicyApi.java |   2 +-
 .../brooklyn/rest/api/PolicyConfigApi.java      |   2 +-
 .../org/apache/brooklyn/rest/api/ScriptApi.java |   2 +-
 .../org/apache/brooklyn/rest/api/SensorApi.java |   5 +-
 .../org/apache/brooklyn/rest/api/ServerApi.java |   2 +-
 .../org/apache/brooklyn/rest/api/UsageApi.java  |   2 +-
 .../apache/brooklyn/rest/api/VersionApi.java    |   4 +-
 rest/rest-api/src/main/webapp/WEB-INF/web.xml   | 121 ----
 .../rest/domain/ApplicationSpecTest.java        |   2 +-
 .../rest/domain/EffectorSummaryTest.java        |   2 +-
 .../brooklyn/rest/domain/EntitySummaryTest.java |   8 +-
 .../rest/domain/LocationSummaryTest.java        |   2 +-
 .../resources/fixtures/application-list.json    |   8 +-
 .../resources/fixtures/application-spec.json    |   2 +-
 .../test/resources/fixtures/application.json    |   4 +-
 .../fixtures/effector-summary-list.json         |  18 +-
 .../resources/fixtures/effector-summary.json    |   2 +-
 .../resources/fixtures/entity-summary-list.json |   8 +-
 .../test/resources/fixtures/entity-summary.json |   8 +-
 .../test/resources/fixtures/location-list.json  |   2 +-
 .../resources/fixtures/location-summary.json    |   2 +-
 .../resources/fixtures/sensor-summary-list.json |  16 +-
 .../test/resources/fixtures/sensor-summary.json |   2 +-
 rest/rest-client/pom.xml                        |   7 +
 .../rest/client/BrooklynApiRestClientTest.java  |   3 +-
 .../rest/client/BrooklynApiUtilTest.java        |   2 +-
 rest/rest-resources/pom.xml                     | 220 ++++++
 .../apache/brooklyn/rest/BrooklynRestApi.java   |  91 +++
 .../apache/brooklyn/rest/BrooklynRestApp.java   |  56 ++
 .../apache/brooklyn/rest/BrooklynWebConfig.java | 164 +++++
 .../rest/filter/HaHotCheckResourceFilter.java   | 159 +++++
 .../rest/filter/HaHotStateRequired.java         |  38 +
 .../brooklyn/rest/filter/NoCacheFilter.java     |  40 ++
 .../resources/AbstractBrooklynRestResource.java | 156 +++++
 .../brooklyn/rest/resources/AccessResource.java |  46 ++
 .../rest/resources/ActivityResource.java        |  99 +++
 .../brooklyn/rest/resources/ApidocResource.java |  34 +
 .../rest/resources/ApplicationResource.java     | 474 +++++++++++++
 .../rest/resources/CatalogResource.java         | 516 ++++++++++++++
 .../rest/resources/EffectorResource.java        | 114 +++
 .../rest/resources/EntityConfigResource.java    | 206 ++++++
 .../brooklyn/rest/resources/EntityResource.java | 223 ++++++
 .../rest/resources/LocationResource.java        | 186 +++++
 .../rest/resources/PolicyConfigResource.java    | 108 +++
 .../brooklyn/rest/resources/PolicyResource.java | 135 ++++
 .../brooklyn/rest/resources/ScriptResource.java | 102 +++
 .../brooklyn/rest/resources/SensorResource.java | 184 +++++
 .../brooklyn/rest/resources/ServerResource.java | 497 +++++++++++++
 .../brooklyn/rest/resources/UsageResource.java  | 256 +++++++
 .../rest/resources/VersionResource.java         |  32 +
 .../brooklyn/rest/security/PasswordHasher.java  |  32 +
 .../provider/AbstractSecurityProvider.java      |  56 ++
 .../provider/AnyoneSecurityProvider.java        |  40 ++
 .../provider/BlackholeSecurityProvider.java     |  40 ++
 ...nUserWithRandomPasswordSecurityProvider.java |  73 ++
 .../provider/DelegatingSecurityProvider.java    | 165 +++++
 .../provider/ExplicitUsersSecurityProvider.java | 117 ++++
 .../security/provider/LdapSecurityProvider.java | 132 ++++
 .../security/provider/SecurityProvider.java     |  35 +
 .../rest/transform/AccessTransformer.java       |  42 ++
 .../rest/transform/ApplicationTransformer.java  | 125 ++++
 .../transform/BrooklynFeatureTransformer.java   |  45 ++
 .../rest/transform/CatalogTransformer.java      | 188 +++++
 .../rest/transform/EffectorTransformer.java     |  91 +++
 .../rest/transform/EntityTransformer.java       | 182 +++++
 .../transform/HighAvailabilityTransformer.java  |  50 ++
 .../rest/transform/LocationTransformer.java     | 202 ++++++
 .../rest/transform/PolicyTransformer.java       |  97 +++
 .../rest/transform/SensorTransformer.java       |  88 +++
 .../rest/transform/TaskTransformer.java         | 153 ++++
 .../rest/util/BrooklynRestResourceUtils.java    | 609 ++++++++++++++++
 .../rest/util/DefaultExceptionMapper.java       | 117 ++++
 .../brooklyn/rest/util/EntityLocationUtils.java |  85 +++
 .../brooklyn/rest/util/FormMapProvider.java     |  86 +++
 .../rest/util/ManagementContextProvider.java    |  45 ++
 .../apache/brooklyn/rest/util/OsgiCompat.java   |  40 ++
 .../brooklyn/rest/util/ShutdownHandler.java     |  23 +
 .../rest/util/ShutdownHandlerProvider.java      |  45 ++
 .../brooklyn/rest/util/URLParamEncoder.java     |  27 +
 .../brooklyn/rest/util/WebResourceUtils.java    | 231 ++++++
 .../rest/util/json/BidiSerialization.java       | 173 +++++
 .../util/json/BrooklynJacksonJsonProvider.java  | 173 +++++
 .../json/ConfigurableSerializerProvider.java    |  90 +++
 .../ErrorAndToStringUnknownTypeSerializer.java  | 123 ++++
 .../rest/util/json/MultimapSerializer.java      |  64 ++
 ...StrictPreferringFieldsVisibilityChecker.java | 108 +++
 .../resources/OSGI-INF/blueprint/service.xml    | 112 +++
 .../main/resources/build-metadata.properties    |  18 +
 .../brooklyn/rest/domain/ApplicationTest.java   |  86 +++
 .../brooklyn/rest/domain/SensorSummaryTest.java | 102 +++
 .../brooklyn/rest/filter/HaHotCheckTest.java    | 120 ++++
 .../rest/resources/AccessResourceTest.java      |  67 ++
 .../rest/resources/ApidocResourceTest.java      | 135 ++++
 .../ApplicationResourceIntegrationTest.java     | 132 ++++
 .../rest/resources/ApplicationResourceTest.java | 697 ++++++++++++++++++
 .../rest/resources/CatalogResetTest.java        | 118 ++++
 .../rest/resources/CatalogResourceTest.java     | 510 ++++++++++++++
 .../rest/resources/DelegatingPrintStream.java   | 183 +++++
 .../rest/resources/DescendantsTest.java         | 128 ++++
 .../resources/EntityConfigResourceTest.java     | 171 +++++
 .../rest/resources/EntityResourceTest.java      | 188 +++++
 .../rest/resources/ErrorResponseTest.java       |  98 +++
 .../rest/resources/LocationResourceTest.java    | 190 +++++
 .../rest/resources/PolicyResourceTest.java      | 143 ++++
 .../rest/resources/ScriptResourceTest.java      |  56 ++
 .../SensorResourceIntegrationTest.java          |  74 ++
 .../rest/resources/SensorResourceTest.java      | 267 +++++++
 .../rest/resources/ServerResourceTest.java      | 168 +++++
 .../rest/resources/ServerShutdownTest.java      | 164 +++++
 .../rest/resources/UsageResourceTest.java       | 444 ++++++++++++
 .../rest/resources/VersionResourceTest.java     |  46 ++
 .../rest/security/PasswordHasherTest.java       |  37 +
 .../test/config/render/TestRendererHints.java   |  36 +
 .../brooklynnode/DeployBlueprintTest.java       |  97 +++
 .../rest/testing/BrooklynRestApiTest.java       | 223 ++++++
 .../rest/testing/BrooklynRestResourceTest.java  | 212 ++++++
 .../rest/testing/mocks/CapitalizePolicy.java    |  33 +
 .../rest/testing/mocks/EverythingGroup.java     |  27 +
 .../rest/testing/mocks/EverythingGroupImpl.java |  32 +
 .../rest/testing/mocks/NameMatcherGroup.java    |  30 +
 .../testing/mocks/NameMatcherGroupImpl.java     |  33 +
 .../rest/testing/mocks/RestMockApp.java         |  24 +
 .../rest/testing/mocks/RestMockAppBuilder.java  |  39 ++
 .../testing/mocks/RestMockSimpleEntity.java     | 103 +++
 .../testing/mocks/RestMockSimplePolicy.java     |  64 ++
 .../util/BrooklynRestResourceUtilsTest.java     | 213 ++++++
 .../rest/util/EntityLocationUtilsTest.java      |  72 ++
 .../rest/util/HaHotStateCheckClassResource.java |  38 +
 .../rest/util/HaHotStateCheckResource.java      |  44 ++
 .../brooklyn/rest/util/TestShutdownHandler.java |  37 +
 .../json/BrooklynJacksonSerializerTest.java     | 264 +++++++
 .../META-INF/cxf/org.apache.cxf.Logger          |  18 +
 .../resources/brooklyn/scanning.catalog.bom     |  19 +
 .../src/test/resources/not-a-jar-file.txt       |  18 +
 .../src/test/resources/reset-catalog.xml        |  37 +
 rest/rest-server-jersey/pom.xml                 | 317 +++++++++
 .../org/apache/brooklyn/rest/RestApiSetup.java  |  78 +++
 .../rest/filter/HaHotCheckResourceFilter.java   | 163 +++++
 .../brooklyn/rest/filter/NoCacheFilter.java     |  40 ++
 .../brooklyn/rest/filter/SwaggerFilter.java     |  79 +++
 .../rest/resources/ApiListingResource.java      | 260 +++++++
 .../brooklyn/rest/resources/ApidocResource.java |  33 +
 .../brooklyn/rest/util/FormMapProvider.java     |  81 +++
 .../main/resources/build-metadata.properties    |  18 +
 .../src/main/webapp/WEB-INF/web.xml             | 144 ++++
 .../BrooklynPropertiesSecurityFilterTest.java   | 151 ++++
 .../brooklyn/rest/BrooklynRestApiLauncher.java  | 499 +++++++++++++
 .../rest/BrooklynRestApiLauncherTest.java       |  77 ++
 .../BrooklynRestApiLauncherTestFixture.java     | 109 +++
 .../apache/brooklyn/rest/HaHotCheckTest.java    | 130 ++++
 .../brooklyn/rest/HaMasterCheckFilterTest.java  | 218 ++++++
 .../rest/domain/AbstractDomainTest.java         |  44 ++
 .../brooklyn/rest/domain/ApiErrorTest.java      |  71 ++
 .../rest/domain/ApplicationSpecTest.java        |  40 ++
 .../brooklyn/rest/domain/ApplicationTest.java   |  87 +++
 .../rest/domain/EffectorSummaryTest.java        |  44 ++
 .../brooklyn/rest/domain/EntitySpecTest.java    |  48 ++
 .../brooklyn/rest/domain/EntitySummaryTest.java |  48 ++
 .../brooklyn/rest/domain/LocationSpecTest.java  |  58 ++
 .../rest/domain/LocationSummaryTest.java        |  41 ++
 .../brooklyn/rest/domain/SensorSummaryTest.java | 103 +++
 .../rest/domain/VersionSummaryTest.java         |  49 ++
 .../AbstractRestApiEntitlementsTest.java        | 111 +++
 .../ActivityApiEntitlementsTest.java            | 123 ++++
 .../AuthenticateAnyoneSecurityProvider.java     |  41 ++
 .../EntityConfigApiEntitlementsTest.java        | 103 +++
 .../entitlement/SensorApiEntitlementsTest.java  | 108 +++
 .../entitlement/ServerApiEntitlementsTest.java  |  34 +
 .../StaticDelegatingEntitlementManager.java     |  37 +
 .../rest/resources/AccessResourceTest.java      |  68 ++
 .../rest/resources/ApidocResourceTest.java      | 173 +++++
 .../ApplicationResourceIntegrationTest.java     | 133 ++++
 .../rest/resources/ApplicationResourceTest.java | 701 +++++++++++++++++++
 .../rest/resources/CatalogResetTest.java        | 113 +++
 .../rest/resources/CatalogResourceTest.java     | 515 ++++++++++++++
 .../rest/resources/DelegatingPrintStream.java   | 183 +++++
 .../rest/resources/DescendantsTest.java         | 130 ++++
 .../resources/EntityConfigResourceTest.java     | 172 +++++
 .../rest/resources/EntityResourceTest.java      | 189 +++++
 .../rest/resources/ErrorResponseTest.java       |  98 +++
 .../rest/resources/LocationResourceTest.java    | 188 +++++
 .../rest/resources/PolicyResourceTest.java      | 145 ++++
 .../rest/resources/ScriptResourceTest.java      |  54 ++
 .../SensorResourceIntegrationTest.java          |  82 +++
 .../rest/resources/SensorResourceTest.java      | 271 +++++++
 .../ServerResourceIntegrationTest.java          | 125 ++++
 .../rest/resources/ServerResourceTest.java      | 168 +++++
 .../rest/resources/ServerShutdownTest.java      | 185 +++++
 .../rest/resources/UsageResourceTest.java       | 443 ++++++++++++
 .../rest/resources/VersionResourceTest.java     |  52 ++
 .../rest/security/PasswordHasherTest.java       |  37 +
 .../security/provider/TestSecurityProvider.java |  46 ++
 .../test/config/render/TestRendererHints.java   |  36 +
 .../brooklynnode/DeployBlueprintTest.java       |  89 +++
 .../rest/testing/BrooklynRestApiTest.java       | 223 ++++++
 .../rest/testing/BrooklynRestResourceTest.java  | 154 ++++
 .../rest/testing/mocks/CapitalizePolicy.java    |  33 +
 .../rest/testing/mocks/EverythingGroup.java     |  27 +
 .../rest/testing/mocks/EverythingGroupImpl.java |  32 +
 .../rest/testing/mocks/NameMatcherGroup.java    |  30 +
 .../testing/mocks/NameMatcherGroupImpl.java     |  33 +
 .../rest/testing/mocks/RestMockApp.java         |  24 +
 .../rest/testing/mocks/RestMockAppBuilder.java  |  39 ++
 .../testing/mocks/RestMockSimpleEntity.java     | 103 +++
 .../testing/mocks/RestMockSimplePolicy.java     |  64 ++
 .../util/BrooklynRestResourceUtilsTest.java     | 213 ++++++
 .../rest/util/EntityLocationUtilsTest.java      |  72 ++
 .../rest/util/HaHotStateCheckClassResource.java |  38 +
 .../rest/util/HaHotStateCheckResource.java      |  44 ++
 .../rest/util/NoOpRecordingShutdownHandler.java |  39 ++
 .../util/NullHttpServletRequestProvider.java    |  46 ++
 .../rest/util/NullServletConfigProvider.java    |  51 ++
 .../brooklyn/rest/util/RestApiTestUtils.java    |  58 ++
 .../util/ServerStoppingShutdownHandler.java     |  75 ++
 .../json/BrooklynJacksonSerializerTest.java     | 399 +++++++++++
 .../resources/brooklyn/scanning.catalog.bom     |  19 +
 .../resources/fixtures/api-error-basic.json     |   4 +
 .../fixtures/api-error-no-details.json          |   3 +
 .../resources/fixtures/application-list.json    |  44 ++
 .../resources/fixtures/application-spec.json    |  16 +
 .../resources/fixtures/application-tree.json    |  43 ++
 .../test/resources/fixtures/application.json    |  22 +
 .../fixtures/catalog-application-list.json      |  29 +
 .../resources/fixtures/catalog-application.json |   9 +
 .../fixtures/effector-summary-list.json         |  47 ++
 .../resources/fixtures/effector-summary.json    |   9 +
 .../resources/fixtures/entity-only-type.json    |   3 +
 .../resources/fixtures/entity-summary-list.json |  14 +
 .../test/resources/fixtures/entity-summary.json |  13 +
 .../src/test/resources/fixtures/entity.json     |   7 +
 .../src/test/resources/fixtures/ha-summary.json |  19 +
 .../test/resources/fixtures/location-list.json  |  10 +
 .../resources/fixtures/location-summary.json    |   8 +
 .../fixtures/location-without-credential.json   |   5 +
 .../src/test/resources/fixtures/location.json   |   4 +
 .../fixtures/sensor-current-state.json          |   6 +
 .../resources/fixtures/sensor-summary-list.json |  42 ++
 .../test/resources/fixtures/sensor-summary.json |   8 +
 .../test/resources/fixtures/server-version.json |  14 +
 .../test/resources/fixtures/service-state.json  |   1 +
 .../resources/fixtures/task-summary-list.json   |  15 +
 rest/rest-server/pom.xml                        |  76 +-
 .../apache/brooklyn/rest/BrooklynRestApi.java   |  89 ---
 .../apache/brooklyn/rest/BrooklynWebConfig.java | 158 -----
 .../org/apache/brooklyn/rest/RestApiSetup.java  |  72 ++
 .../BrooklynPropertiesSecurityFilter.java       |  19 +-
 .../rest/filter/HaHotCheckResourceFilter.java   | 150 ----
 .../rest/filter/HaHotStateRequired.java         |  36 -
 .../rest/filter/HaMasterCheckFilter.java        |  18 +-
 .../brooklyn/rest/filter/LoggingFilter.java     |   2 +
 .../brooklyn/rest/filter/NoCacheFilter.java     |  40 --
 .../rest/filter/RequestTaggingFilter.java       |   1 +
 .../brooklyn/rest/filter/SwaggerFilter.java     |  76 --
 .../resources/AbstractBrooklynRestResource.java | 152 ----
 .../brooklyn/rest/resources/AccessResource.java |  46 --
 .../rest/resources/ActivityResource.java        |  99 ---
 .../brooklyn/rest/resources/ApidocResource.java |  31 -
 .../rest/resources/ApplicationResource.java     | 474 -------------
 .../rest/resources/CatalogResource.java         | 523 --------------
 .../rest/resources/EffectorResource.java        | 114 ---
 .../rest/resources/EntityConfigResource.java    | 206 ------
 .../brooklyn/rest/resources/EntityResource.java | 223 ------
 .../rest/resources/LocationResource.java        | 184 -----
 .../rest/resources/PolicyConfigResource.java    | 108 ---
 .../brooklyn/rest/resources/PolicyResource.java | 131 ----
 .../brooklyn/rest/resources/ScriptResource.java | 102 ---
 .../brooklyn/rest/resources/SensorResource.java | 184 -----
 .../brooklyn/rest/resources/ServerResource.java | 495 -------------
 .../brooklyn/rest/resources/UsageResource.java  | 256 -------
 .../rest/resources/VersionResource.java         |  32 -
 .../brooklyn/rest/security/PasswordHasher.java  |  32 -
 .../provider/AbstractSecurityProvider.java      |  56 --
 .../provider/AnyoneSecurityProvider.java        |  40 --
 .../provider/BlackholeSecurityProvider.java     |  40 --
 ...nUserWithRandomPasswordSecurityProvider.java |  73 --
 .../provider/DelegatingSecurityProvider.java    | 166 -----
 .../provider/ExplicitUsersSecurityProvider.java | 118 ----
 .../security/provider/LdapSecurityProvider.java | 132 ----
 .../security/provider/SecurityProvider.java     |  35 -
 .../rest/transform/AccessTransformer.java       |  39 --
 .../rest/transform/ApplicationTransformer.java  | 116 ---
 .../transform/BrooklynFeatureTransformer.java   |  45 --
 .../rest/transform/CatalogTransformer.java      | 192 -----
 .../rest/transform/EffectorTransformer.java     |  85 ---
 .../rest/transform/EntityTransformer.java       | 165 -----
 .../transform/HighAvailabilityTransformer.java  |  50 --
 .../rest/transform/LocationTransformer.java     | 193 -----
 .../rest/transform/PolicyTransformer.java       |  83 ---
 .../rest/transform/SensorTransformer.java       |  84 ---
 .../rest/transform/TaskTransformer.java         | 146 ----
 .../rest/util/BrooklynRestResourceUtils.java    | 608 ----------------
 .../rest/util/DefaultExceptionMapper.java       | 107 ---
 .../brooklyn/rest/util/EntityLocationUtils.java |  85 ---
 .../brooklyn/rest/util/FormMapProvider.java     |  81 ---
 .../rest/util/ManagementContextProvider.java    |  33 -
 .../apache/brooklyn/rest/util/OsgiCompat.java   |  46 --
 .../brooklyn/rest/util/ShutdownHandler.java     |  23 -
 .../rest/util/ShutdownHandlerProvider.java      |  30 -
 .../brooklyn/rest/util/URLParamEncoder.java     |  27 -
 .../brooklyn/rest/util/WebResourceUtils.java    | 195 ------
 .../rest/util/json/BidiSerialization.java       | 173 -----
 .../util/json/BrooklynJacksonJsonProvider.java  | 165 -----
 .../json/ConfigurableSerializerProvider.java    |  89 ---
 .../ErrorAndToStringUnknownTypeSerializer.java  | 123 ----
 .../rest/util/json/MultimapSerializer.java      |  62 --
 ...StrictPreferringFieldsVisibilityChecker.java | 108 ---
 .../src/main/resources/not-a-jar-file.txt       |  18 -
 .../src/main/resources/reset-catalog.xml        |  37 -
 .../rest-server/src/main/webapp/WEB-INF/web.xml |  96 +--
 .../BrooklynPropertiesSecurityFilterTest.java   |   6 +-
 .../brooklyn/rest/BrooklynRestApiLauncher.java  | 115 +--
 .../rest/BrooklynRestApiLauncherTest.java       |   6 -
 .../BrooklynRestApiLauncherTestFixture.java     |  22 +-
 .../apache/brooklyn/rest/HaHotCheckTest.java    | 129 ----
 .../brooklyn/rest/HaMasterCheckFilterTest.java  |   2 +-
 .../brooklyn/rest/domain/ApplicationTest.java   |  87 ---
 .../brooklyn/rest/domain/SensorSummaryTest.java | 101 ---
 .../rest/resources/AccessResourceTest.java      |  68 --
 .../rest/resources/ApidocResourceTest.java      | 173 -----
 .../ApplicationResourceIntegrationTest.java     | 133 ----
 .../rest/resources/ApplicationResourceTest.java | 701 -------------------
 .../rest/resources/CatalogResetTest.java        | 113 ---
 .../rest/resources/CatalogResourceTest.java     | 514 --------------
 .../rest/resources/DelegatingPrintStream.java   | 183 -----
 .../rest/resources/DescendantsTest.java         | 130 ----
 .../resources/EntityConfigResourceTest.java     | 172 -----
 .../rest/resources/EntityResourceTest.java      | 189 -----
 .../rest/resources/ErrorResponseTest.java       |  98 ---
 .../rest/resources/LocationResourceTest.java    | 189 -----
 .../rest/resources/PolicyResourceTest.java      | 145 ----
 .../rest/resources/ScriptResourceTest.java      |  54 --
 .../SensorResourceIntegrationTest.java          |  82 ---
 .../rest/resources/SensorResourceTest.java      | 271 -------
 .../ServerResourceIntegrationTest.java          |  12 +-
 .../rest/resources/ServerResourceTest.java      | 168 -----
 .../rest/resources/ServerShutdownTest.java      | 185 -----
 .../rest/resources/UsageResourceTest.java       | 443 ------------
 .../rest/resources/VersionResourceTest.java     |  52 --
 .../rest/security/PasswordHasherTest.java       |  37 -
 .../brooklynnode/DeployBlueprintTest.java       |  89 ---
 .../rest/testing/BrooklynRestApiTest.java       | 204 ------
 .../rest/testing/BrooklynRestResourceTest.java  | 154 ----
 .../rest/testing/mocks/CapitalizePolicy.java    |  33 -
 .../rest/testing/mocks/EverythingGroup.java     |  27 -
 .../rest/testing/mocks/EverythingGroupImpl.java |  32 -
 .../rest/testing/mocks/NameMatcherGroup.java    |  30 -
 .../testing/mocks/NameMatcherGroupImpl.java     |  33 -
 .../rest/testing/mocks/RestMockApp.java         |  24 -
 .../rest/testing/mocks/RestMockAppBuilder.java  |  39 --
 .../testing/mocks/RestMockSimpleEntity.java     | 103 ---
 .../testing/mocks/RestMockSimplePolicy.java     |  64 --
 .../util/BrooklynRestResourceUtilsTest.java     | 213 ------
 .../rest/util/EntityLocationUtilsTest.java      |  72 --
 .../rest/util/HaHotStateCheckClassResource.java |  38 -
 .../rest/util/HaHotStateCheckResource.java      |  44 --
 .../util/NullHttpServletRequestProvider.java    |  46 --
 .../rest/util/NullServletConfigProvider.java    |  51 --
 ...rooklynJacksonSerializerIntegrationTest.java | 173 +++++
 .../json/BrooklynJacksonSerializerTest.java     | 399 -----------
 .../resources/brooklyn/scanning.catalog.bom     |  19 -
 utils/rest-swagger/pom.xml                      |  28 +-
 .../rest/apidoc/ApiListingResource.java         | 260 -------
 .../rest/apidoc/RestApiResourceScanner.java     |  23 +-
 397 files changed, 26076 insertions(+), 14868 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/79b98c67/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
----------------------------------------------------------------------
diff --cc rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
index 0000000,986855e..f24dcf9
mode 000000,100644..100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/ApplicationResource.java
@@@ -1,0 -1,451 +1,474 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing,
+  * software distributed under the License is distributed on an
+  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  * KIND, either express or implied.  See the License for the
+  * specific language governing permissions and limitations
+  * under the License.
+  */
+ package org.apache.brooklyn.rest.resources;
+ 
+ import static com.google.common.base.Preconditions.checkNotNull;
+ import static javax.ws.rs.core.Response.created;
+ import static javax.ws.rs.core.Response.status;
+ import static javax.ws.rs.core.Response.Status.ACCEPTED;
+ import static org.apache.brooklyn.rest.util.WebResourceUtils.serviceAbsoluteUriBuilder;
+ 
+ import java.net.URI;
+ import java.net.URISyntaxException;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.Map;
+ 
+ import javax.ws.rs.core.Context;
+ import javax.ws.rs.core.Response;
+ import javax.ws.rs.core.Response.ResponseBuilder;
+ import javax.ws.rs.core.UriInfo;
+ 
+ import org.apache.brooklyn.api.entity.Application;
+ import org.apache.brooklyn.api.entity.Entity;
+ import org.apache.brooklyn.api.entity.EntitySpec;
+ import org.apache.brooklyn.api.entity.Group;
+ import org.apache.brooklyn.api.location.Location;
+ import org.apache.brooklyn.api.mgmt.Task;
+ import org.apache.brooklyn.api.objs.BrooklynObject;
+ import org.apache.brooklyn.api.sensor.AttributeSensor;
+ import org.apache.brooklyn.api.sensor.Sensor;
+ import org.apache.brooklyn.api.typereg.RegisteredType;
+ import org.apache.brooklyn.core.config.ConstraintViolationException;
+ import org.apache.brooklyn.core.entity.Attributes;
+ import org.apache.brooklyn.core.entity.EntityPredicates;
+ import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+ import org.apache.brooklyn.core.entity.trait.Startable;
+ import org.apache.brooklyn.core.mgmt.EntityManagementUtils;
+ import org.apache.brooklyn.core.mgmt.EntityManagementUtils.CreationResult;
+ import org.apache.brooklyn.core.mgmt.entitlement.EntitlementPredicates;
+ import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+ import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.EntityAndItem;
+ import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.StringAndArgument;
+ import org.apache.brooklyn.core.sensor.Sensors;
+ import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
+ import org.apache.brooklyn.core.typereg.RegisteredTypes;
+ import org.apache.brooklyn.entity.group.AbstractGroup;
+ import org.apache.brooklyn.rest.api.ApplicationApi;
+ import org.apache.brooklyn.rest.domain.ApplicationSpec;
+ import org.apache.brooklyn.rest.domain.ApplicationSummary;
+ import org.apache.brooklyn.rest.domain.EntityDetail;
+ import org.apache.brooklyn.rest.domain.EntitySummary;
+ import org.apache.brooklyn.rest.domain.TaskSummary;
+ import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+ import org.apache.brooklyn.rest.transform.ApplicationTransformer;
+ import org.apache.brooklyn.rest.transform.EntityTransformer;
+ import org.apache.brooklyn.rest.transform.TaskTransformer;
+ import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils;
+ import org.apache.brooklyn.rest.util.WebResourceUtils;
+ import org.apache.brooklyn.util.collections.MutableMap;
+ import org.apache.brooklyn.util.core.ResourceUtils;
+ import org.apache.brooklyn.util.exceptions.Exceptions;
+ import org.apache.brooklyn.util.exceptions.UserFacingException;
+ import org.apache.brooklyn.util.guava.Maybe;
+ import org.apache.brooklyn.util.javalang.JavaClassNames;
++import org.apache.brooklyn.util.net.Urls;
+ import org.apache.brooklyn.util.text.Strings;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ 
 -import com.google.common.base.Throwables;
++import com.google.common.base.Preconditions;
+ import com.google.common.collect.FluentIterable;
+ import com.google.common.collect.ImmutableMap;
+ import com.google.common.collect.Iterables;
+ import com.google.common.collect.Lists;
+ 
+ @HaHotStateRequired
+ public class ApplicationResource extends AbstractBrooklynRestResource implements ApplicationApi {
+ 
+     private static final Logger log = LoggerFactory.getLogger(ApplicationResource.class);
+ 
+     @Context
+     private UriInfo uriInfo;
+ 
+     private EntityDetail fromEntity(Entity entity) {
+         Boolean serviceUp = entity.getAttribute(Attributes.SERVICE_UP);
+ 
+         Lifecycle serviceState = entity.getAttribute(Attributes.SERVICE_STATE_ACTUAL);
+ 
+         String iconUrl = entity.getIconUrl();
+         if (iconUrl!=null) {
+             if (brooklyn().isUrlServerSideAndSafe(iconUrl))
+                 // route to server if it is a server-side url
+                 iconUrl = EntityTransformer.entityUri(entity, ui.getBaseUriBuilder())+"/icon";
+         }
+ 
+         List<EntitySummary> children = Lists.newArrayList();
+         if (!entity.getChildren().isEmpty()) {
+             for (Entity child : entity.getChildren()) {
+                 children.add(fromEntity(child));
+             }
+         }
+ 
+         String parentId = null;
+         if (entity.getParent()!= null) {
+             parentId = entity.getParent().getId();
+         }
+ 
+         List<String> groupIds = Lists.newArrayList();
+         if (!entity.groups().isEmpty()) {
+             groupIds.addAll(entitiesIdAsArray(entity.groups()));
+         }
+ 
+         List<Map<String, String>> members = Lists.newArrayList();
+         if (entity instanceof Group) {
+             // use attribute instead of method in case it is read-only
+             Collection<Entity> memberEntities = entity.getAttribute(AbstractGroup.GROUP_MEMBERS);
+             if (memberEntities != null && !memberEntities.isEmpty())
+                 members.addAll(entitiesIdAndNameAsList(memberEntities));
+         }
+ 
+         return new EntityDetail(
+                 entity.getApplicationId(),
+                 entity.getId(),
+                 parentId,
+                 entity.getDisplayName(),
+                 entity.getEntityType().getName(),
+                 serviceUp,
+                 serviceState,
+                 iconUrl,
+                 entity.getCatalogItemId(),
+                 children,
+                 groupIds,
+                 members);
+     }
+ 
+     private List<Map<String, String>> entitiesIdAndNameAsList(Collection<? extends Entity> entities) {
+         List<Map<String, String>> members = Lists.newArrayList();
+         for (Entity entity : entities) {
+             if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+                 members.add(ImmutableMap.of("id", entity.getId(), "name", entity.getDisplayName()));
+             }
+         }
+         return members;
+     }
+ 
+     private List<String> entitiesIdAsArray(Iterable<? extends Entity> entities) {
+         List<String> ids = Lists.newArrayList();
+         for (Entity entity : entities) {
+             if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+                 ids.add(entity.getId());
+             }
+         }
+         return ids;
+     }
+ 
+     @Override
+     public List<EntityDetail> fetch(String entityIds) {
+ 
+         List<EntityDetail> entitySummaries = Lists.newArrayList();
+         for (Entity application : mgmt().getApplications()) {
+             entitySummaries.add(fromEntity(application));
+         }
+ 
+         if (entityIds != null) {
+             for (String entityId: entityIds.split(",")) {
+                 Entity entity = mgmt().getEntityManager().getEntity(entityId.trim());
+                 while (entity != null && entity.getParent() != null) {
+                     if (Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY, entity)) {
+                         entitySummaries.add(fromEntity(entity));
+                     }
+                     entity = entity.getParent();
+                 }
+             }
+         }
+         return entitySummaries;
+     }
+ 
+     @Override
+     public List<ApplicationSummary> list(String typeRegex) {
+         if (Strings.isBlank(typeRegex)) {
+             typeRegex = ".*";
+         }
+         return FluentIterable
+                 .from(mgmt().getApplications())
+                 .filter(EntitlementPredicates.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_ENTITY))
+                 .filter(EntityPredicates.hasInterfaceMatching(typeRegex))
+                 .transform(ApplicationTransformer.fromApplication(ui.getBaseUriBuilder()))
+                 .toList();
+     }
+ 
+     @Override
+     public ApplicationSummary get(String application) {
+         return ApplicationTransformer.summaryFromApplication(brooklyn().getApplication(application), ui.getBaseUriBuilder());
+     }
+ 
+     @Override
+     public Response create(ApplicationSpec applicationSpec) {
+         return createFromAppSpec(applicationSpec);
+     }
+ 
+     /** @deprecated since 0.7.0 see #create */ @Deprecated
+     protected Response createFromAppSpec(ApplicationSpec applicationSpec) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, applicationSpec)) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to start application %s",
+                 Entitlements.getEntitlementContext().user(), applicationSpec);
+         }
+ 
+         checkApplicationTypesAreValid(applicationSpec);
+         checkLocationsAreValid(applicationSpec);
+         // TODO duplicate prevention
+         List<Location> locations = brooklyn().getLocations(applicationSpec);
+         Application app = brooklyn().create(applicationSpec);
+         Task<?> t = brooklyn().start(app, locations);
+         TaskSummary ts = TaskTransformer.fromTask(ui.getBaseUriBuilder()).apply(t);
+         URI ref = serviceAbsoluteUriBuilder(uriInfo.getBaseUriBuilder(), ApplicationApi.class, "get")
+                 .build(app.getApplicationId());
+         return created(ref).entity(ts).build();
+     }
+ 
+     @Override
+     public Response createFromYaml(String yaml) {
+         // First of all, see if it's a URL
 -        URI uri;
++        Preconditions.checkNotNull(yaml, "Blueprint must not be null");
++        URI uri = null;
+         try {
 -            uri = new URI(yaml);
++            String yamlUrl = yaml.trim();
++            if (Urls.isUrlWithProtocol(yamlUrl)) {
++                uri = new URI(yamlUrl);
++            }
+         } catch (URISyntaxException e) {
+             // It's not a URI then...
+             uri = null;
+         }
+         if (uri != null) {
+             log.debug("Create app called with URI; retrieving contents: {}", uri);
 -            yaml = ResourceUtils.create(mgmt()).getResourceAsString(uri.toString());
++            try {
++                yaml = ResourceUtils.create(mgmt()).getResourceAsString(uri.toString());
++            } catch (Exception e) {
++                Exceptions.propagateIfFatal(e);
++                throw new UserFacingException("Cannot resolve URL: "+uri, e);
++            }
+         }
+ 
+         log.debug("Creating app from yaml:\n{}", yaml);
 -        EntitySpec<? extends Application> spec = createEntitySpecForApplication(yaml);
+ 
++        EntitySpec<? extends Application> spec;
++        try {
++            spec = createEntitySpecForApplication(yaml);
++        } catch (Exception e) {
++            Exceptions.propagateIfFatal(e);
++            UserFacingException userFacing = Exceptions.getFirstThrowableOfType(e, UserFacingException.class);
++            if (userFacing!=null) {
++                log.debug("Throwing "+userFacing+", wrapped in "+e);
++                throw userFacing;
++            }
++            throw WebResourceUtils.badRequest(e, "Error in blueprint");
++        }
++        
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.DEPLOY_APPLICATION, spec)) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to start application %s",
+                 Entitlements.getEntitlementContext().user(), yaml);
+         }
+ 
 -        return launch(yaml, spec);
++        try {
++            return launch(yaml, spec);
++        } catch (Exception e) {
++            throw WebResourceUtils.badRequest(e, "Error launching blueprint");
++        }
+     }
+ 
+     private Response launch(String yaml, EntitySpec<? extends Application> spec) {
+         try {
+             Application app = EntityManagementUtils.createUnstarted(mgmt(), spec);
+             CreationResult<Application,Void> result = EntityManagementUtils.start(app);
+ 
+             boolean isEntitled = Entitlements.isEntitled(
+                     mgmt().getEntitlementManager(),
+                     Entitlements.INVOKE_EFFECTOR,
+                     EntityAndItem.of(app, StringAndArgument.of(Startable.START.getName(), null)));
+ 
+             if (!isEntitled) {
+                 throw WebResourceUtils.forbidden("User '%s' is not authorized to start application %s",
+                     Entitlements.getEntitlementContext().user(), spec.getType());
+             }
+ 
+             log.info("Launched from YAML: " + yaml + " -> " + app + " (" + result.task() + ")");
+ 
+             URI ref = serviceAbsoluteUriBuilder(ui.getBaseUriBuilder(), ApplicationApi.class, "get").build(app.getApplicationId());
+             ResponseBuilder response = created(ref);
+             if (result.task() != null)
+                 response.entity(TaskTransformer.fromTask(ui.getBaseUriBuilder()).apply(result.task()));
+             return response.build();
+         } catch (ConstraintViolationException e) {
+             throw new UserFacingException(e);
+         } catch (Exception e) {
+             throw Exceptions.propagate(e);
+         }
+     }
+ 
+     @Override
+     public Response createPoly(byte[] inputToAutodetectType) {
+         log.debug("Creating app from autodetecting input");
+ 
+         boolean looksLikeLegacy = false;
+         Exception legacyFormatException = null;
+         // attempt legacy format
+         try {
+             ApplicationSpec appSpec = mapper().readValue(inputToAutodetectType, ApplicationSpec.class);
+             if (appSpec.getType() != null || appSpec.getEntities() != null) {
+                 looksLikeLegacy = true;
+             }
+             return createFromAppSpec(appSpec);
+         } catch (Exception e) {
+             Exceptions.propagateIfFatal(e);
+             legacyFormatException = e;
+             log.debug("Input is not legacy ApplicationSpec JSON (will try others): "+e, e);
+         }
+ 
+         //TODO infer encoding from request
+         String potentialYaml = new String(inputToAutodetectType);
 -        EntitySpec<? extends Application> spec = createEntitySpecForApplication(potentialYaml);
++        EntitySpec<? extends Application> spec;
++        try {
++            spec = createEntitySpecForApplication(potentialYaml);
++        } catch (Exception e) {
++            Exceptions.propagateIfFatal(e);
++            
++            // TODO if not yaml/json - try ZIP, etc
++            
++            throw WebResourceUtils.badRequest(e, "Error in blueprint");
++        }
+ 
 -        // TODO not json - try ZIP, etc
+ 
+         if (spec != null) {
+             return launch(potentialYaml, spec);
+         } else if (looksLikeLegacy) {
 -            throw Throwables.propagate(legacyFormatException);
++            throw Exceptions.propagate(legacyFormatException);
+         } else {
+             return Response.serverError().entity("Unsupported format; not able to autodetect.").build();
+         }
+     }
+ 
+     @Override
+     public Response createFromForm(String contents) {
+         log.debug("Creating app from form");
+         return createPoly(contents.getBytes());
+     }
+ 
+     @Override
+     public Response delete(String application) {
+         Application app = brooklyn().getApplication(application);
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.INVOKE_EFFECTOR, Entitlements.EntityAndItem.of(app,
+             StringAndArgument.of(Entitlements.LifecycleEffectors.DELETE, null)))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to delete application %s",
+                 Entitlements.getEntitlementContext().user(), app);
+         }
+         Task<?> t = brooklyn().destroy(app);
+         TaskSummary ts = TaskTransformer.fromTask(ui.getBaseUriBuilder()).apply(t);
+         return status(ACCEPTED).entity(ts).build();
+     }
+ 
+     private EntitySpec<? extends Application> createEntitySpecForApplication(String potentialYaml) {
 -        try {
 -            return EntityManagementUtils.createEntitySpecForApplication(mgmt(), potentialYaml);
 -        } catch (Exception e) {
 -            // An IllegalArgumentException for creating the entity spec gets wrapped in a ISE, and possibly a Compound.
 -            // But we want to return a 400 rather than 500, so ensure we throw IAE.
 -            IllegalArgumentException iae = (IllegalArgumentException) Exceptions.getFirstThrowableOfType(e, IllegalArgumentException.class);
 -            if (iae != null) {
 -                throw new IllegalArgumentException("Cannot create spec for app: "+iae.getMessage(), e);
 -            } else {
 -                throw Exceptions.propagate(e);
 -            }
 -        }
++        return EntityManagementUtils.createEntitySpecForApplication(mgmt(), potentialYaml);
+     }
+ 
+     private void checkApplicationTypesAreValid(ApplicationSpec applicationSpec) {
+         String appType = applicationSpec.getType();
+         if (appType != null) {
+             checkEntityTypeIsValid(appType);
+ 
+             if (applicationSpec.getEntities() != null) {
+                 throw WebResourceUtils.preconditionFailed("Application given explicit type '%s' must not define entities", appType);
+             }
+             return;
+         }
+ 
+         for (org.apache.brooklyn.rest.domain.EntitySpec entitySpec : applicationSpec.getEntities()) {
+             String entityType = entitySpec.getType();
+             checkEntityTypeIsValid(checkNotNull(entityType, "entityType"));
+         }
+     }
+ 
+     private void checkSpecTypeIsValid(String type, Class<? extends BrooklynObject> subType) {
+         Maybe<RegisteredType> typeV = RegisteredTypes.tryValidate(mgmt().getTypeRegistry().get(type), RegisteredTypeLoadingContexts.spec(subType));
+         if (!typeV.isNull()) {
+             // found, throw if any problem
+             typeV.get();
+             return;
+         }
+ 
+         // not found, try classloading
+         try {
+             brooklyn().getCatalogClassLoader().loadClass(type);
+         } catch (ClassNotFoundException e) {
+             log.debug("Class not found for type '" + type + "'; reporting 404", e);
+             throw WebResourceUtils.notFound("Undefined type '%s'", type);
+         }
+         log.info(JavaClassNames.simpleClassName(subType)+" type '{}' not defined in catalog but is on classpath; continuing", type);
+     }
+ 
+     private void checkEntityTypeIsValid(String type) {
+         checkSpecTypeIsValid(type, Entity.class);
+     }
+ 
+     @SuppressWarnings("deprecation")
+     private void checkLocationsAreValid(ApplicationSpec applicationSpec) {
+         for (String locationId : applicationSpec.getLocations()) {
+             locationId = BrooklynRestResourceUtils.fixLocation(locationId);
+             if (!brooklyn().getLocationRegistry().canMaybeResolve(locationId) && brooklyn().getLocationRegistry().getDefinedLocationById(locationId)==null) {
+                 throw WebResourceUtils.notFound("Undefined location '%s'", locationId);
+             }
+         }
+     }
+ 
+     @Override
+     public List<EntitySummary> getDescendants(String application, String typeRegex) {
+         return EntityTransformer.entitySummaries(brooklyn().descendantsOfType(application, application, typeRegex), ui.getBaseUriBuilder());
+     }
+ 
+     @Override
+     public Map<String, Object> getDescendantsSensor(String application, String sensor, String typeRegex) {
+         Iterable<Entity> descs = brooklyn().descendantsOfType(application, application, typeRegex);
+         return getSensorMap(sensor, descs);
+     }
+ 
+     public static Map<String, Object> getSensorMap(String sensor, Iterable<Entity> descs) {
+         if (Iterables.isEmpty(descs))
+             return Collections.emptyMap();
+         Map<String, Object> result = MutableMap.of();
+         Iterator<Entity> di = descs.iterator();
+         Sensor<?> s = null;
+         while (di.hasNext()) {
+             Entity potentialSource = di.next();
+             s = potentialSource.getEntityType().getSensor(sensor);
+             if (s!=null) break;
+         }
+         if (s==null)
+             s = Sensors.newSensor(Object.class, sensor);
+         if (!(s instanceof AttributeSensor<?>)) {
+             log.warn("Cannot retrieve non-attribute sensor "+s+" for entities; returning empty map");
+             return result;
+         }
+         for (Entity e: descs) {
+             Object v = null;
+             try {
+                 v = e.getAttribute((AttributeSensor<?>)s);
+             } catch (Exception exc) {
+                 Exceptions.propagateIfFatal(exc);
+                 log.warn("Error retrieving sensor "+s+" for "+e+" (ignoring): "+exc);
+             }
+             if (v!=null)
+                 result.put(e.getId(), v);
+         }
+         return result;
+     }
+ 
+ }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/79b98c67/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
----------------------------------------------------------------------
diff --cc rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
index 0000000,d7b206a..77f8470
mode 000000,100644..100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/resources/CatalogResource.java
@@@ -1,0 -1,509 +1,516 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one
+  * or more contributor license agreements.  See the NOTICE file
+  * distributed with this work for additional information
+  * regarding copyright ownership.  The ASF licenses this file
+  * to you under the Apache License, Version 2.0 (the
+  * "License"); you may not use this file except in compliance
+  * with the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing,
+  * software distributed under the License is distributed on an
+  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  * KIND, either express or implied.  See the License for the
+  * specific language governing permissions and limitations
+  * under the License.
+  */
+ package org.apache.brooklyn.rest.resources;
+ 
+ import java.net.URI;
+ import java.util.ArrayList;
+ import java.util.Iterator;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.NoSuchElementException;
+ import java.util.Set;
+ 
+ import javax.annotation.Nullable;
+ import javax.ws.rs.core.MediaType;
+ import javax.ws.rs.core.Response;
+ import javax.ws.rs.core.Response.Status;
+ 
+ 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.entity.EntitySpec;
+ import org.apache.brooklyn.api.location.Location;
+ import org.apache.brooklyn.api.location.LocationSpec;
+ import org.apache.brooklyn.api.policy.Policy;
+ import org.apache.brooklyn.api.policy.PolicySpec;
+ import org.apache.brooklyn.api.typereg.RegisteredType;
+ import org.apache.brooklyn.core.catalog.CatalogPredicates;
+ import org.apache.brooklyn.core.catalog.internal.BasicBrooklynCatalog;
+ import org.apache.brooklyn.core.catalog.internal.CatalogDto;
+ import org.apache.brooklyn.core.catalog.internal.CatalogItemComparator;
+ import org.apache.brooklyn.core.catalog.internal.CatalogUtils;
+ import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
+ import org.apache.brooklyn.core.mgmt.entitlement.Entitlements.StringAndArgument;
+ import org.apache.brooklyn.core.typereg.RegisteredTypeLoadingContexts;
+ import org.apache.brooklyn.core.typereg.RegisteredTypePredicates;
+ import org.apache.brooklyn.core.typereg.RegisteredTypes;
+ import org.apache.brooklyn.rest.api.CatalogApi;
+ import org.apache.brooklyn.rest.domain.ApiError;
+ import org.apache.brooklyn.rest.domain.CatalogEntitySummary;
+ import org.apache.brooklyn.rest.domain.CatalogItemSummary;
+ import org.apache.brooklyn.rest.domain.CatalogLocationSummary;
+ import org.apache.brooklyn.rest.domain.CatalogPolicySummary;
+ import org.apache.brooklyn.rest.filter.HaHotStateRequired;
+ import org.apache.brooklyn.rest.transform.CatalogTransformer;
+ import org.apache.brooklyn.rest.util.WebResourceUtils;
+ import org.apache.brooklyn.util.collections.MutableMap;
+ import org.apache.brooklyn.util.collections.MutableSet;
+ import org.apache.brooklyn.util.core.ResourceUtils;
+ import org.apache.brooklyn.util.exceptions.Exceptions;
+ import org.apache.brooklyn.util.text.StringPredicates;
+ 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.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 javax.ws.rs.core.UriInfo;
+ import org.apache.brooklyn.util.guava.Maybe;
+ 
+ @HaHotStateRequired
+ public class CatalogResource extends AbstractBrooklynRestResource implements CatalogApi {
+ 
+     private static final Logger log = LoggerFactory.getLogger(CatalogResource.class);
+     
+     @SuppressWarnings("rawtypes")
+     private Function<CatalogItem, CatalogItemSummary> toCatalogItemSummary(final UriInfo ui) {
+         return new Function<CatalogItem, CatalogItemSummary>() {
+             @Override
+             public CatalogItemSummary apply(@Nullable CatalogItem input) {
+                 return CatalogTransformer.catalogItemSummary(brooklyn(), input, ui.getBaseUriBuilder());
+             }
+         };
+     };
+ 
+     static Set<String> missingIcons = MutableSet.of();
+     
+     @Override
+     public Response create(String yaml) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ADD_CATALOG_ITEM, yaml)) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to add catalog item",
+                 Entitlements.getEntitlementContext().user());
+         }
+         
+         Iterable<? extends CatalogItem<?, ?>> items; 
+         try {
+             items = brooklyn().getCatalog().addItems(yaml);
 -        } catch (IllegalArgumentException e) {
 -            return Response.status(Status.BAD_REQUEST)
 -                    .type(MediaType.APPLICATION_JSON)
 -                    .entity(ApiError.of(e))
 -                    .build();
++        } catch (Exception e) {
++            Exceptions.propagateIfFatal(e);
++            return ApiError.of(e).asBadRequestResponseJson();
+         }
+ 
+         log.info("REST created catalog items: "+items);
+ 
+         Map<String,Object> result = MutableMap.of();
+         
+         for (CatalogItem<?,?> item: items) {
 -            result.put(item.getId(), CatalogTransformer.catalogItemSummary(brooklyn(), item, ui.getBaseUriBuilder()));
++            try {
++                result.put(item.getId(), CatalogTransformer.catalogItemSummary(brooklyn(), item, ui.getBaseUriBuilder()));
++            } catch (Throwable t) {
++                log.warn("Error loading catalog item '"+item+"' (rethrowing): "+t);
++                // unfortunately items are already added to the catalog and hard to remove,
++                // but at least let the user know;
++                // happens eg if a class refers to a missing class, like 
++                // loading nosql items including mongo without the mongo bson class on the classpath 
++                throw Exceptions.propagateAnnotated("At least one unusable item was added ("+item.getId()+")", t);
++            }
+         }
+         return Response.status(Status.CREATED).entity(result).build();
+     }
+ 
+     @SuppressWarnings("deprecation")
+     @Override
+     public Response resetXml(String xml, boolean ignoreErrors) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, null) ||
+             !Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.ADD_CATALOG_ITEM, null)) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog",
+                 Entitlements.getEntitlementContext().user());
+         }
+ 
+         ((BasicBrooklynCatalog)mgmt().getCatalog()).reset(CatalogDto.newDtoFromXmlContents(xml, "REST reset"), !ignoreErrors);
+         return Response.ok().build();
+     }
+     
+     @Override
+     @Deprecated
+     public void deleteEntity_0_7_0(String entityId) throws Exception {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(entityId, "delete"))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog",
+                 Entitlements.getEntitlementContext().user());
+         }
+         try {
+             Maybe<RegisteredType> item = RegisteredTypes.tryValidate(mgmt().getTypeRegistry().get(entityId), RegisteredTypeLoadingContexts.spec(Entity.class));
+             if (item.isNull()) {
+                 throw WebResourceUtils.notFound("Entity with id '%s' not found", entityId);
+             }
+             if (item.isAbsent()) {
+                 throw WebResourceUtils.notFound("Item with id '%s' is not an entity", entityId);
+             }
+ 
+             brooklyn().getCatalog().deleteCatalogItem(item.get().getSymbolicName(), item.get().getVersion());
+ 
+         } catch (NoSuchElementException e) {
+             // shouldn't come here
+             throw WebResourceUtils.notFound("Entity with id '%s' could not be deleted", entityId);
+ 
+         }
+     }
+ 
+     @Override
+     public void deleteApplication(String symbolicName, String version) throws Exception {
+         deleteEntity(symbolicName, version);
+     }
+ 
+     @Override
+     public void deleteEntity(String symbolicName, String version) throws Exception {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(symbolicName+(Strings.isBlank(version) ? "" : ":"+version), "delete"))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog",
+                 Entitlements.getEntitlementContext().user());
+         }
+         
+         RegisteredType item = mgmt().getTypeRegistry().get(symbolicName, version);
+         if (item == null) {
+             throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", symbolicName, version);
+         } else if (!RegisteredTypePredicates.IS_ENTITY.apply(item) && !RegisteredTypePredicates.IS_APPLICATION.apply(item)) {
+             throw WebResourceUtils.preconditionFailed("Item with id '%s:%s' not an entity", symbolicName, version);
+         } else {
+             brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+         }
+     }
+ 
+     @Override
+     public void deletePolicy(String policyId, String version) throws Exception {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(policyId+(Strings.isBlank(version) ? "" : ":"+version), "delete"))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog",
+                 Entitlements.getEntitlementContext().user());
+         }
+         
+         RegisteredType item = mgmt().getTypeRegistry().get(policyId, version);
+         if (item == null) {
+             throw WebResourceUtils.notFound("Policy with id '%s:%s' not found", policyId, version);
+         } else if (!RegisteredTypePredicates.IS_POLICY.apply(item)) {
+             throw WebResourceUtils.preconditionFailed("Item with id '%s:%s' not a policy", policyId, version);
+         } else {
+             brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+         }
+     }
+ 
+     @Override
+     public void deleteLocation(String locationId, String version) throws Exception {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(locationId+(Strings.isBlank(version) ? "" : ":"+version), "delete"))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog",
+                 Entitlements.getEntitlementContext().user());
+         }
+         
+         RegisteredType item = mgmt().getTypeRegistry().get(locationId, version);
+         if (item == null) {
+             throw WebResourceUtils.notFound("Location with id '%s:%s' not found", locationId, version);
+         } else if (!RegisteredTypePredicates.IS_LOCATION.apply(item)) {
+             throw WebResourceUtils.preconditionFailed("Item with id '%s:%s' not a location", locationId, version);
+         } else {
+             brooklyn().getCatalog().deleteCatalogItem(item.getSymbolicName(), item.getVersion());
+         }
+     }
+ 
+     @Override
+     public List<CatalogEntitySummary> listEntities(String regex, String fragment, boolean allVersions) {
+         Predicate<CatalogItem<Entity, EntitySpec<?>>> filter =
+                 Predicates.and(
+                         CatalogPredicates.IS_ENTITY,
+                         CatalogPredicates.<Entity, EntitySpec<?>>disabled(false));
+         List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions);
+         return castList(result, CatalogEntitySummary.class);
+     }
+ 
+     @Override
+     public List<CatalogItemSummary> listApplications(String regex, String fragment, boolean allVersions) {
+         @SuppressWarnings("unchecked")
+         Predicate<CatalogItem<Application, EntitySpec<? extends Application>>> filter =
+                 Predicates.and(
+                         CatalogPredicates.IS_TEMPLATE,
+                         CatalogPredicates.<Application,EntitySpec<? extends Application>>deprecated(false),
+                         CatalogPredicates.<Application,EntitySpec<? extends Application>>disabled(false));
+         return getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions);
+     }
+ 
+     @Override
+     @Deprecated
+     public CatalogEntitySummary getEntity_0_7_0(String entityId) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, entityId)) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to see catalog entry",
+                 Entitlements.getEntitlementContext().user());
+         }
+ 
+         CatalogItem<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, ui.getBaseUriBuilder());
+     }
+     
+     @Override
+     public CatalogEntitySummary getEntity(String symbolicName, String version) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, symbolicName+(Strings.isBlank(version)?"":":"+version))) {
+             throw WebResourceUtils.forbidden("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<Entity, EntitySpec<?>> result =
+               (CatalogItem<Entity, EntitySpec<?>>) brooklyn().getCatalog().getCatalogItem(symbolicName, version);
+ 
+         if (result==null) {
+             throw WebResourceUtils.notFound("Entity with id '%s:%s' not found", symbolicName, version);
+         }
+ 
+         return CatalogTransformer.catalogEntitySummary(brooklyn(), result, ui.getBaseUriBuilder());
+     }
+ 
+     @Override
+     @Deprecated
+     public CatalogEntitySummary getApplication_0_7_0(String applicationId) throws Exception {
+         return getEntity_0_7_0(applicationId);
+     }
+ 
+     @Override
+     public CatalogEntitySummary getApplication(String symbolicName, String version) {
+         return getEntity(symbolicName, version);
+     }
+ 
+     @Override
+     public List<CatalogPolicySummary> listPolicies(String regex, String fragment, boolean allVersions) {
+         Predicate<CatalogItem<Policy, PolicySpec<?>>> filter =
+                 Predicates.and(
+                         CatalogPredicates.IS_POLICY,
+                         CatalogPredicates.<Policy, PolicySpec<?>>disabled(false));
+         List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions);
+         return castList(result, CatalogPolicySummary.class);
+     }
+ 
+     @Override
+     @Deprecated
+     public CatalogPolicySummary getPolicy_0_7_0(String policyId) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, policyId)) {
+             throw WebResourceUtils.forbidden("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, ui.getBaseUriBuilder());
+     }
+ 
+     @Override
+     public CatalogPolicySummary getPolicy(String policyId, String version) throws Exception {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, policyId+(Strings.isBlank(version)?"":":"+version))) {
+             throw WebResourceUtils.forbidden("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, ui.getBaseUriBuilder());
+     }
+ 
+     @Override
+     public List<CatalogLocationSummary> listLocations(String regex, String fragment, boolean allVersions) {
+         Predicate<CatalogItem<Location, LocationSpec<?>>> filter =
+                 Predicates.and(
+                         CatalogPredicates.IS_LOCATION,
+                         CatalogPredicates.<Location, LocationSpec<?>>disabled(false));
+         List<CatalogItemSummary> result = getCatalogItemSummariesMatchingRegexFragment(filter, regex, fragment, allVersions);
+         return castList(result, CatalogLocationSummary.class);
+     }
+ 
+     @Override
+     @Deprecated
+     public CatalogLocationSummary getLocation_0_7_0(String locationId) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, locationId)) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to see catalog entry",
+                 Entitlements.getEntitlementContext().user());
+         }
+ 
+         CatalogItem<? extends Location, LocationSpec<?>> result =
+             CatalogUtils.getCatalogItemOptionalVersion(mgmt(), Location.class, locationId);
+ 
+         if (result==null) {
+             throw WebResourceUtils.notFound("Location with id '%s' not found", locationId);
+         }
+ 
+         return CatalogTransformer.catalogLocationSummary(brooklyn(), result, ui.getBaseUriBuilder());
+     }
+ 
+     @Override
+     public CatalogLocationSummary getLocation(String locationId, String version) throws Exception {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, locationId+(Strings.isBlank(version)?"":":"+version))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to see catalog entry",
+                 Entitlements.getEntitlementContext().user());
+         }
+ 
+         @SuppressWarnings("unchecked")
+         CatalogItem<? extends Location, LocationSpec<?>> result =
+                 (CatalogItem<? extends Location, LocationSpec<?>>)brooklyn().getCatalog().getCatalogItem(locationId, version);
+ 
+         if (result==null) {
+           throw WebResourceUtils.notFound("Location with id '%s:%s' not found", locationId, version);
+         }
+ 
+         return CatalogTransformer.catalogLocationSummary(brooklyn(), result, ui.getBaseUriBuilder());
+     }
+ 
+     @SuppressWarnings({ "unchecked", "rawtypes" })
+     private <T,SpecT> List<CatalogItemSummary> getCatalogItemSummariesMatchingRegexFragment(Predicate<CatalogItem<T,SpecT>> type, String regex, String fragment, boolean allVersions) {
+         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)));
+         if (!allVersions)
+             filters.add(CatalogPredicates.isBestVersion(mgmt()));
+         
+         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, toCatalogItemSummary(ui));
+     }
+ 
+     @Override
+     @Deprecated
+     public Response getIcon_0_7_0(String itemId) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, itemId)) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to see catalog entry",
+                 Entitlements.getEntitlementContext().user());
+         }
+ 
+         return getCatalogItemIcon( mgmt().getTypeRegistry().get(itemId) );
+     }
+ 
+     @Override
+     public Response getIcon(String itemId, String version) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.SEE_CATALOG_ITEM, itemId+(Strings.isBlank(version)?"":":"+version))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to see catalog entry",
+                 Entitlements.getEntitlementContext().user());
+         }
+         
+         return getCatalogItemIcon(mgmt().getTypeRegistry().get(itemId, version));
+     }
+ 
+     @Override
+     public void setDeprecatedLegacy(String itemId, boolean deprecated) {
+         log.warn("Use of deprecated \"/catalog/entities/{itemId}/deprecated/{deprecated}\" for "+itemId
+                 +"; use \"/catalog/entities/{itemId}/deprecated\" with request body");
+         setDeprecated(itemId, deprecated);
+     }
+     
+     @SuppressWarnings("deprecation")
+     @Override
+     public void setDeprecated(String itemId, boolean deprecated) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(itemId, "deprecated"))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog",
+                     Entitlements.getEntitlementContext().user());
+         }
+         CatalogUtils.setDeprecated(mgmt(), itemId, deprecated);
+     }
+ 
+     @SuppressWarnings("deprecation")
+     @Override
+     public void setDisabled(String itemId, boolean disabled) {
+         if (!Entitlements.isEntitled(mgmt().getEntitlementManager(), Entitlements.MODIFY_CATALOG_ITEM, StringAndArgument.of(itemId, "disabled"))) {
+             throw WebResourceUtils.forbidden("User '%s' is not authorized to modify catalog",
+                     Entitlements.getEntitlementContext().user());
+         }
+         CatalogUtils.setDisabled(mgmt(), itemId, disabled);
+     }
+ 
+     private Response getCatalogItemIcon(RegisteredType 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();
+     }
+ 
+     // TODO Move to an appropriate utility class?
+     @SuppressWarnings("unchecked")
+     private static <T> List<T> castList(List<? super T> list, Class<T> elementType) {
+         List<T> result = Lists.newArrayList();
+         Iterator<? super T> li = list.iterator();
+         while (li.hasNext()) {
+             try {
+                 result.add((T) li.next());
+             } catch (Throwable throwable) {
+                 if (throwable instanceof NoClassDefFoundError) {
+                     // happens if class cannot be loaded for any reason during transformation - don't treat as fatal
+                 } else {
+                     Exceptions.propagateIfFatal(throwable);
+                 }
+                 
+                 // item cannot be transformed; we will have logged a warning earlier
+                 log.debug("Ignoring invalid catalog item: "+throwable);
+             }
+         }
+         return result;
+     }
+ }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/79b98c67/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
----------------------------------------------------------------------
diff --cc rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
index 0000000,1926d5e..6797a64
mode 000000,100644..100644
--- a/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
+++ b/rest/rest-resources/src/main/java/org/apache/brooklyn/rest/util/DefaultExceptionMapper.java
@@@ -1,0 -1,111 +1,117 @@@
+ /*
+  * 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.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;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ import org.yaml.snakeyaml.error.YAMLException;
+ 
+ @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) {
+         // EofException is thrown when the connection is reset,
+         // for example when refreshing the browser window.
+         // Don't depend on jetty, could be running in other environments as well.
+         if (throwable1.getClass().getName().equals("org.eclipse.jetty.io.EofException")) {
+             if (LOG.isTraceEnabled()) {
+                 LOG.trace("REST request running as {} threw: {}", Entitlements.getEntitlementContext(), 
+                         Exceptions.collapse(throwable1));
+             }
+             return null;
+         }
+ 
+         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);
+             }
+         }
++
++        // Before saying server error, look for a user-facing exception anywhere in the hierarchy
++        UserFacingException userFacing = Exceptions.getFirstThrowableOfType(throwable1, UserFacingException.class);
++        if (userFacing instanceof UserFacingException) {
++            return ApiError.of(userFacing.getMessage()).asBadRequestResponseJson();
++        }
+         
+         Builder rb = ApiError.builderFromThrowable(Exceptions.collapse(throwable2));
+         if (Strings.isBlank(rb.getMessage()))
+             rb.message("Internal error. Contact server administrator to consult logs for more details.");
+         return rb.build().asResponse(Status.INTERNAL_SERVER_ERROR, MediaType.APPLICATION_JSON_TYPE);
+     }
+ }


Mime
View raw message