Repository: incubator-brooklyn Updated Branches: refs/heads/master 2ed62d61b -> 2aac052fb
http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/security/provider/TestSecurityProvider.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/security/provider/TestSecurityProvider.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/security/provider/TestSecurityProvider.java new file mode 100644 index 0000000..cad251f --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/security/provider/TestSecurityProvider.java @@ -0,0 +1,46 @@ +/* + * 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.security.provider; + +import javax.servlet.http.HttpSession; + +import org.apache.http.auth.UsernamePasswordCredentials; + +public class TestSecurityProvider implements SecurityProvider { + + public static final String USER = "test"; + public static final String PASSWORD = "opensesame"; + public static final UsernamePasswordCredentials CREDENTIAL = + new UsernamePasswordCredentials(TestSecurityProvider.USER, TestSecurityProvider.PASSWORD); + + @Override + public boolean isAuthenticated(HttpSession session) { + return false; + } + + @Override + public boolean authenticate(HttpSession session, String user, String password) { + return USER.equals(user) && PASSWORD.equals(password); + } + + @Override + public boolean logout(HttpSession session) { + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java new file mode 100644 index 0000000..252c972 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestApiTest.java @@ -0,0 +1,185 @@ +/* + * 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.testing; + +import org.apache.brooklyn.camp.brooklyn.BrooklynCampPlatformLauncherNoServer; +import org.codehaus.jackson.map.ObjectMapper; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeMethod; + +import com.google.common.base.Preconditions; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.core.DefaultResourceConfig; +import com.sun.jersey.test.framework.AppDescriptor; +import com.sun.jersey.test.framework.JerseyTest; +import com.sun.jersey.test.framework.LowLevelAppDescriptor; + +import brooklyn.entity.basic.Entities; +import brooklyn.location.LocationRegistry; +import brooklyn.location.basic.BasicLocationRegistry; +import brooklyn.management.ManagementContext; +import brooklyn.management.ManagementContextInjectable; +import brooklyn.management.internal.LocalManagementContext; +import org.apache.brooklyn.rest.BrooklynRestApi; +import org.apache.brooklyn.rest.BrooklynRestApiLauncherTest; +import org.apache.brooklyn.rest.util.BrooklynRestResourceUtils; +import org.apache.brooklyn.rest.util.NullHttpServletRequestProvider; +import org.apache.brooklyn.rest.util.NullServletConfigProvider; +import org.apache.brooklyn.rest.util.ShutdownHandlerProvider; +import org.apache.brooklyn.rest.util.TestShutdownHandler; +import org.apache.brooklyn.rest.util.json.BrooklynJacksonJsonProvider; +import brooklyn.test.entity.LocalManagementContextForTests; +import brooklyn.util.exceptions.Exceptions; + +public abstract class BrooklynRestApiTest { + + protected ManagementContext manager; + + protected boolean useLocalScannedCatalog = false; + protected TestShutdownHandler shutdownListener = createShutdownHandler(); + + @BeforeMethod(alwaysRun = true) + public void setUpMethod() { + shutdownListener.reset(); + } + + protected synchronized void useLocalScannedCatalog() { + if (manager!=null && !useLocalScannedCatalog) + throw new IllegalStateException("useLocalScannedCatalog must be specified before manager is accessed/created"); + useLocalScannedCatalog = true; + } + + private TestShutdownHandler createShutdownHandler() { + return new TestShutdownHandler(); + } + + protected synchronized ManagementContext getManagementContext() { + if (manager==null) { + if (useLocalScannedCatalog) { + manager = new LocalManagementContext(); + BrooklynRestApiLauncherTest.forceUseOfDefaultCatalogWithJavaClassPath(manager); + } else { + manager = new LocalManagementContextForTests(); + } + manager.getHighAvailabilityManager().disabled(); + BasicLocationRegistry.setupLocationRegistryForTesting(manager); + + new BrooklynCampPlatformLauncherNoServer() + .useManagementContext(manager) + .launch(); + } + return manager; + } + + protected ObjectMapper mapper() { + return BrooklynJacksonJsonProvider.findSharedObjectMapper(null, getManagementContext()); + } + + @AfterClass + public void tearDown() throws Exception { + destroyManagementContext(); + } + + protected void destroyManagementContext() { + if (manager!=null) { + Entities.destroyAll(manager); + manager = null; + } + } + + public LocationRegistry getLocationRegistry() { + return new BrooklynRestResourceUtils(getManagementContext()).getLocationRegistry(); + } + + private JerseyTest jerseyTest; + protected DefaultResourceConfig config = new DefaultResourceConfig(); + + protected final void addResource(Object resource) { + Preconditions.checkNotNull(config, "Must run before setUpJersey"); + + if (resource instanceof Class) + config.getClasses().add((Class<?>)resource); + else + config.getSingletons().add(resource); + + if (resource instanceof ManagementContextInjectable) { + ((ManagementContextInjectable)resource).injectManagementContext(getManagementContext()); + } + } + + protected final void addProvider(Class<?> provider) { + Preconditions.checkNotNull(config, "Must run before setUpJersey"); + + config.getClasses().add(provider); + } + + protected void addDefaultResources() { + // seems we have to provide our own injector because the jersey test framework + // doesn't inject ServletConfig and it all blows up + // and the servlet config provider must be an instance; addClasses doesn't work for some reason + addResource(new NullServletConfigProvider()); + addProvider(NullHttpServletRequestProvider.class); + addResource(new ShutdownHandlerProvider(shutdownListener)); + } + + protected final void setUpResources() { + addDefaultResources(); + addBrooklynResources(); + for (Object r: BrooklynRestApi.getMiscResources()) + addResource(r); + } + + /** intended for overriding if you only want certain resources added, or additional ones added */ + protected void addBrooklynResources() { + for (Object r: BrooklynRestApi.getBrooklynRestResources()) + addResource(r); + } + + protected void setUpJersey() { + setUpResources(); + + jerseyTest = new JerseyTest() { + @Override + protected AppDescriptor configure() { + return new LowLevelAppDescriptor.Builder(config).build(); + } + }; + config = null; + try { + jerseyTest.setUp(); + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + protected void tearDownJersey() { + if (jerseyTest != null) { + try { + jerseyTest.tearDown(); + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + config = new DefaultResourceConfig(); + } + + public Client client() { + Preconditions.checkNotNull(jerseyTest, "Must run setUpJersey first"); + return jerseyTest.client(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java new file mode 100644 index 0000000..9db714d --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/BrooklynRestResourceTest.java @@ -0,0 +1,155 @@ +/* + * 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.testing; + +import static org.testng.Assert.assertTrue; + +import java.net.URI; +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; + +import javax.ws.rs.core.MediaType; + +import org.codehaus.jackson.map.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; + +import brooklyn.entity.Application; +import brooklyn.entity.basic.Entities; +import brooklyn.rest.domain.ApplicationSpec; +import brooklyn.rest.domain.ApplicationSummary; +import brooklyn.rest.domain.Status; +import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.repeat.Repeater; +import brooklyn.util.time.Duration; + +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.UniformInterfaceException; +import com.sun.jersey.spi.inject.Errors; + +public abstract class BrooklynRestResourceTest extends BrooklynRestApiTest { + + private static final Logger log = LoggerFactory.getLogger(BrooklynRestResourceTest.class); + + @BeforeClass(alwaysRun = true) + public void setUp() throws Exception { + // need this to debug jersey inject errors + java.util.logging.Logger.getLogger(Errors.class.getName()).setLevel(Level.INFO); + + setUpJersey(); + } + + @Override + @AfterClass(alwaysRun = true) + public void tearDown() throws Exception { + tearDownJersey(); + super.tearDown(); + } + + + protected ClientResponse clientDeploy(ApplicationSpec spec) { + try { + // dropwizard TestClient won't skip deserialization of trivial things like string and byte[] and inputstream + // if we pass in an object it serializes, so we have to serialize things ourselves + return client().resource("/v1/applications") + .entity(new ObjectMapper().writer().writeValueAsBytes(spec), MediaType.APPLICATION_OCTET_STREAM) + .post(ClientResponse.class); + } catch (Exception e) { + throw Exceptions.propagate(e); + } + } + + protected void waitForApplicationToBeRunning(final URI applicationRef) { + waitForApplicationToBeRunning(applicationRef, Duration.minutes(3)); + } + protected void waitForApplicationToBeRunning(final URI applicationRef, Duration timeout) { + if (applicationRef==null) + throw new NullPointerException("No application URI available (consider using BrooklynRestResourceTest.clientDeploy)"); + + boolean started = Repeater.create("Wait for application startup") + .until(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + Status status = getApplicationStatus(applicationRef); + if (status == Status.ERROR) { + Assert.fail("Application failed with ERROR"); + } + return status == Status.RUNNING; + } + }) + .backoffTo(Duration.ONE_SECOND) + .limitTimeTo(timeout) + .run(); + + if (!started) { + log.warn("Did not start application "+applicationRef+":"); + Collection<Application> apps = getManagementContext().getApplications(); + for (Application app: apps) + Entities.dumpInfo(app); + } + assertTrue(started); + } + + protected Status getApplicationStatus(URI uri) { + return client().resource(uri).get(ApplicationSummary.class).getStatus(); + } + + protected void waitForPageFoundResponse(final String resource, final Class<?> clazz) { + boolean found = Repeater.create("Wait for page found") + .until(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + try { + client().resource(resource).get(clazz); + return true; + } catch (UniformInterfaceException e) { + return false; + } + } + }) + .every(1, TimeUnit.SECONDS) + .limitTimeTo(30, TimeUnit.SECONDS) + .run(); + assertTrue(found); + } + + protected void waitForPageNotFoundResponse(final String resource, final Class<?> clazz) { + boolean success = Repeater.create("Wait for page not found") + .until(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + try { + client().resource(resource).get(clazz); + return false; + } catch (UniformInterfaceException e) { + return e.getResponse().getStatus() == 404; + } + } + }) + .every(1, TimeUnit.SECONDS) + .limitTimeTo(30, TimeUnit.SECONDS) + .run(); + assertTrue(success); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java new file mode 100644 index 0000000..c174891 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/CapitalizePolicy.java @@ -0,0 +1,32 @@ +/* + * 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.testing.mocks; + +import brooklyn.entity.basic.EntityLocal; +import brooklyn.policy.basic.AbstractPolicy; + +public class CapitalizePolicy extends AbstractPolicy { + + @Override + public void setEntity(EntityLocal entity) { + super.setEntity(entity); + // TODO subscribe to foo and emit an enriched sensor on different channel which is capitalized + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java new file mode 100644 index 0000000..3020f9e --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroup.java @@ -0,0 +1,27 @@ +/* + * 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.testing.mocks; + +import brooklyn.entity.Group; +import brooklyn.entity.proxying.ImplementedBy; + +@ImplementedBy(EverythingGroupImpl.class) +public interface EverythingGroup extends Group { + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java new file mode 100644 index 0000000..61c9f3b --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/EverythingGroupImpl.java @@ -0,0 +1,32 @@ +/* + * 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.testing.mocks; + +import brooklyn.entity.basic.DynamicGroupImpl; + +import com.google.common.base.Predicates; + +public class EverythingGroupImpl extends DynamicGroupImpl implements EverythingGroup { + + public EverythingGroupImpl() { + super(); + config().set(ENTITY_FILTER, Predicates.alwaysTrue()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java new file mode 100644 index 0000000..06552a6 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroup.java @@ -0,0 +1,30 @@ +/* + * 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.testing.mocks; + +import brooklyn.config.ConfigKey; +import brooklyn.entity.Group; +import brooklyn.entity.basic.ConfigKeys; +import brooklyn.entity.proxying.ImplementedBy; + +@ImplementedBy(NameMatcherGroupImpl.class) +public interface NameMatcherGroup extends Group { + + public static final ConfigKey<String> NAME_REGEX = ConfigKeys.newStringConfigKey("namematchergroup.regex"); +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java new file mode 100644 index 0000000..f0a307c --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/NameMatcherGroupImpl.java @@ -0,0 +1,33 @@ +/* + * 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.testing.mocks; + +import brooklyn.entity.basic.DynamicGroupImpl; +import brooklyn.entity.basic.EntityPredicates; +import brooklyn.util.text.StringPredicates; + +public class NameMatcherGroupImpl extends DynamicGroupImpl implements NameMatcherGroup { + + @Override + public void init() { + super.init(); + config().set(ENTITY_FILTER, EntityPredicates.displayNameSatisfies(StringPredicates.matchesRegex(getConfig(NAME_REGEX)))); + rescanEntities(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java new file mode 100644 index 0000000..33447fd --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockApp.java @@ -0,0 +1,24 @@ +/* + * 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.testing.mocks; + +import brooklyn.entity.basic.AbstractApplication; + +public class RestMockApp extends AbstractApplication { +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java new file mode 100644 index 0000000..b9d7d87 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockAppBuilder.java @@ -0,0 +1,39 @@ +/* + * 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.testing.mocks; + +import brooklyn.entity.Entity; +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.StartableApplication; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.util.javalang.Reflections; + +public class RestMockAppBuilder extends ApplicationBuilder { + + public RestMockAppBuilder() { + super(EntitySpec.create(StartableApplication.class).impl(RestMockApp.class)); + } + + @Override + protected void doBuild() { + addChild(EntitySpec.create(Entity.class).impl(RestMockSimpleEntity.class) + .additionalInterfaces(Reflections.getAllInterfaces(RestMockSimpleEntity.class)) + .displayName("child1")); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java new file mode 100644 index 0000000..7fd13da --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimpleEntity.java @@ -0,0 +1,104 @@ +/* + * 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.testing.mocks; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.config.ConfigKey; +import brooklyn.entity.Entity; +import brooklyn.entity.annotation.Effector; +import brooklyn.entity.annotation.EffectorParam; +import brooklyn.entity.basic.AbstractSoftwareProcessSshDriver; +import brooklyn.entity.basic.EntityLocal; +import brooklyn.entity.basic.MethodEffector; +import brooklyn.entity.basic.SoftwareProcessImpl; +import brooklyn.event.AttributeSensor; +import brooklyn.event.basic.BasicAttributeSensor; +import brooklyn.event.basic.BasicConfigKey; +import brooklyn.location.basic.SshMachineLocation; +import brooklyn.util.flags.SetFromFlag; + +public class RestMockSimpleEntity extends SoftwareProcessImpl { + + private static final Logger log = LoggerFactory.getLogger(RestMockSimpleEntity.class); + + public RestMockSimpleEntity() { + super(); + } + + public RestMockSimpleEntity(Entity parent) { + super(parent); + } + + public RestMockSimpleEntity(@SuppressWarnings("rawtypes") Map flags, Entity parent) { + super(flags, parent); + } + + public RestMockSimpleEntity(@SuppressWarnings("rawtypes") Map flags) { + super(flags); + } + + @Override + protected void connectSensors() { + super.connectSensors(); + connectServiceUpIsRunning(); + } + + @SetFromFlag("sampleConfig") + public static final ConfigKey<String> SAMPLE_CONFIG = new BasicConfigKey<String>( + String.class, "brooklyn.rest.mock.sample.config", "Mock sample config", "DEFAULT_VALUE"); + + public static final AttributeSensor<String> SAMPLE_SENSOR = new BasicAttributeSensor<String>( + String.class, "brooklyn.rest.mock.sample.sensor", "Mock sample sensor"); + + public static final MethodEffector<String> SAMPLE_EFFECTOR = new MethodEffector<String>(RestMockSimpleEntity.class, "sampleEffector"); + + @Effector + public String sampleEffector(@EffectorParam(name="param1", description="param one") String param1, + @EffectorParam(name="param2") Integer param2) { + log.info("Invoked sampleEffector("+param1+","+param2+")"); + String result = ""+param1+param2; + setAttribute(SAMPLE_SENSOR, result); + return result; + } + + @SuppressWarnings("rawtypes") + @Override + public Class getDriverInterface() { + return MockSshDriver.class; + } + + public static class MockSshDriver extends AbstractSoftwareProcessSshDriver { + public MockSshDriver(EntityLocal entity, SshMachineLocation machine) { + super(entity, machine); + } + public boolean isRunning() { return true; } + public void stop() {} + public void kill() {} + public void install() {} + public void customize() {} + public void launch() {} + public void setup() { } + public void copyInstallResources() { } + public void copyRuntimeResources() { } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java new file mode 100644 index 0000000..92d488f --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/testing/mocks/RestMockSimplePolicy.java @@ -0,0 +1,65 @@ +/* + * 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.testing.mocks; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.config.ConfigKey; +import brooklyn.event.basic.BasicConfigKey; +import brooklyn.policy.basic.AbstractPolicy; +import brooklyn.util.flags.SetFromFlag; + +public class RestMockSimplePolicy extends AbstractPolicy { + + @SuppressWarnings("unused") + private static final Logger log = LoggerFactory.getLogger(RestMockSimplePolicy.class); + + public RestMockSimplePolicy() { + super(); + } + + @SuppressWarnings("rawtypes") + public RestMockSimplePolicy(Map flags) { + super(flags); + } + + @SetFromFlag("sampleConfig") + public static final ConfigKey<String> SAMPLE_CONFIG = BasicConfigKey.builder(String.class) + .name("brooklyn.rest.mock.sample.config") + .description("Mock sample config") + .defaultValue("DEFAULT_VALUE") + .reconfigurable(true) + .build(); + + @SetFromFlag + public static final ConfigKey<Integer> INTEGER_CONFIG = BasicConfigKey.builder(Integer.class) + .name("brooklyn.rest.mock.sample.integer") + .description("Mock integer config") + .defaultValue(1) + .reconfigurable(true) + .build(); + + @Override + protected <T> void doReconfigureConfig(ConfigKey<T> key, T val) { + // no-op + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java new file mode 100644 index 0000000..07aefc9 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/BrooklynRestResourceUtilsTest.java @@ -0,0 +1,215 @@ +/* + * 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 org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.util.Map; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import org.apache.brooklyn.catalog.Catalog; +import brooklyn.catalog.internal.CatalogItemBuilder; +import brooklyn.catalog.internal.CatalogTemplateItemDto; +import brooklyn.catalog.internal.CatalogUtils; +import brooklyn.entity.Application; +import brooklyn.entity.Entity; +import brooklyn.entity.basic.AbstractApplication; +import brooklyn.entity.basic.BasicEntity; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.EntityLocal; +import brooklyn.entity.proxying.EntityProxy; +import brooklyn.management.internal.LocalManagementContext; +import brooklyn.policy.Policy; +import brooklyn.policy.basic.AbstractPolicy; +import brooklyn.rest.domain.ApplicationSpec; +import brooklyn.rest.domain.EntitySpec; +import brooklyn.test.entity.LocalManagementContextForTests; +import brooklyn.test.entity.TestEntityImpl; +import brooklyn.util.collections.MutableMap; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +public class BrooklynRestResourceUtilsTest { + + private LocalManagementContext managementContext; + private BrooklynRestResourceUtils util; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + managementContext = LocalManagementContextForTests.newInstance(); + util = new BrooklynRestResourceUtils(managementContext); + } + + @AfterMethod(alwaysRun=true) + public void tearDown() throws Exception { + if (managementContext != null) managementContext.terminate(); + } + + @Test + public void testCreateAppFromImplClass() { + ApplicationSpec spec = ApplicationSpec.builder() + .name("myname") + .type(SampleNoOpApplication.class.getName()) + .locations(ImmutableSet.of("localhost")) + .build(); + Application app = util.create(spec); + + assertEquals(ImmutableList.copyOf(managementContext.getApplications()), ImmutableList.of(app)); + assertEquals(app.getDisplayName(), "myname"); + assertTrue(app instanceof EntityProxy); + assertTrue(app instanceof MyInterface); + assertFalse(app instanceof SampleNoOpApplication); + } + + @Test + public void testCreateAppFromCatalogByType() { + createAppFromCatalog(SampleNoOpApplication.class.getName()); + } + + @Test + public void testCreateAppFromCatalogByName() { + createAppFromCatalog("app.noop"); + } + + @Test + public void testCreateAppFromCatalogById() { + createAppFromCatalog("app.noop:0.0.1"); + } + + @Test + @SuppressWarnings("deprecation") + public void testCreateAppFromCatalogByTypeMultipleItems() { + CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.noop", "0.0.2-SNAPSHOT") + .javaType(SampleNoOpApplication.class.getName()) + .build(); + managementContext.getCatalog().addItem(item); + createAppFromCatalog(SampleNoOpApplication.class.getName()); + } + + @SuppressWarnings("deprecation") + private void createAppFromCatalog(String type) { + CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.noop", "0.0.1") + .javaType(SampleNoOpApplication.class.getName()) + .build(); + managementContext.getCatalog().addItem(item); + + ApplicationSpec spec = ApplicationSpec.builder() + .name("myname") + .type(type) + .locations(ImmutableSet.of("localhost")) + .build(); + Application app = util.create(spec); + + assertEquals(app.getCatalogItemId(), "app.noop:0.0.1"); + } + + @Test + public void testEntityAppFromCatalogByType() { + createEntityFromCatalog(BasicEntity.class.getName()); + } + + @Test + public void testEntityAppFromCatalogByName() { + createEntityFromCatalog("app.basic"); + } + + @Test + public void testEntityAppFromCatalogById() { + createEntityFromCatalog("app.basic:0.0.1"); + } + + @Test + @SuppressWarnings("deprecation") + public void testEntityAppFromCatalogByTypeMultipleItems() { + CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate("app.basic", "0.0.2-SNAPSHOT") + .javaType(SampleNoOpApplication.class.getName()) + .build(); + managementContext.getCatalog().addItem(item); + createEntityFromCatalog(BasicEntity.class.getName()); + } + + @SuppressWarnings("deprecation") + private void createEntityFromCatalog(String type) { + String symbolicName = "app.basic"; + String version = "0.0.1"; + CatalogTemplateItemDto item = CatalogItemBuilder.newTemplate(symbolicName, version) + .javaType(BasicEntity.class.getName()) + .build(); + managementContext.getCatalog().addItem(item); + + ApplicationSpec spec = ApplicationSpec.builder() + .name("myname") + .entities(ImmutableSet.of(new EntitySpec(type))) + .locations(ImmutableSet.of("localhost")) + .build(); + Application app = util.create(spec); + + Entity entity = Iterables.getOnlyElement(app.getChildren()); + assertEquals(entity.getCatalogItemId(), CatalogUtils.getVersionedId(symbolicName, version)); + } + + @Test + public void testNestedApplications() { + // hierarchy is: app -> subapp -> subentity (where subentity has a policy) + + SampleNoOpApplication app = new SampleNoOpApplication(); + app.setDisplayName("app"); + + SampleNoOpApplication subapp = new SampleNoOpApplication(); + subapp.setDisplayName("subapp"); + + TestEntityImpl subentity = new TestEntityImpl(MutableMap.of("displayName", "subentity"), subapp); + subentity.addPolicy(new MyPolicy(MutableMap.of("name", "mypolicy"))); + subentity.getApplication(); // force this to be cached + + app.addChild(subapp); + Entities.startManagement(app, managementContext); + + EntityLocal subappRetrieved = util.getEntity(app.getId(), subapp.getId()); + assertEquals(subappRetrieved.getDisplayName(), "subapp"); + + EntityLocal subentityRetrieved = util.getEntity(app.getId(), subentity.getId()); + assertEquals(subentityRetrieved.getDisplayName(), "subentity"); + + Policy subappPolicy = util.getPolicy(app.getId(), subentity.getId(), "mypolicy"); + assertEquals(subappPolicy.getDisplayName(), "mypolicy"); + } + + public interface MyInterface { + } + + @Catalog(name="Sample No-Op Application", + description="Application which does nothing, included only as part of the test cases.", + iconUrl="") + public static class SampleNoOpApplication extends AbstractApplication implements MyInterface { + } + + public static class MyPolicy extends AbstractPolicy { + public MyPolicy(Map<String, ?> flags) { + super(flags); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/EntityLocationUtilsTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/EntityLocationUtilsTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/EntityLocationUtilsTest.java new file mode 100644 index 0000000..9840d84 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/EntityLocationUtilsTest.java @@ -0,0 +1,73 @@ +/* + * 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 org.testng.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.BrooklynAppUnitTestSupport; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.basic.SoftwareProcess; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.location.Location; +import brooklyn.location.basic.AbstractLocation; +import brooklyn.location.basic.LocationInternal; +import brooklyn.location.geo.HostGeoInfo; +import org.apache.brooklyn.rest.testing.mocks.RestMockSimpleEntity; + +import com.google.common.collect.ImmutableList; + +public class EntityLocationUtilsTest extends BrooklynAppUnitTestSupport { + + private static final Logger log = LoggerFactory.getLogger(EntityLocationUtilsTest.class); + + private Location loc; + + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + super.setUp(); + loc = mgmt.getLocationRegistry().resolve("localhost"); + ((AbstractLocation)loc).setHostGeoInfo(new HostGeoInfo("localhost", "localhost", 50, 0)); + } + + @Test + public void testCount() { + @SuppressWarnings("unused") + SoftwareProcess r1 = app.createAndManageChild(EntitySpec.create(SoftwareProcess.class, RestMockSimpleEntity.class)); + SoftwareProcess r2 = app.createAndManageChild(EntitySpec.create(SoftwareProcess.class, RestMockSimpleEntity.class)); + Entities.start(app, Arrays.<Location>asList(loc)); + + Entities.dumpInfo(app); + + log.info("r2loc: "+r2.getLocations()); + log.info("props: "+((LocationInternal)r2.getLocations().iterator().next()).config().getBag().getAllConfig()); + + Map<Location, Integer> counts = new EntityLocationUtils(mgmt).countLeafEntitiesByLocatedLocations(); + log.info("count: "+counts); + assertEquals(ImmutableList.copyOf(counts.values()), ImmutableList.of(2), "counts="+counts); + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckClassResource.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckClassResource.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckClassResource.java new file mode 100644 index 0000000..80e9c46 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckClassResource.java @@ -0,0 +1,38 @@ +/* + * 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.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.apache.brooklyn.rest.filter.HaHotStateRequired; + +@Path("/ha/class") +@Produces(MediaType.APPLICATION_JSON) +@HaHotStateRequired +public class HaHotStateCheckClassResource { + + @GET + @Path("fail") + public String fail() { + return "FAIL"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckResource.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckResource.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckResource.java new file mode 100644 index 0000000..5c9d4d1 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/HaHotStateCheckResource.java @@ -0,0 +1,44 @@ +/* + * 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.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.apache.brooklyn.rest.filter.HaHotStateRequired; + +@Path("/ha/method") +@Produces(MediaType.APPLICATION_JSON) +public class HaHotStateCheckResource { + + @GET + @Path("ok") + public String ok() { + return "OK"; + } + + @GET + @Path("fail") + @HaHotStateRequired + public String fail() { + return "FAIL"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullHttpServletRequestProvider.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullHttpServletRequestProvider.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullHttpServletRequestProvider.java new file mode 100644 index 0000000..7a24f31 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullHttpServletRequestProvider.java @@ -0,0 +1,46 @@ +/* + * 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.lang.reflect.Type; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.Provider; + +import com.sun.jersey.core.spi.component.ComponentContext; +import com.sun.jersey.core.spi.component.ComponentScope; +import com.sun.jersey.spi.inject.Injectable; +import com.sun.jersey.spi.inject.InjectableProvider; + +@Provider +public class NullHttpServletRequestProvider implements InjectableProvider<Context, Type> { + public Injectable<HttpServletRequest> getInjectable(ComponentContext ic, + Context a, Type c) { + if (HttpServletRequest.class == c) { + return new Injectable<HttpServletRequest>() { + public HttpServletRequest getValue() { return null; } + }; + } else + return null; + } + public ComponentScope getScope() { + return ComponentScope.Singleton; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java new file mode 100644 index 0000000..06c60ea --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/NullServletConfigProvider.java @@ -0,0 +1,46 @@ +/* + * 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.lang.reflect.Type; + +import javax.servlet.ServletContext; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.Provider; + +import com.sun.jersey.core.spi.component.ComponentContext; +import com.sun.jersey.core.spi.component.ComponentScope; +import com.sun.jersey.spi.inject.Injectable; +import com.sun.jersey.spi.inject.InjectableProvider; + +@Provider +public class NullServletConfigProvider implements InjectableProvider<Context, Type> { + public Injectable<ServletContext> getInjectable(ComponentContext ic, + Context a, Type c) { + if (ServletContext.class == c) { + return new Injectable<ServletContext>() { + public ServletContext getValue() { return null; } + }; + } else + return null; + } + public ComponentScope getScope() { + return ComponentScope.Singleton; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java new file mode 100644 index 0000000..87fbb24 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/TestShutdownHandler.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.brooklyn.rest.util.ShutdownHandler; + +public class TestShutdownHandler implements ShutdownHandler { + private volatile boolean isRequested; + + @Override + public void onShutdownRequest() { + isRequested = true; + } + + public boolean isRequested() { + return isRequested; + } + + public void reset() { + isRequested = false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/a9e5ca55/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java ---------------------------------------------------------------------- diff --git a/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java new file mode 100644 index 0000000..8e16198 --- /dev/null +++ b/usage/rest-server/src/test/java/org/apache/brooklyn/rest/util/json/BrooklynJacksonSerializerTest.java @@ -0,0 +1,399 @@ +/* + * 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.NotSerializableException; +import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.ws.rs.core.MediaType; + +import org.apache.http.client.HttpClient; +import org.apache.http.client.utils.URIBuilder; +import org.codehaus.jackson.annotate.JsonProperty; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.eclipse.jetty.server.Server; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.Test; + +import brooklyn.entity.Entity; +import brooklyn.entity.basic.Attributes; +import brooklyn.entity.basic.BrooklynTaskTags; +import brooklyn.entity.basic.Entities; +import brooklyn.management.ManagementContext; +import org.apache.brooklyn.rest.BrooklynRestApiLauncher; +import brooklyn.test.entity.LocalManagementContextForTests; +import brooklyn.test.entity.TestApplication; +import brooklyn.test.entity.TestEntity; +import brooklyn.util.collections.MutableList; +import brooklyn.util.collections.MutableMap; +import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.http.HttpTool; +import brooklyn.util.stream.Streams; +import brooklyn.util.text.Strings; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import com.google.gson.Gson; + +public class BrooklynJacksonSerializerTest { + + private static final Logger log = LoggerFactory.getLogger(BrooklynJacksonSerializerTest.class); + + public static class SillyClassWithManagementContext { + @JsonProperty + ManagementContext mgmt; + @JsonProperty + String id; + + public SillyClassWithManagementContext() { } + + public SillyClassWithManagementContext(String id, ManagementContext mgmt) { + this.id = id; + this.mgmt = mgmt; + } + + @Override + public String toString() { + return super.toString()+"[id="+id+";mgmt="+mgmt+"]"; + } + } + + @Test + public void testCustomSerializerWithSerializableSillyManagementExample() throws Exception { + ManagementContext mgmt = LocalManagementContextForTests.newInstance(); + try { + + ObjectMapper mapper = BrooklynJacksonJsonProvider.newPrivateObjectMapper(mgmt); + + SillyClassWithManagementContext silly = new SillyClassWithManagementContext("123", mgmt); + log.info("silly is: "+silly); + + String sillyS = mapper.writeValueAsString(silly); + + log.info("silly json is: "+sillyS); + + SillyClassWithManagementContext silly2 = mapper.readValue(sillyS, SillyClassWithManagementContext.class); + log.info("silly2 is: "+silly2); + + Assert.assertEquals(silly.id, silly2.id); + + } finally { + Entities.destroyAll(mgmt); + } + } + + public static class SelfRefNonSerializableClass { + @JsonProperty + Object bogus = this; + } + + @Test + public void testSelfReferenceFailsWhenStrict() { + checkNonSerializableWhenStrict(new SelfRefNonSerializableClass()); + } + @Test + public void testSelfReferenceGeneratesErrorMapObject() throws Exception { + checkSerializesAsMapWithErrorAndToString(new SelfRefNonSerializableClass()); + } + @Test + public void testNonSerializableInListIsShownInList() throws Exception { + List<?> result = checkSerializesAs(MutableList.of(1, new SelfRefNonSerializableClass()), List.class); + Assert.assertEquals( result.get(0), 1 ); + Assert.assertEquals( ((Map<?,?>)result.get(1)).get("errorType"), NotSerializableException.class.getName() ); + } + @Test + public void testNonSerializableInMapIsShownInMap() throws Exception { + Map<?,?> result = checkSerializesAs(MutableMap.of("x", new SelfRefNonSerializableClass()), Map.class); + Assert.assertEquals( ((Map<?,?>)result.get("x")).get("errorType"), NotSerializableException.class.getName() ); + } + static class TupleWithNonSerializable { + String good = "bon"; + SelfRefNonSerializableClass bad = new SelfRefNonSerializableClass(); + } + @Test + public void testNonSerializableInObjectIsShownInMap() throws Exception { + String resultS = checkSerializesAs(new TupleWithNonSerializable(), null); + log.info("nested non-serializable json is "+resultS); + Assert.assertTrue(resultS.startsWith("{\"good\":\"bon\",\"bad\":{"), "expected a nested map for the error field, not "+resultS); + + Map<?,?> result = checkSerializesAs(new TupleWithNonSerializable(), Map.class); + Assert.assertEquals( result.get("good"), "bon" ); + Assert.assertTrue( result.containsKey("bad"), "Should have had a key for field 'bad'" ); + Assert.assertEquals( ((Map<?,?>)result.get("bad")).get("errorType"), NotSerializableException.class.getName() ); + } + + public static class EmptyClass { + } + + @Test + public void testEmptySerializesAsEmpty() throws Exception { + // deliberately, a class with no fields and no annotations serializes as an error, + // because the alternative, {}, is useless. however if it *is* annotated, as below, then it will serialize fine. + checkSerializesAsMapWithErrorAndToString(new SelfRefNonSerializableClass()); + } + @Test + public void testEmptyNonSerializableFailsWhenStrict() { + checkNonSerializableWhenStrict(new EmptyClass()); + } + + @JsonSerialize + public static class EmptyClassWithSerialize { + } + + @Test + public void testEmptyAnnotatedSerializesAsEmptyEvenWhenStrict() throws Exception { + try { + BidiSerialization.setStrictSerialization(true); + testEmptyAnnotatedSerializesAsEmpty(); + } finally { + BidiSerialization.clearStrictSerialization(); + } + } + + @Test + public void testEmptyAnnotatedSerializesAsEmpty() throws Exception { + Map<?, ?> map = checkSerializesAs( new EmptyClassWithSerialize(), Map.class ); + Assert.assertTrue(map.isEmpty(), "Expected an empty map; instead got: "+map); + + String result = checkSerializesAs( MutableList.of(new EmptyClassWithSerialize()), null ); + result = result.replaceAll(" ", "").trim(); + Assert.assertEquals(result, "[{}]"); + } + + @Test + public void testSensorFailsWhenStrict() { + checkNonSerializableWhenStrict(MutableList.of(Attributes.HTTP_PORT)); + } + @Test + public void testSensorSensible() throws Exception { + Map<?,?> result = checkSerializesAs(Attributes.HTTP_PORT, Map.class); + log.info("SENSOR json is: "+result); + Assert.assertFalse(result.toString().contains("error"), "Shouldn't have had an error, instead got: "+result); + } + + @Test + public void testLinkedListSerialization() throws Exception { + LinkedList<Object> ll = new LinkedList<Object>(); + ll.add(1); ll.add("two"); + String result = checkSerializesAs(ll, null); + log.info("LLIST json is: "+result); + Assert.assertFalse(result.contains("error"), "Shouldn't have had an error, instead got: "+result); + Assert.assertEquals(Strings.collapseWhitespace(result, ""), "[1,\"two\"]"); + } + + @Test + public void testMultiMapSerialization() throws Exception { + Multimap<String, Integer> m = MultimapBuilder.hashKeys().arrayListValues().build(); + m.put("bob", 24); + m.put("bob", 25); + String result = checkSerializesAs(m, null); + log.info("multimap serialized as: " + result); + Assert.assertFalse(result.contains("error"), "Shouldn't have had an error, instead got: "+result); + Assert.assertEquals(Strings.collapseWhitespace(result, ""), "{\"bob\":[24,25]}"); + } + + @Test + public void testSupplierSerialization() throws Exception { + String result = checkSerializesAs(Strings.toStringSupplier(Streams.byteArrayOfString("x")), null); + log.info("SUPPLIER json is: "+result); + Assert.assertFalse(result.contains("error"), "Shouldn't have had an error, instead got: "+result); + } + + @Test + public void testWrappedStreamSerialization() throws Exception { + String result = checkSerializesAs(BrooklynTaskTags.tagForStream("TEST", Streams.byteArrayOfString("x")), null); + log.info("WRAPPED STREAM json is: "+result); + Assert.assertFalse(result.contains("error"), "Shouldn't have had an error, instead got: "+result); + } + + @SuppressWarnings("unchecked") + protected <T> T checkSerializesAs(Object x, Class<T> type) { + ManagementContext mgmt = LocalManagementContextForTests.newInstance(); + try { + ObjectMapper mapper = BrooklynJacksonJsonProvider.newPrivateObjectMapper(mgmt); + String tS = mapper.writeValueAsString(x); + log.debug("serialized "+x+" as "+tS); + Assert.assertTrue(tS.length() < 1000, "Data too long, size "+tS.length()+" for "+x); + if (type==null) return (T) tS; + return mapper.readValue(tS, type); + } catch (Exception e) { + throw Exceptions.propagate(e); + } finally { + Entities.destroyAll(mgmt); + } + } + protected Map<?,?> checkSerializesAsMapWithErrorAndToString(Object x) { + Map<?,?> rt = checkSerializesAs(x, Map.class); + Assert.assertEquals(rt.get("toString"), x.toString()); + Assert.assertEquals(rt.get("error"), Boolean.TRUE); + return rt; + } + protected void checkNonSerializableWhenStrict(Object x) { + checkNonSerializable(x, true); + } + protected void checkNonSerializable(Object x, boolean strict) { + ManagementContext mgmt = LocalManagementContextForTests.newInstance(); + try { + ObjectMapper mapper = BrooklynJacksonJsonProvider.newPrivateObjectMapper(mgmt); + if (strict) + BidiSerialization.setStrictSerialization(true); + + String tS = mapper.writeValueAsString(x); + Assert.fail("Should not have serialized "+x+"; instead gave: "+tS); + + } catch (Exception e) { + Exceptions.propagateIfFatal(e); + log.info("Got expected error, when serializing "+x+": "+e); + + } finally { + if (strict) + BidiSerialization.clearStrictSerialization(); + Entities.destroyAll(mgmt); + } + } + + // Ensure TEXT_PLAIN just returns toString for ManagementContext instance. + // Strangely, testWithLauncherSerializingListsContainingEntitiesAndOtherComplexStuff ended up in the + // EntityConfigResource.getPlain code, throwing a ClassCastException. + // + // TODO This tests the fix for that ClassCastException, but does not explain why + // testWithLauncherSerializingListsContainingEntitiesAndOtherComplexStuff was calling it. + @Test(groups="Integration") //because of time + public void testWithAcceptsPlainText() throws Exception { + ManagementContext mgmt = LocalManagementContextForTests.newInstance(); + Server server = null; + try { + server = BrooklynRestApiLauncher.launcher().managementContext(mgmt).start(); + HttpClient client = HttpTool.httpClientBuilder().build(); + + TestApplication app = TestApplication.Factory.newManagedInstanceForTests(mgmt); + + String serverAddress = "http://localhost:"+server.getConnectors()[0].getLocalPort(); + String appUrl = serverAddress + "/v1/applications/" + app.getId(); + String entityUrl = appUrl + "/entities/" + app.getId(); + URI configUri = new URIBuilder(entityUrl + "/config/" + TestEntity.CONF_OBJECT.getName()) + .addParameter("raw", "true") + .build(); + + // assert config here is just mgmt.toString() + app.config().set(TestEntity.CONF_OBJECT, mgmt); + String content = get(client, configUri, ImmutableMap.of("Accept", MediaType.TEXT_PLAIN)); + log.info("CONFIG MGMT is:\n"+content); + Assert.assertEquals(content, mgmt.toString(), "content="+content); + + } finally { + try { + if (server != null) server.stop(); + } catch (Exception e) { + log.warn("failed to stop server: "+e); + } + Entities.destroyAll(mgmt); + } + } + + @Test(groups="Integration") //because of time + public void testWithLauncherSerializingListsContainingEntitiesAndOtherComplexStuff() throws Exception { + ManagementContext mgmt = LocalManagementContextForTests.newInstance(); + Server server = null; + try { + server = BrooklynRestApiLauncher.launcher().managementContext(mgmt).start(); + HttpClient client = HttpTool.httpClientBuilder().build(); + + TestApplication app = TestApplication.Factory.newManagedInstanceForTests(mgmt); + + String serverAddress = "http://localhost:"+server.getConnectors()[0].getLocalPort(); + String appUrl = serverAddress + "/v1/applications/" + app.getId(); + String entityUrl = appUrl + "/entities/" + app.getId(); + URI configUri = new URIBuilder(entityUrl + "/config/" + TestEntity.CONF_OBJECT.getName()) + .addParameter("raw", "true") + .build(); + + // assert config here is just mgmt + app.config().set(TestEntity.CONF_OBJECT, mgmt); + String content = get(client, configUri, ImmutableMap.of("Accept", MediaType.APPLICATION_JSON)); + log.info("CONFIG MGMT is:\n"+content); + @SuppressWarnings("rawtypes") + Map values = new Gson().fromJson(content, Map.class); + Assert.assertEquals(values, ImmutableMap.of("type", LocalManagementContextForTests.class.getCanonicalName()), "values="+values); + + // assert normal API returns the same, containing links + content = get(client, entityUrl, ImmutableMap.of("Accept", MediaType.APPLICATION_JSON)); + log.info("ENTITY is: \n"+content); + values = new Gson().fromJson(content, Map.class); + Assert.assertTrue(values.size()>=3, "Map is too small: "+values); + Assert.assertTrue(values.size()<=6, "Map is too big: "+values); + Assert.assertEquals(values.get("type"), TestApplication.class.getCanonicalName(), "values="+values); + Assert.assertNotNull(values.get("links"), "Map should have contained links: values="+values); + + // but config etc returns our nicely json serialized + app.config().set(TestEntity.CONF_OBJECT, app); + content = get(client, configUri, ImmutableMap.of("Accept", MediaType.APPLICATION_JSON)); + log.info("CONFIG ENTITY is:\n"+content); + values = new Gson().fromJson(content, Map.class); + Assert.assertEquals(values, ImmutableMap.of("type", Entity.class.getCanonicalName(), "id", app.getId()), "values="+values); + + // and self-ref gives error + toString + SelfRefNonSerializableClass angry = new SelfRefNonSerializableClass(); + app.config().set(TestEntity.CONF_OBJECT, angry); + content = get(client, configUri, ImmutableMap.of("Accept", MediaType.APPLICATION_JSON)); + log.info("CONFIG ANGRY is:\n"+content); + assertErrorObjectMatchingToString(content, angry); + + // as does Server + app.config().set(TestEntity.CONF_OBJECT, server); + content = get(client, configUri, ImmutableMap.of("Accept", MediaType.APPLICATION_JSON)); + // NOTE, if using the default visibility / object mapper, the getters of the object are invoked + // resulting in an object which is huge, 7+MB -- and it wreaks havoc w eclipse console regex parsing! + // (but with our custom VisibilityChecker server just gives us the nicer error!) + log.info("CONFIG SERVER is:\n"+content); + assertErrorObjectMatchingToString(content, server); + Assert.assertTrue(content.contains(NotSerializableException.class.getCanonicalName()), "server should have contained things which are not serializable"); + Assert.assertTrue(content.length() < 1024, "content should not have been very long; instead was: "+content.length()); + + } finally { + try { + if (server != null) server.stop(); + } catch (Exception e) { + log.warn("failed to stop server: "+e); + } + Entities.destroyAll(mgmt); + } + } + + private void assertErrorObjectMatchingToString(String content, Object expected) { + Object value = new Gson().fromJson(content, Object.class); + Assert.assertTrue(value instanceof Map, "Expected map, got: "+value); + Assert.assertEquals(((Map<?,?>)value).get("toString"), expected.toString()); + } + + private String get(HttpClient client, String uri, Map<String, String> headers) { + return get(client, URI.create(uri), headers); + } + + private String get(HttpClient client, URI uri, Map<String, String> headers) { + return HttpTool.httpGet(client, uri, headers).getContentAsString(); + } +}
