mirror of https://github.com/apache/jclouds.git
Issue 1111:Move tweetstore to new jclouds-tweetstore repository
This commit is contained in:
parent
4e2834b6cd
commit
e43df10f39
|
@ -37,7 +37,6 @@
|
||||||
<module>speedtest-azurequeue</module>
|
<module>speedtest-azurequeue</module>
|
||||||
<module>speedtest-sqs</module>
|
<module>speedtest-sqs</module>
|
||||||
<module>simpledb</module>
|
<module>simpledb</module>
|
||||||
<module>tweetstore</module>
|
|
||||||
</modules>
|
</modules>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
====
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
====
|
|
||||||
|
|
||||||
A guide to generating Twitter consumer keys and access tokens is at http://tinyurl.com/2fhebgb
|
|
||||||
|
|
||||||
Please modify your maven settings.xml like below before attempting to run 'mvn -Plive install'
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<id>keys</id>
|
|
||||||
<activation>
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<test.aws-s3.identity>YOUR_ACCESS_KEY_ID</test.aws-s3.identity>
|
|
||||||
<test.aws-s3.credential>YOUR_SECRET_KEY</test.aws-s3.credential>
|
|
||||||
<test.cloudfiles-us.identity>YOUR_USER</test.cloudfiles-us.identity>
|
|
||||||
<test.cloudfiles-us.credential>YOUR_HEX_KEY</test.cloudfiles-us.credential>
|
|
||||||
<test.azureblob.identity>YOUR_ACCOUNT</test.azureblob.identity>
|
|
||||||
<test.azureblob.credential>YOUR_BASE64_ENCODED_KEY</test.azureblob.credential>
|
|
||||||
<test.twitter.cf-tweetstore-spring.consumer.identity>YOUR_TWITTER_CONSUMER_KEY</test.twitter.cf-tweetstore-spring.consumer.identity>
|
|
||||||
<test.twitter.cf-tweetstore-spring.consumer.credential>YOUR_TWITTER_CONSUMER_SECRET</test.twitter.cf-tweetstore-spring.consumer.credential>
|
|
||||||
<test.twitter.cf-tweetstore-spring.access.identity>YOUR_TWITTER_ACCESSTOKEN</test.twitter.cf-tweetstore-spring.access.identity>
|
|
||||||
<test.twitter.cf-tweetstore-spring.access.credential>YOUR_TWITTER_ACCESSTOKEN_SECRET</test.twitter.cf-tweetstore-spring.access.credential>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
|
@ -1,191 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.jclouds</groupId>
|
|
||||||
<artifactId>jclouds-demos-tweetstore-project</artifactId>
|
|
||||||
<version>1.6.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>jclouds-demo-cf-tweetstore-spring</artifactId>
|
|
||||||
<packaging>war</packaging>
|
|
||||||
<name>jclouds TweetStore for Cloud Foundry</name>
|
|
||||||
<description>jclouds TweetStore for Cloud Foundry using Spring for Dependency Injection</description>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<cloudfoundry.version>0.8.1</cloudfoundry.version>
|
|
||||||
<cloudfoundry.applicationid>jclouds-tweetstore</cloudfoundry.applicationid>
|
|
||||||
<test.cloudfoundry.target>http://api.cloudfoundry.com</test.cloudfoundry.target>
|
|
||||||
<test.cloudfoundry.address>test-${cloudfoundry.applicationid}.cloudfoundry.com</test.cloudfoundry.address>
|
|
||||||
<test.cloudfoundry.port>80</test.cloudfoundry.port>
|
|
||||||
<jclouds.tweetstore.container>jclouds-cf-tweetstore-spring</jclouds.tweetstore.container>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<!-- ensure this matches the version of Spring imported by cloudfoundry-runtime -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-context</artifactId>
|
|
||||||
<version>3.0.5.RELEASE</version>
|
|
||||||
<!-- using the SLF4J/commons-logging bridge -->
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>commons-logging</groupId>
|
|
||||||
<artifactId>commons-logging</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-webmvc</artifactId>
|
|
||||||
<version>3.0.5.RELEASE</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>cglib</groupId>
|
|
||||||
<artifactId>cglib-nodep</artifactId>
|
|
||||||
<version>2.2</version>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.quartz-scheduler</groupId>
|
|
||||||
<artifactId>quartz</artifactId>
|
|
||||||
<version>2.1.3</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.slf4j</groupId>
|
|
||||||
<artifactId>slf4j-api</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Cloud Foundry API -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cloudfoundry</groupId>
|
|
||||||
<artifactId>cloudfoundry-runtime</artifactId>
|
|
||||||
<version>${cloudfoundry.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.cloudfoundry</groupId>
|
|
||||||
<artifactId>cloudfoundry-client-lib</artifactId>
|
|
||||||
<version>0.7.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.codehaus.plexus</groupId>
|
|
||||||
<artifactId>plexus-archiver</artifactId>
|
|
||||||
<version>2.1.1</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>org.springframework.maven.milestone</id>
|
|
||||||
<url>http://maven.springframework.org/milestone</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>live</id>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>integration</id>
|
|
||||||
<phase>integration-test</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>test</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<systemPropertyVariables>
|
|
||||||
<test.twitter.consumer.identity>${test.twitter.gae-tweetstore-spring.consumer.identity}</test.twitter.consumer.identity>
|
|
||||||
<test.twitter.consumer.credential>${test.twitter.gae-tweetstore-spring.consumer.credential}</test.twitter.consumer.credential>
|
|
||||||
<test.twitter.access.identity>${test.twitter.gae-tweetstore-spring.access.identity}</test.twitter.access.identity>
|
|
||||||
<test.twitter.access.credential>${test.twitter.gae-tweetstore-spring.access.credential}</test.twitter.access.credential>
|
|
||||||
<test.azureblob.identity>${test.azureblob.identity}</test.azureblob.identity>
|
|
||||||
<test.azureblob.credential>${test.azureblob.credential}</test.azureblob.credential>
|
|
||||||
<test.cloudfiles-us.identity>${test.cloudfiles-us.identity}</test.cloudfiles-us.identity>
|
|
||||||
<test.cloudfiles-us.credential>${test.cloudfiles-us.credential}</test.cloudfiles-us.credential>
|
|
||||||
<test.aws-s3.identity>${test.aws-s3.identity}</test.aws-s3.identity>
|
|
||||||
<test.aws-s3.credential>${test.aws-s3.credential}</test.aws-s3.credential>
|
|
||||||
<test.cloudonestorage.identity>${test.cloudonestorage.identity}</test.cloudonestorage.identity>
|
|
||||||
<test.cloudonestorage.credential>${test.cloudonestorage.credential}</test.cloudonestorage.credential>
|
|
||||||
<test.ninefold-storage.identity>${test.ninefold-storage.identity}</test.ninefold-storage.identity>
|
|
||||||
<test.ninefold-storage.credential>${test.ninefold-storage.credential}</test.ninefold-storage.credential>
|
|
||||||
<cloudfoundry.address>${test.cloudfoundry.address}</cloudfoundry.address>
|
|
||||||
<cloudfoundry.port>${test.cloudfoundry.port}</cloudfoundry.port>
|
|
||||||
<cloudfoundry.target>${test.cloudfoundry.target}</cloudfoundry.target>
|
|
||||||
<cloudfoundry.username>${cloudfoundry.username}</cloudfoundry.username>
|
|
||||||
<cloudfoundry.password>${cloudfoundry.password}</cloudfoundry.password>
|
|
||||||
<jclouds.tweetstore.blobstores>${jclouds.tweetstore.blobstores}</jclouds.tweetstore.blobstores>
|
|
||||||
<jclouds.tweetstore.container>test.${jclouds.tweetstore.container}</jclouds.tweetstore.container>
|
|
||||||
<warfile>${project.build.directory}/${project.build.finalName}</warfile>
|
|
||||||
</systemPropertyVariables>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<id>deploy</id>
|
|
||||||
<properties>
|
|
||||||
<!-- classifier to choose the correct jclouds.properties file -->
|
|
||||||
<tweetstore.instance>cf-tweetstore-spring</tweetstore.instance>
|
|
||||||
</properties>
|
|
||||||
<pluginRepositories>
|
|
||||||
<pluginRepository>
|
|
||||||
<id>org.springframework.maven.milestone</id>
|
|
||||||
<url>http://maven.springframework.org/milestone</url>
|
|
||||||
<snapshots>
|
|
||||||
<enabled>false</enabled>
|
|
||||||
</snapshots>
|
|
||||||
</pluginRepository>
|
|
||||||
</pluginRepositories>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.cloudfoundry</groupId>
|
|
||||||
<artifactId>cf-maven-plugin</artifactId>
|
|
||||||
<version>1.0.0.M1</version>
|
|
||||||
<configuration>
|
|
||||||
<target>http://api.cloudfoundry.com</target>
|
|
||||||
<username>${cloudfoundry.username}</username>
|
|
||||||
<password>${cloudfoundry.password}</password>
|
|
||||||
<appname>${cloudfoundry.applicationid}</appname>
|
|
||||||
<memory>256</memory>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</project>
|
|
|
@ -1,64 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static org.jclouds.demo.paas.config.PlatformServicesInitializer.PLATFORM_SERVICES_ATTRIBUTE_NAME;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
|
|
||||||
import org.jclouds.demo.paas.service.scheduler.Scheduler;
|
|
||||||
import org.jclouds.demo.paas.service.taskqueue.TaskQueue;
|
|
||||||
import org.jclouds.javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public class PlatformServices {
|
|
||||||
protected final String baseUrl;
|
|
||||||
protected final Scheduler scheduler;
|
|
||||||
private ImmutableMap<String, TaskQueue> taskQueues;
|
|
||||||
|
|
||||||
public PlatformServices(String baseUrl, Scheduler scheduler, Map<String, TaskQueue> taskQueues) {
|
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
this.scheduler = scheduler;
|
|
||||||
this.taskQueues = ImmutableMap.copyOf(taskQueues);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBaseUrl() {
|
|
||||||
return baseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Scheduler getScheduler() {
|
|
||||||
return scheduler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable TaskQueue getTaskQueue(String name) {
|
|
||||||
return taskQueues.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PlatformServices get(ServletContext context) {
|
|
||||||
return (PlatformServices) checkNotNull(context.getAttribute(
|
|
||||||
PLATFORM_SERVICES_ATTRIBUTE_NAME), PLATFORM_SERVICES_ATTRIBUTE_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,126 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
|
||||||
|
|
||||||
import org.jclouds.http.HttpCommand;
|
|
||||||
import org.jclouds.http.HttpCommandExecutorService;
|
|
||||||
import org.jclouds.http.HttpRequest;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMultimap;
|
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
|
|
||||||
public class RunnableHttpRequest implements Runnable {
|
|
||||||
public static final String PLATFORM_REQUEST_ORIGINATOR_HEADER = "X-Platform-Originator";
|
|
||||||
|
|
||||||
public static Factory factory(HttpCommandExecutorService httpClient) {
|
|
||||||
return factory(httpClient, format("%s@%d", Factory.class.getName(), System.currentTimeMillis()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Factory factory(HttpCommandExecutorService httpClient, String originator) {
|
|
||||||
return new Factory(httpClient, originator);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory {
|
|
||||||
protected final HttpCommandExecutorService httpClient;
|
|
||||||
protected final String originator;
|
|
||||||
|
|
||||||
private Factory(HttpCommandExecutorService httpClient, String originator) {
|
|
||||||
this.httpClient = httpClient;
|
|
||||||
this.originator = originator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RunnableHttpRequest create(HttpRequest request) {
|
|
||||||
HttpRequest requestWithSubmitter = request.toBuilder()
|
|
||||||
.headers(copyOfWithEntry(request.getHeaders(),
|
|
||||||
PLATFORM_REQUEST_ORIGINATOR_HEADER, originator)).build();
|
|
||||||
return new RunnableHttpRequest(httpClient, requestWithSubmitter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <K, V> Multimap<K, V> copyOfWithEntry(
|
|
||||||
Multimap<? extends K, ? extends V> multimap, K k1, V v1) {
|
|
||||||
return ImmutableMultimap.<K, V>builder().putAll(multimap).put(k1, v1).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final HttpCommandExecutorService httpClient;
|
|
||||||
private final HttpRequest request;
|
|
||||||
|
|
||||||
private RunnableHttpRequest(HttpCommandExecutorService httpClient, HttpRequest request) {
|
|
||||||
this.httpClient = httpClient;
|
|
||||||
this.request = request;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
httpClient.submit(new ImmutableHttpCommand(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ImmutableHttpCommand implements HttpCommand {
|
|
||||||
private final HttpRequest request;
|
|
||||||
|
|
||||||
public ImmutableHttpCommand(HttpRequest request) {
|
|
||||||
this.request = request;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setException(Exception exception) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCurrentRequest(HttpRequest request) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReplayable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int incrementRedirectCount() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int incrementFailureCount() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRedirectCount() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getFailureCount() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Exception getException() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpRequest getCurrentRequest() {
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas.config;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.inject.name.Names.bindProperties;
|
|
||||||
import static org.jclouds.Constants.*;
|
|
||||||
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
import javax.ws.rs.core.UriBuilder;
|
|
||||||
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.PropertiesLoader;
|
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
|
||||||
import com.sun.jersey.api.uri.UriBuilderImpl;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public class HttpClientModule extends AbstractModule {
|
|
||||||
private final ServletContext context;
|
|
||||||
|
|
||||||
HttpClientModule(ServletContext context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configure() {
|
|
||||||
// URL connection defaults
|
|
||||||
Properties toBind = defaultProperties();
|
|
||||||
toBind.putAll(checkNotNull(new PropertiesLoader(context).get(), "properties"));
|
|
||||||
toBind.putAll(System.getProperties());
|
|
||||||
bindProperties(binder(), toBind);
|
|
||||||
bind(UriBuilder.class).to(UriBuilderImpl.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Properties defaultProperties() {
|
|
||||||
Properties props = new Properties();
|
|
||||||
props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 20 + "");
|
|
||||||
props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 0 + "");
|
|
||||||
props.setProperty(PROPERTY_SO_TIMEOUT, 60000 + "");
|
|
||||||
props.setProperty(PROPERTY_CONNECTION_TIMEOUT, 60000 + "");
|
|
||||||
props.setProperty(PROPERTY_USER_THREADS, 0 + "");
|
|
||||||
props.setProperty(PROPERTY_IO_WORKER_THREADS, 20 + "");
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas.config;
|
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
import javax.servlet.ServletContextEvent;
|
|
||||||
import javax.servlet.ServletContextListener;
|
|
||||||
|
|
||||||
import org.cloudfoundry.runtime.env.ApplicationInstanceInfo;
|
|
||||||
import org.cloudfoundry.runtime.env.CloudEnvironment;
|
|
||||||
import org.jclouds.concurrent.config.ExecutorServiceModule;
|
|
||||||
import org.jclouds.demo.paas.PlatformServices;
|
|
||||||
import org.jclouds.demo.paas.service.scheduler.Scheduler;
|
|
||||||
import org.jclouds.demo.paas.service.taskqueue.TaskQueue;
|
|
||||||
import org.jclouds.http.HttpCommandExecutorService;
|
|
||||||
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.ImmutableMap.Builder;
|
|
||||||
import com.google.inject.Guice;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public class PlatformServicesInitializer implements ServletContextListener {
|
|
||||||
public static final String PLATFORM_SERVICES_ATTRIBUTE_NAME = PlatformServices.class.getName();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextInitialized(ServletContextEvent contextEvent) {
|
|
||||||
ServletContext context = contextEvent.getServletContext();
|
|
||||||
context.setAttribute(PLATFORM_SERVICES_ATTRIBUTE_NAME, createServices(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static PlatformServices createServices(ServletContext context) {
|
|
||||||
HttpCommandExecutorService httpClient = createHttpClient(context);
|
|
||||||
return new PlatformServices(getBaseUrl(context), new Scheduler(httpClient),
|
|
||||||
createTaskQueues(httpClient));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static HttpCommandExecutorService createHttpClient(
|
|
||||||
final ServletContext context) {
|
|
||||||
return Guice.createInjector(new ExecutorServiceModule(),
|
|
||||||
new JavaUrlHttpCommandExecutorServiceModule(),
|
|
||||||
new HttpClientModule(context))
|
|
||||||
.getInstance(HttpCommandExecutorService.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static String getBaseUrl(ServletContext context) {
|
|
||||||
ApplicationInstanceInfo instanceInfo = new CloudEnvironment().getInstanceInfo();
|
|
||||||
// using localhost instead of instanceInfo.getHost(). See http://support.cloudfoundry.com/requests/102117
|
|
||||||
return "http://127.0.0.1:" + instanceInfo.getPort() + context.getContextPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: make the number and names of queues configurable
|
|
||||||
protected static ImmutableMap<String, TaskQueue> createTaskQueues(HttpCommandExecutorService httpClient) {
|
|
||||||
Builder<String, TaskQueue> taskQueues = ImmutableMap.builder();
|
|
||||||
taskQueues.put("twitter", TaskQueue.builder(httpClient)
|
|
||||||
.name("twitter").period(SECONDS.toMillis(30))
|
|
||||||
.build());
|
|
||||||
return taskQueues.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextDestroyed(ServletContextEvent servletContextEvent) {
|
|
||||||
ServletContext context = servletContextEvent.getServletContext();
|
|
||||||
context.removeAttribute(PLATFORM_SERVICES_ATTRIBUTE_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas.reference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration properties and constants used in PaaS applications.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public interface PaasConstants {
|
|
||||||
static final String PROPERTY_PLATFORM_BASE_URL = "jclouds.paas.baseurl";
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas.service.scheduler;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
|
|
||||||
import org.jclouds.demo.paas.PlatformServices;
|
|
||||||
import org.jclouds.demo.paas.RunnableHttpRequest;
|
|
||||||
import org.jclouds.http.HttpRequest;
|
|
||||||
import org.quartz.Job;
|
|
||||||
import org.quartz.JobExecutionContext;
|
|
||||||
import org.quartz.JobExecutionException;
|
|
||||||
import org.quartz.SchedulerException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public class HttpRequestJob implements Job {
|
|
||||||
protected static final String URL_ATTRIBUTE_NAME = "url";
|
|
||||||
|
|
||||||
// keep in sync with "quartz:scheduler-context-servlet-context-key" param in web.xml
|
|
||||||
protected static final String SERVLET_CONTEXT_KEY = "servlet-context";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(JobExecutionContext context) throws JobExecutionException {
|
|
||||||
PlatformServices platform = JobContexts.getPlatform(context);
|
|
||||||
RunnableHttpRequest request = platform.getScheduler().getHttpRequestFactory().create(
|
|
||||||
HttpRequest.builder()
|
|
||||||
.endpoint(JobContexts.getTargetUrl(platform.getBaseUrl(), context))
|
|
||||||
.method("GET").build());
|
|
||||||
request.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class JobContexts {
|
|
||||||
private static URI getTargetUrl(String baseUrl, JobExecutionContext context) {
|
|
||||||
return URI.create(baseUrl + (String) checkNotNull(
|
|
||||||
context.getMergedJobDataMap().get(URL_ATTRIBUTE_NAME), URL_ATTRIBUTE_NAME));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PlatformServices getPlatform(JobExecutionContext jobContext) throws JobExecutionException {
|
|
||||||
try {
|
|
||||||
return PlatformServices.get((ServletContext) checkNotNull(
|
|
||||||
jobContext.getScheduler().getContext().get(SERVLET_CONTEXT_KEY), SERVLET_CONTEXT_KEY));
|
|
||||||
} catch (SchedulerException exception) {
|
|
||||||
throw new JobExecutionException("Unable to get platform services from the job execution context", exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas.service.scheduler;
|
|
||||||
|
|
||||||
import org.jclouds.demo.paas.RunnableHttpRequest;
|
|
||||||
import org.jclouds.demo.paas.RunnableHttpRequest.Factory;
|
|
||||||
import org.jclouds.http.HttpCommandExecutorService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public class Scheduler {
|
|
||||||
protected static final String SCHEDULER_ORIGINATOR_NAME = "scheduler";
|
|
||||||
|
|
||||||
protected final Factory httpRequestFactory;
|
|
||||||
|
|
||||||
public Scheduler(HttpCommandExecutorService httpClient) {
|
|
||||||
httpRequestFactory =
|
|
||||||
RunnableHttpRequest.factory(httpClient, SCHEDULER_ORIGINATOR_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Factory getHttpRequestFactory() {
|
|
||||||
return httpRequestFactory;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,401 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas.service.scheduler.quartz.plugins;
|
|
||||||
|
|
||||||
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
|
|
||||||
import static org.quartz.TriggerBuilder.newTrigger;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.quartz.JobBuilder;
|
|
||||||
import org.quartz.JobDetail;
|
|
||||||
import org.quartz.Scheduler;
|
|
||||||
import org.quartz.SchedulerException;
|
|
||||||
import org.quartz.SimpleTrigger;
|
|
||||||
import org.quartz.TriggerKey;
|
|
||||||
import org.quartz.jobs.FileScanJob;
|
|
||||||
import org.quartz.jobs.FileScanListener;
|
|
||||||
import org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin;
|
|
||||||
import org.quartz.simpl.CascadingClassLoadHelper;
|
|
||||||
import org.quartz.spi.ClassLoadHelper;
|
|
||||||
import org.quartz.spi.SchedulerPlugin;
|
|
||||||
import org.quartz.xml.XMLSchedulingDataProcessor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A copy of {@link XMLSchedulingDataProcessorPlugin} that does not reference
|
|
||||||
* {@code javax.transaction.UserTransaction} as so does not require a dependency
|
|
||||||
* on JTA.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
* @see XMLSchedulingDataProcessorPlugin
|
|
||||||
*/
|
|
||||||
public class TransactionlessXmlSchedulingDataProcessorPlugin implements
|
|
||||||
FileScanListener, SchedulerPlugin {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Data members.
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*/
|
|
||||||
private static final int MAX_JOB_TRIGGER_NAME_LEN = 80;
|
|
||||||
private static final String JOB_INITIALIZATION_PLUGIN_NAME = "JobSchedulingDataLoaderPlugin";
|
|
||||||
private static final String FILE_NAME_DELIMITERS = ",";
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private Scheduler scheduler;
|
|
||||||
private final Logger log = Logger.CONSOLE;
|
|
||||||
|
|
||||||
private boolean failOnFileNotFound = true;
|
|
||||||
|
|
||||||
private String fileNames = XMLSchedulingDataProcessor.QUARTZ_XML_DEFAULT_FILE_NAME;
|
|
||||||
|
|
||||||
// Populated by initialization
|
|
||||||
private Map<String, JobFile> jobFiles = new LinkedHashMap<String, JobFile>();
|
|
||||||
|
|
||||||
private long scanInterval = 0;
|
|
||||||
|
|
||||||
boolean started = false;
|
|
||||||
|
|
||||||
protected ClassLoadHelper classLoadHelper = null;
|
|
||||||
|
|
||||||
private Set<String> jobTriggerNameSet = new HashSet<String>();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Interface.
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Comma separated list of file names (with paths) to the XML files that should be read.
|
|
||||||
*/
|
|
||||||
public String getFileNames() {
|
|
||||||
return fileNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The file name (and path) to the XML file that should be read.
|
|
||||||
*/
|
|
||||||
public void setFileNames(String fileNames) {
|
|
||||||
this.fileNames = fileNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The interval (in seconds) at which to scan for changes to the file.
|
|
||||||
* If the file has been changed, it is re-loaded and parsed. The default
|
|
||||||
* value for the interval is 0, which disables scanning.
|
|
||||||
*
|
|
||||||
* @return Returns the scanInterval.
|
|
||||||
*/
|
|
||||||
public long getScanInterval() {
|
|
||||||
return scanInterval / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The interval (in seconds) at which to scan for changes to the file.
|
|
||||||
* If the file has been changed, it is re-loaded and parsed. The default
|
|
||||||
* value for the interval is 0, which disables scanning.
|
|
||||||
*
|
|
||||||
* @param scanInterval The scanInterval to set.
|
|
||||||
*/
|
|
||||||
public void setScanInterval(long scanInterval) {
|
|
||||||
this.scanInterval = scanInterval * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not initialization of the plugin should fail (throw an
|
|
||||||
* exception) if the file cannot be found. Default is <code>true</code>.
|
|
||||||
*/
|
|
||||||
public boolean isFailOnFileNotFound() {
|
|
||||||
return failOnFileNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether or not initialization of the plugin should fail (throw an
|
|
||||||
* exception) if the file cannot be found. Default is <code>true</code>.
|
|
||||||
*/
|
|
||||||
public void setFailOnFileNotFound(boolean failOnFileNotFound) {
|
|
||||||
this.failOnFileNotFound = failOnFileNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* SchedulerPlugin Interface.
|
|
||||||
*
|
|
||||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>
|
|
||||||
* Called during creation of the <code>Scheduler</code> in order to give
|
|
||||||
* the <code>SchedulerPlugin</code> a chance to initialize.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @throws org.quartz.SchedulerConfigException
|
|
||||||
* if there is an error initializing.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void initialize(String name, Scheduler scheduler)
|
|
||||||
throws SchedulerException {
|
|
||||||
this.name = name;
|
|
||||||
this.scheduler = scheduler;
|
|
||||||
|
|
||||||
classLoadHelper = new CascadingClassLoadHelper();
|
|
||||||
classLoadHelper.initialize();
|
|
||||||
|
|
||||||
log.info("Registering Quartz Job Initialization Plug-in.");
|
|
||||||
|
|
||||||
// Create JobFile objects
|
|
||||||
StringTokenizer stok = new StringTokenizer(fileNames, FILE_NAME_DELIMITERS);
|
|
||||||
while (stok.hasMoreTokens()) {
|
|
||||||
final String fileName = stok.nextToken();
|
|
||||||
final JobFile jobFile = new JobFile(fileName);
|
|
||||||
jobFiles.put(fileName, jobFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
try {
|
|
||||||
if (jobFiles.isEmpty() == false) {
|
|
||||||
|
|
||||||
if (scanInterval > 0) {
|
|
||||||
scheduler.getContext().put(JOB_INITIALIZATION_PLUGIN_NAME + '_' + name, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<JobFile> iterator = jobFiles.values().iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
JobFile jobFile = iterator.next();
|
|
||||||
|
|
||||||
if (scanInterval > 0) {
|
|
||||||
String jobTriggerName = buildJobTriggerName(jobFile.getFileBasename());
|
|
||||||
TriggerKey tKey = new TriggerKey(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME);
|
|
||||||
|
|
||||||
// remove pre-existing job/trigger, if any
|
|
||||||
scheduler.unscheduleJob(tKey);
|
|
||||||
|
|
||||||
// TODO: convert to use builder
|
|
||||||
SimpleTrigger trig = newTrigger()
|
|
||||||
.withIdentity(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME)
|
|
||||||
.startNow()
|
|
||||||
.endAt(null)
|
|
||||||
.withSchedule(simpleSchedule()
|
|
||||||
.repeatForever()
|
|
||||||
.withIntervalInMilliseconds(scanInterval))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
JobDetail job = JobBuilder.newJob(FileScanJob.class)
|
|
||||||
.withIdentity(jobTriggerName, JOB_INITIALIZATION_PLUGIN_NAME)
|
|
||||||
.build();
|
|
||||||
job.getJobDataMap().put(FileScanJob.FILE_NAME, jobFile.getFileName());
|
|
||||||
job.getJobDataMap().put(FileScanJob.FILE_SCAN_LISTENER_NAME, JOB_INITIALIZATION_PLUGIN_NAME + '_' + name);
|
|
||||||
|
|
||||||
scheduler.scheduleJob(job, trig);
|
|
||||||
log.debug("Scheduled file scan job for data file: {}, at interval: {}", jobFile.getFileName(), scanInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
processFile(jobFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(SchedulerException se) {
|
|
||||||
log.error("Error starting background-task for watching jobs file.", se);
|
|
||||||
} finally {
|
|
||||||
started = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method for generating unique job/trigger name for the
|
|
||||||
* file scanning jobs (one per FileJob). The unique names are saved
|
|
||||||
* in jobTriggerNameSet.
|
|
||||||
*/
|
|
||||||
private String buildJobTriggerName(
|
|
||||||
String fileBasename) {
|
|
||||||
// Name w/o collisions will be prefix + _ + filename (with '.' of filename replaced with '_')
|
|
||||||
// For example: JobInitializationPlugin_jobInitializer_myjobs_xml
|
|
||||||
String jobTriggerName = JOB_INITIALIZATION_PLUGIN_NAME + '_' + name + '_' + fileBasename.replace('.', '_');
|
|
||||||
|
|
||||||
// If name is too long (DB column is 80 chars), then truncate to max length
|
|
||||||
if (jobTriggerName.length() > MAX_JOB_TRIGGER_NAME_LEN) {
|
|
||||||
jobTriggerName = jobTriggerName.substring(0, MAX_JOB_TRIGGER_NAME_LEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure this name is unique in case the same file name under different
|
|
||||||
// directories is being checked, or had a naming collision due to length truncation.
|
|
||||||
// If there is a conflict, keep incrementing a _# suffix on the name (being sure
|
|
||||||
// not to get too long), until we find a unique name.
|
|
||||||
int currentIndex = 1;
|
|
||||||
while (jobTriggerNameSet.add(jobTriggerName) == false) {
|
|
||||||
// If not our first time through, then strip off old numeric suffix
|
|
||||||
if (currentIndex > 1) {
|
|
||||||
jobTriggerName = jobTriggerName.substring(0, jobTriggerName.lastIndexOf('_'));
|
|
||||||
}
|
|
||||||
|
|
||||||
String numericSuffix = "_" + currentIndex++;
|
|
||||||
|
|
||||||
// If the numeric suffix would make the name too long, then make room for it.
|
|
||||||
if (jobTriggerName.length() > (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length())) {
|
|
||||||
jobTriggerName = jobTriggerName.substring(0, (MAX_JOB_TRIGGER_NAME_LEN - numericSuffix.length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
jobTriggerName += numericSuffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
return jobTriggerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutdown() {
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processFile(JobFile jobFile) {
|
|
||||||
if (jobFile == null || !jobFile.getFileFound()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
XMLSchedulingDataProcessor processor =
|
|
||||||
new XMLSchedulingDataProcessor(this.classLoadHelper);
|
|
||||||
|
|
||||||
processor.addJobGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME);
|
|
||||||
processor.addTriggerGroupToNeverDelete(JOB_INITIALIZATION_PLUGIN_NAME);
|
|
||||||
|
|
||||||
processor.processFileAndScheduleJobs(
|
|
||||||
jobFile.getFileName(),
|
|
||||||
jobFile.getFileName(), // systemId
|
|
||||||
scheduler);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Error scheduling jobs: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processFile(String filePath) {
|
|
||||||
processFile((JobFile)jobFiles.get(filePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.quartz.jobs.FileScanListener#fileUpdated(java.lang.String)
|
|
||||||
*/
|
|
||||||
public void fileUpdated(String fileName) {
|
|
||||||
if (started) {
|
|
||||||
processFile(fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JobFile {
|
|
||||||
private String fileName;
|
|
||||||
|
|
||||||
// These are set by initialize()
|
|
||||||
private String filePath;
|
|
||||||
private String fileBasename;
|
|
||||||
private boolean fileFound;
|
|
||||||
|
|
||||||
protected JobFile(String fileName) throws SchedulerException {
|
|
||||||
this.fileName = fileName;
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getFileName() {
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean getFileFound() {
|
|
||||||
return fileFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getFilePath() {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getFileBasename() {
|
|
||||||
return fileBasename;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize() throws SchedulerException {
|
|
||||||
InputStream f = null;
|
|
||||||
try {
|
|
||||||
String furl = null;
|
|
||||||
|
|
||||||
File file = new File(getFileName()); // files in filesystem
|
|
||||||
if (!file.exists()) {
|
|
||||||
URL url = classLoadHelper.getResource(getFileName());
|
|
||||||
if(url != null) {
|
|
||||||
try {
|
|
||||||
furl = URLDecoder.decode(url.getPath(), "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
furl = url.getPath();
|
|
||||||
}
|
|
||||||
file = new File(furl);
|
|
||||||
try {
|
|
||||||
f = url.openStream();
|
|
||||||
} catch (IOException ignore) {
|
|
||||||
// Swallow the exception
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
f = new java.io.FileInputStream(file);
|
|
||||||
}catch (FileNotFoundException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f == null) {
|
|
||||||
if (isFailOnFileNotFound()) {
|
|
||||||
throw new SchedulerException(
|
|
||||||
"File named '" + getFileName() + "' does not exist.");
|
|
||||||
} else {
|
|
||||||
log.warn("File named '" + getFileName() + "' does not exist.");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fileFound = true;
|
|
||||||
}
|
|
||||||
filePath = (furl != null) ? furl : file.getAbsolutePath();
|
|
||||||
fileBasename = file.getName();
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
if (f != null) {
|
|
||||||
f.close();
|
|
||||||
}
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
log.warn("Error closing jobs file " + getFileName(), ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.paas.service.taskqueue;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static java.lang.String.format;
|
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.jclouds.demo.paas.RunnableHttpRequest;
|
|
||||||
import org.jclouds.demo.paas.RunnableHttpRequest.Factory;
|
|
||||||
import org.jclouds.http.HttpCommandExecutorService;
|
|
||||||
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
public class TaskQueue {
|
|
||||||
protected final Factory httpRequestFactory;
|
|
||||||
private final Timer timer;
|
|
||||||
private final ConcurrentLinkedQueue<Runnable> tasks = new ConcurrentLinkedQueue<Runnable>();
|
|
||||||
|
|
||||||
private TaskQueue(String name, long pollingIntervalMillis, Factory httpRequestFactory) {
|
|
||||||
this.httpRequestFactory = httpRequestFactory;
|
|
||||||
timer = new Timer(name);
|
|
||||||
timer.scheduleAtFixedRate(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Runnable task = tasks.poll();
|
|
||||||
if (task != null) {
|
|
||||||
task.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 0, pollingIntervalMillis);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(final Runnable task) {
|
|
||||||
tasks.add(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Factory getHttpRequestFactory() {
|
|
||||||
return httpRequestFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void destroy() {
|
|
||||||
timer.cancel();
|
|
||||||
tasks.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Builder builder(HttpCommandExecutorService httpClient) {
|
|
||||||
return new Builder(httpClient);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder implements Provider<TaskQueue> {
|
|
||||||
protected final HttpCommandExecutorService httpClient;
|
|
||||||
protected String name = "default";
|
|
||||||
protected long pollingIntervalMillis = TimeUnit.SECONDS.toMillis(1);
|
|
||||||
|
|
||||||
private Builder(HttpCommandExecutorService httpClient) {
|
|
||||||
this.httpClient = checkNotNull(httpClient, "httpClient");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder name(String name) {
|
|
||||||
this.name = checkNotNull(name, "name");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder period(TimeUnit period) {
|
|
||||||
this.pollingIntervalMillis = checkNotNull(period, "period").toMillis(1);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder period(long pollingIntervalMillis) {
|
|
||||||
checkArgument(pollingIntervalMillis > 0, "pollingIntervalMillis");
|
|
||||||
this.pollingIntervalMillis = pollingIntervalMillis;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskQueue build() {
|
|
||||||
return new TaskQueue(name, pollingIntervalMillis,
|
|
||||||
RunnableHttpRequest.factory(httpClient, format("taskqueue-%s", name)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TaskQueue get() {
|
|
||||||
return build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
package org.jclouds.demo.tweetstore.config;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
|
||||||
import org.springframework.beans.TypeConverter;
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
|
||||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
|
||||||
|
|
||||||
class DelegatingAutowireCapableBeanFactory implements AutowireCapableBeanFactory {
|
|
||||||
private final AutowireCapableBeanFactory delegate;
|
|
||||||
|
|
||||||
DelegatingAutowireCapableBeanFactory(AutowireCapableBeanFactory delegate) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T createBean(Class<T> beanClass) throws BeansException {
|
|
||||||
return delegate.createBean(beanClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void autowireBean(Object existingBean) throws BeansException {
|
|
||||||
delegate.autowireBean(existingBean);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object configureBean(Object existingBean, String beanName)
|
|
||||||
throws BeansException {
|
|
||||||
return delegate.configureBean(existingBean, beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getBean(String name) throws BeansException {
|
|
||||||
return delegate.getBean(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object resolveDependency(DependencyDescriptor descriptor,
|
|
||||||
String beanName) throws BeansException {
|
|
||||||
return delegate.resolveDependency(descriptor, beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T getBean(String name, Class<T> requiredType)
|
|
||||||
throws BeansException {
|
|
||||||
return delegate.getBean(name, requiredType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public Object createBean(Class beanClass, int autowireMode,
|
|
||||||
boolean dependencyCheck) throws BeansException {
|
|
||||||
return delegate.createBean(beanClass, autowireMode, dependencyCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T getBean(Class<T> requiredType) throws BeansException {
|
|
||||||
return delegate.getBean(requiredType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public Object autowire(Class beanClass, int autowireMode,
|
|
||||||
boolean dependencyCheck) throws BeansException {
|
|
||||||
return delegate.autowire(beanClass, autowireMode, dependencyCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getBean(String name, Object... args) throws BeansException {
|
|
||||||
return delegate.getBean(name, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void autowireBeanProperties(Object existingBean, int autowireMode,
|
|
||||||
boolean dependencyCheck) throws BeansException {
|
|
||||||
delegate.autowireBeanProperties(existingBean, autowireMode,
|
|
||||||
dependencyCheck);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean containsBean(String name) {
|
|
||||||
return delegate.containsBean(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSingleton(String name)
|
|
||||||
throws NoSuchBeanDefinitionException {
|
|
||||||
return delegate.isSingleton(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void applyBeanPropertyValues(Object existingBean, String beanName)
|
|
||||||
throws BeansException {
|
|
||||||
delegate.applyBeanPropertyValues(existingBean, beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPrototype(String name)
|
|
||||||
throws NoSuchBeanDefinitionException {
|
|
||||||
return delegate.isPrototype(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public boolean isTypeMatch(String name, Class targetType)
|
|
||||||
throws NoSuchBeanDefinitionException {
|
|
||||||
return delegate.isTypeMatch(name, targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object initializeBean(Object existingBean, String beanName)
|
|
||||||
throws BeansException {
|
|
||||||
return delegate.initializeBean(existingBean, beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
|
|
||||||
return delegate.getType(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object applyBeanPostProcessorsBeforeInitialization(
|
|
||||||
Object existingBean, String beanName) throws BeansException {
|
|
||||||
return delegate.applyBeanPostProcessorsBeforeInitialization(
|
|
||||||
existingBean, beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getAliases(String name) {
|
|
||||||
return delegate.getAliases(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object applyBeanPostProcessorsAfterInitialization(
|
|
||||||
Object existingBean, String beanName) throws BeansException {
|
|
||||||
return delegate.applyBeanPostProcessorsAfterInitialization(
|
|
||||||
existingBean, beanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object resolveDependency(DependencyDescriptor descriptor,
|
|
||||||
String beanName, Set<String> autowiredBeanNames,
|
|
||||||
TypeConverter typeConverter) throws BeansException {
|
|
||||||
return delegate.resolveDependency(descriptor, beanName,
|
|
||||||
autowiredBeanNames, typeConverter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static org.jclouds.logging.LoggingModules.firstOrJDKLoggingModule;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.jclouds.logging.Logger.LoggerFactory;
|
|
||||||
import org.springframework.beans.BeansException;
|
|
||||||
import org.springframework.beans.TypeConverter;
|
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
|
||||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
|
||||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
|
||||||
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring config that sets up {@link CommonAnnotationBeanPostProcessor} support
|
|
||||||
* for injecting loggers.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
abstract class LoggingConfig implements BeanFactoryAware {
|
|
||||||
protected static final LoggerFactory LOGGER_FACTORY = firstOrJDKLoggingModule().createLoggerFactory();
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LOGGER_FACTORY.getLogger(LoggingConfig.class.getName());
|
|
||||||
|
|
||||||
private AutowireCapableBeanFactory beanFactory;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void initLoggerSupport() {
|
|
||||||
CommonAnnotationBeanPostProcessor resourceProcessor =
|
|
||||||
(CommonAnnotationBeanPostProcessor) beanFactory.getBean(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME);
|
|
||||||
resourceProcessor.setResourceFactory(new LoggerResourceBeanFactory(beanFactory));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class LoggerResourceBeanFactory extends DelegatingAutowireCapableBeanFactory {
|
|
||||||
|
|
||||||
LoggerResourceBeanFactory(AutowireCapableBeanFactory delegate) {
|
|
||||||
super(delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object resolveDependency(DependencyDescriptor descriptor,
|
|
||||||
String beanName, Set<String> autowiredBeanNames,
|
|
||||||
TypeConverter typeConverter) throws BeansException {
|
|
||||||
if (descriptor.getDependencyType().equals(Logger.class)) {
|
|
||||||
Class<?> requestingType = getType(beanName);
|
|
||||||
LOGGER.trace("About to resolve logger for bean '%s' of type '%s'",
|
|
||||||
beanName, requestingType);
|
|
||||||
Logger logger = resolveLogger(requestingType, autowiredBeanNames);
|
|
||||||
LOGGER.trace("Successfully resolved logger.");
|
|
||||||
return logger;
|
|
||||||
}
|
|
||||||
return super.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Logger resolveLogger(Class<?> type, Set<String> autowiredBeanNames) {
|
|
||||||
String loggerBeanName = format("%s#logger", type);
|
|
||||||
if (autowiredBeanNames.contains(loggerBeanName)) {
|
|
||||||
LOGGER.trace("Returning existing bean '%s'", loggerBeanName);
|
|
||||||
return (Logger) getBean(loggerBeanName);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.trace("About to create logger for type '%s'", type);
|
|
||||||
Logger logger = LOGGER_FACTORY.getLogger(type.getName());
|
|
||||||
LOGGER.trace("Successfully created logger.");
|
|
||||||
return logger;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
|
||||||
checkArgument(beanFactory instanceof AutowireCapableBeanFactory, "expected an instance of '%s' but was '%s'",
|
|
||||||
AutowireCapableBeanFactory.class, beanFactory.getClass());
|
|
||||||
this.beanFactory = (AutowireCapableBeanFactory) beanFactory;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,242 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.base.Predicates.in;
|
|
||||||
import static com.google.common.collect.ImmutableSet.copyOf;
|
|
||||||
import static com.google.common.collect.Sets.filter;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_BLOBSTORES;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN_SECRET;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_KEY;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_SECRET;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.annotation.PreDestroy;
|
|
||||||
import javax.servlet.Servlet;
|
|
||||||
import javax.servlet.ServletConfig;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.paas.PlatformServices;
|
|
||||||
import org.jclouds.demo.paas.service.taskqueue.TaskQueue;
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.AddTweetsController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.ClearTweetsController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.EnqueueStoresController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.StoreTweetsController;
|
|
||||||
import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.context.ServletConfigAware;
|
|
||||||
import org.springframework.web.servlet.HandlerAdapter;
|
|
||||||
import org.springframework.web.servlet.HandlerMapping;
|
|
||||||
import org.springframework.web.servlet.handler.SimpleServletHandlerAdapter;
|
|
||||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
|
||||||
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
import twitter4j.TwitterFactory;
|
|
||||||
import twitter4j.conf.ConfigurationBuilder;
|
|
||||||
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.io.Closeables;
|
|
||||||
import com.google.inject.Module;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates servlets (using resources from the {@link SpringAppConfig}) and mappings.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
* @see SpringAppConfig
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class SpringServletConfig extends LoggingConfig implements ServletConfigAware {
|
|
||||||
public static final String PROPERTY_BLOBSTORE_CONTEXTS = "blobstore.contexts";
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LOGGER_FACTORY.getLogger(SpringServletConfig.class.getName());
|
|
||||||
|
|
||||||
private ServletConfig servletConfig;
|
|
||||||
|
|
||||||
private Map<String, BlobStoreContext> providerTypeToBlobStoreMap;
|
|
||||||
private Twitter twitterClient;
|
|
||||||
private String container;
|
|
||||||
private TaskQueue queue;
|
|
||||||
private String baseUrl;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void initialize() throws IOException {
|
|
||||||
Properties props = loadJCloudsProperties();
|
|
||||||
LOGGER.trace("About to initialize members.");
|
|
||||||
|
|
||||||
Set<Module> modules = ImmutableSet.of();
|
|
||||||
// shared across all blobstores and used to retrieve tweets
|
|
||||||
try {
|
|
||||||
twitter4j.conf.Configuration twitterConf = new ConfigurationBuilder()
|
|
||||||
.setOAuthConsumerKey(props.getProperty(PROPERTY_TWITTER_CONSUMER_KEY))
|
|
||||||
.setOAuthConsumerSecret(props.getProperty(PROPERTY_TWITTER_CONSUMER_SECRET))
|
|
||||||
.setOAuthAccessToken(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN))
|
|
||||||
.setOAuthAccessTokenSecret(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET))
|
|
||||||
.build();
|
|
||||||
twitterClient = new TwitterFactory(twitterConf).getInstance();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new IllegalArgumentException("properties for twitter not configured properly in " + props.toString(), e);
|
|
||||||
}
|
|
||||||
// common namespace for storing tweets
|
|
||||||
container = checkNotNull(props.getProperty(PROPERTY_TWEETSTORE_CONTAINER), PROPERTY_TWEETSTORE_CONTAINER);
|
|
||||||
|
|
||||||
// instantiate and store references to all blobstores by provider name
|
|
||||||
providerTypeToBlobStoreMap = Maps.newHashMap();
|
|
||||||
for (String hint : getBlobstoreContexts(props)) {
|
|
||||||
providerTypeToBlobStoreMap.put(hint, ContextBuilder.newBuilder(hint)
|
|
||||||
.modules(modules).overrides(props).build(BlobStoreContext.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a queue for submitting store tweet requests and the application's base URL
|
|
||||||
PlatformServices platform = PlatformServices.get(servletConfig.getServletContext());
|
|
||||||
queue = platform.getTaskQueue("twitter");
|
|
||||||
baseUrl = platform.getBaseUrl();
|
|
||||||
|
|
||||||
LOGGER.trace("Members initialized. Twitter: '%s', container: '%s', provider types: '%s'", twitterClient,
|
|
||||||
container, providerTypeToBlobStoreMap.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Iterable<String> getBlobstoreContexts(Properties props) {
|
|
||||||
Set<String> contexts = new CredentialsCollector().apply(props).keySet();
|
|
||||||
String explicitContexts = props.getProperty(PROPERTY_TWEETSTORE_BLOBSTORES);
|
|
||||||
if (explicitContexts != null) {
|
|
||||||
contexts = filter(contexts, in(copyOf(Splitter.on(',').split(explicitContexts))));
|
|
||||||
}
|
|
||||||
checkState(!contexts.isEmpty(), "no credentials available for any requested context");
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Properties loadJCloudsProperties() {
|
|
||||||
LOGGER.trace("About to read properties from '%s'", "/WEB-INF/jclouds.properties");
|
|
||||||
Properties props = new Properties();
|
|
||||||
InputStream input = servletConfig.getServletContext().getResourceAsStream("/WEB-INF/jclouds.properties");
|
|
||||||
try {
|
|
||||||
props.load(input);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
Closeables.closeQuietly(input);
|
|
||||||
}
|
|
||||||
LOGGER.trace("Properties successfully read.");
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public StoreTweetsController storeTweetsController() {
|
|
||||||
StoreTweetsController controller = new StoreTweetsController(providerTypeToBlobStoreMap, container, twitterClient);
|
|
||||||
injectServletConfig(controller);
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public AddTweetsController addTweetsController() {
|
|
||||||
AddTweetsController controller = new AddTweetsController(providerTypeToBlobStoreMap,
|
|
||||||
serviceToStoredTweetStatuses());
|
|
||||||
injectServletConfig(controller);
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public EnqueueStoresController enqueueStoresController() {
|
|
||||||
return new EnqueueStoresController(providerTypeToBlobStoreMap, queue, baseUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ClearTweetsController clearTweetsController() {
|
|
||||||
return new ClearTweetsController(providerTypeToBlobStoreMap, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void injectServletConfig(Servlet servlet) {
|
|
||||||
LOGGER.trace("About to inject servlet config '%s'", servletConfig);
|
|
||||||
try {
|
|
||||||
servlet.init(checkNotNull(servletConfig));
|
|
||||||
} catch (ServletException exception) {
|
|
||||||
throw new BeanCreationException("Unable to instantiate " + servlet, exception);
|
|
||||||
}
|
|
||||||
LOGGER.trace("Successfully injected servlet config.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ServiceToStoredTweetStatuses serviceToStoredTweetStatuses() {
|
|
||||||
return new ServiceToStoredTweetStatuses(providerTypeToBlobStoreMap, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public HandlerMapping handlerMapping() {
|
|
||||||
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
|
|
||||||
Map<String, Object> urlMap = Maps.newHashMapWithExpectedSize(2);
|
|
||||||
urlMap.put("/store/*", storeTweetsController());
|
|
||||||
urlMap.put("/tweets/*", addTweetsController());
|
|
||||||
urlMap.put("/stores/*", enqueueStoresController());
|
|
||||||
urlMap.put("/clear/*", clearTweetsController());
|
|
||||||
mapping.setUrlMap(urlMap);
|
|
||||||
/*
|
|
||||||
* "/store", "/tweets" and "/stores" are part of the servlet mapping and thus
|
|
||||||
* stripped by the mapping if using default settings.
|
|
||||||
*/
|
|
||||||
mapping.setAlwaysUseFullPath(true);
|
|
||||||
return mapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public HandlerAdapter servletHandlerAdapter() {
|
|
||||||
return new SimpleServletHandlerAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void destroy() throws Exception {
|
|
||||||
LOGGER.trace("About to close contexts.");
|
|
||||||
for (BlobStoreContext context : providerTypeToBlobStoreMap.values()) {
|
|
||||||
context.close();
|
|
||||||
}
|
|
||||||
LOGGER.trace("Contexts closed.");
|
|
||||||
LOGGER.trace("About to purge request queue.");
|
|
||||||
queue.destroy();
|
|
||||||
LOGGER.trace("Request queue purged.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.springframework.web.context.ServletConfigAware#setServletConfig(javax.servlet.ServletConfig
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setServletConfig(ServletConfig servletConfig) {
|
|
||||||
this.servletConfig = servletConfig;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config.util;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.base.Predicates.notNull;
|
|
||||||
import static com.google.common.collect.Collections2.filter;
|
|
||||||
import static com.google.common.collect.Collections2.transform;
|
|
||||||
import static com.google.common.collect.ImmutableSet.copyOf;
|
|
||||||
import static com.google.common.collect.Maps.filterValues;
|
|
||||||
import static org.jclouds.util.Maps2.fromKeys;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector.Credential;
|
|
||||||
|
|
||||||
import com.google.common.annotations.GwtIncompatible;
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads provider credentials from a {@link Properties} bag.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class CredentialsCollector implements Function<Properties, Map<String, Credential>> {
|
|
||||||
private static final String IDENTITY_PROPERTY_SUFFIX = ".identity";
|
|
||||||
private static final String CREDENTIAL_PROPERTY_SUFFIX = ".credential";
|
|
||||||
|
|
||||||
// using the identity for provider name extraction
|
|
||||||
private static final Pattern IDENTITY_PROPERTY_PATTERN =
|
|
||||||
Pattern.compile("([a-zA-Z0-9-]+)" + Pattern.quote(IDENTITY_PROPERTY_SUFFIX));
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Credential> apply(final Properties properties) {
|
|
||||||
Collection<String> providerNames = transform(
|
|
||||||
filter(properties.stringPropertyNames(), MatchesPattern.matches(IDENTITY_PROPERTY_PATTERN)),
|
|
||||||
new Function<String, String>() {
|
|
||||||
@Override
|
|
||||||
public String apply(String input) {
|
|
||||||
Matcher matcher = IDENTITY_PROPERTY_PATTERN.matcher(input);
|
|
||||||
// as a side-effect, sets the matching group!
|
|
||||||
checkState(matcher.matches(), "'%s' should match '%s'", input, IDENTITY_PROPERTY_PATTERN);
|
|
||||||
return matcher.group(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
* Providers without a credential property result in null values, which are
|
|
||||||
* removed from the returned map.
|
|
||||||
*/
|
|
||||||
return filterValues(fromKeys(copyOf(providerNames), new Function<String, Credential>() {
|
|
||||||
@Override
|
|
||||||
public Credential apply(String providerName) {
|
|
||||||
String identity = properties.getProperty(providerName + IDENTITY_PROPERTY_SUFFIX);
|
|
||||||
String credential = properties.getProperty(providerName + CREDENTIAL_PROPERTY_SUFFIX);
|
|
||||||
return (((identity != null) && (credential != null))
|
|
||||||
? new Credential(identity, credential)
|
|
||||||
: null);
|
|
||||||
}
|
|
||||||
}), notNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Credential {
|
|
||||||
private final String identity;
|
|
||||||
private final String credential;
|
|
||||||
|
|
||||||
public Credential(String identity, String credential) {
|
|
||||||
this.identity = checkNotNull(identity, "identity");
|
|
||||||
this.credential = checkNotNull(credential, "credential");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result
|
|
||||||
+ ((credential == null) ? 0 : credential.hashCode());
|
|
||||||
result = prime * result
|
|
||||||
+ ((identity == null) ? 0 : identity.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
Credential other = (Credential) obj;
|
|
||||||
if (credential == null) {
|
|
||||||
if (other.credential != null)
|
|
||||||
return false;
|
|
||||||
} else if (!credential.equals(other.credential))
|
|
||||||
return false;
|
|
||||||
if (identity == null) {
|
|
||||||
if (other.identity != null)
|
|
||||||
return false;
|
|
||||||
} else if (!identity.equals(other.identity))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIdentity() {
|
|
||||||
return identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCredential() {
|
|
||||||
return credential;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@GwtIncompatible(value = "java.util.regex.Pattern")
|
|
||||||
private static class MatchesPattern implements Predicate<String> {
|
|
||||||
private final Pattern pattern;
|
|
||||||
|
|
||||||
private MatchesPattern(Pattern pattern) {
|
|
||||||
this.pattern = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(String input) {
|
|
||||||
return pattern.matcher(input).matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MatchesPattern matches(Pattern pattern) {
|
|
||||||
return new MatchesPattern(pattern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config.util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
|
||||||
|
|
||||||
import com.google.common.io.Closeables;
|
|
||||||
import com.google.inject.Provider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public class PropertiesLoader implements Provider<Properties>{
|
|
||||||
private static final String PROPERTIES_FILE = "/WEB-INF/jclouds.properties";
|
|
||||||
|
|
||||||
private final Properties properties;
|
|
||||||
|
|
||||||
public PropertiesLoader(ServletContext context) {
|
|
||||||
properties = loadJcloudsProperties(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Properties loadJcloudsProperties(ServletContext context) {
|
|
||||||
InputStream input = context.getResourceAsStream(PROPERTIES_FILE);
|
|
||||||
Properties props = new Properties();
|
|
||||||
try {
|
|
||||||
props.load(input);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
Closeables.closeQuietly(input);
|
|
||||||
}
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Properties get() {
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.RequestDispatcher;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows an example of how to use @{link BlobStoreContext} injected with Guice.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class AddTweetsController extends HttpServlet implements
|
|
||||||
Function<Set<String>, List<StoredTweetStatus>> {
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 3888348023150822683L;
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final ServiceToStoredTweetStatuses blobStoreContextToContainerResult;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public AddTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
ServiceToStoredTweetStatuses blobStoreContextToContainerResult) {
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.blobStoreContextToContainerResult = blobStoreContextToContainerResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
|
||||||
throws ServletException, IOException {
|
|
||||||
try {
|
|
||||||
addMyTweetsToRequest(request);
|
|
||||||
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/tweets.jsp");
|
|
||||||
dispatcher.forward(request, response);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error listing containers");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addMyTweetsToRequest(HttpServletRequest request) throws InterruptedException,
|
|
||||||
ExecutionException, TimeoutException {
|
|
||||||
request.setAttribute("tweets", apply(contexts.keySet()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<StoredTweetStatus> apply(Set<String> in) {
|
|
||||||
List<StoredTweetStatus> statuses = Lists.newArrayList();
|
|
||||||
for (Iterable<StoredTweetStatus> list : Iterables.transform(in,
|
|
||||||
blobStoreContextToContainerResult)) {
|
|
||||||
Iterables.addAll(statuses, list);
|
|
||||||
}
|
|
||||||
return statuses;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Strings.nullToEmpty;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grab tweets related to me and store them into blobstores
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class ClearTweetsController extends HttpServlet {
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@VisibleForTesting
|
|
||||||
public ClearTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container) {
|
|
||||||
this.container = container;
|
|
||||||
this.contexts = contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void clearContainer(String contextName) {
|
|
||||||
BlobStoreContext context = checkNotNull(contexts.get(contextName),
|
|
||||||
"no context for %s in %s", contextName, contexts.keySet());
|
|
||||||
try {
|
|
||||||
context.getBlobStore().clearContainer(container);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error clearing tweets in %s/%s", container, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (nullToEmpty(request.getHeader("X-Originator")).equals("admin")) {
|
|
||||||
try {
|
|
||||||
String contextName = checkNotNull(request.getHeader("context"), "missing header context");
|
|
||||||
logger.info("clearing tweets in %s/%s", container, contextName);
|
|
||||||
clearContainer(contextName);
|
|
||||||
logger.debug("done clearing tweets");
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error clearing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.common.base.Strings.nullToEmpty;
|
|
||||||
import static org.jclouds.demo.paas.RunnableHttpRequest.PLATFORM_REQUEST_ORIGINATOR_HEADER;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.paas.reference.PaasConstants;
|
|
||||||
import org.jclouds.demo.paas.service.taskqueue.TaskQueue;
|
|
||||||
import org.jclouds.http.HttpRequest;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.common.collect.ImmutableMultimap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds tasks to retrieve and store tweets in all registered contexts to an async
|
|
||||||
* task queue.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
* @see StoreTweetsController
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class EnqueueStoresController extends HttpServlet {
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Set<String> contextNames;
|
|
||||||
private final TaskQueue taskQueue;
|
|
||||||
private final String baseUrl;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public EnqueueStoresController(Map<String, BlobStoreContext> contexts, TaskQueue taskQueue,
|
|
||||||
@Named(PaasConstants.PROPERTY_PLATFORM_BASE_URL) String baseUrl) {
|
|
||||||
contextNames = contexts.keySet();
|
|
||||||
this.taskQueue = taskQueue;
|
|
||||||
this.baseUrl = baseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void enqueueStoreTweetTasks() {
|
|
||||||
for (String contextName : contextNames) {
|
|
||||||
logger.debug("enqueuing task to store tweets in blobstore '%s'", contextName);
|
|
||||||
taskQueue.add(taskQueue.getHttpRequestFactory().create(HttpRequest.builder()
|
|
||||||
.endpoint(URI.create(baseUrl + "/store/do"))
|
|
||||||
.headers(ImmutableMultimap.of("context", contextName))
|
|
||||||
.method("GET").build()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (!nullToEmpty(request.getHeader(PLATFORM_REQUEST_ORIGINATOR_HEADER)).equals("scheduler")) {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
enqueueStoreTweetTasks();
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Strings.nullToEmpty;
|
|
||||||
import static org.jclouds.demo.paas.RunnableHttpRequest.PLATFORM_REQUEST_ORIGINATOR_HEADER;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.jclouds.rest.AuthorizationException;
|
|
||||||
|
|
||||||
import twitter4j.Status;
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grab tweets related to me and store them into blobstores
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class StoreTweetsController extends HttpServlet {
|
|
||||||
|
|
||||||
private static final class StatusToBlob implements Function<Status, Blob> {
|
|
||||||
private final BlobMap map;
|
|
||||||
|
|
||||||
private StatusToBlob(BlobMap map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Blob apply(Status from) {
|
|
||||||
Blob to = map.blobBuilder().name(from.getId() + "").build();
|
|
||||||
to.setPayload(from.getText());
|
|
||||||
to.getPayload().getContentMetadata().setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
to.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, from.getUser().getScreenName());
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final Twitter client;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@VisibleForTesting
|
|
||||||
public StoreTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container, Twitter client) {
|
|
||||||
this.container = container;
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void addMyTweets(String contextName, Iterable<Status> responseList) {
|
|
||||||
BlobStoreContext context = checkNotNull(contexts.get(contextName), "no context for " + contextName + " in "
|
|
||||||
+ contexts.keySet());
|
|
||||||
BlobMap map = context.createBlobMap(container);
|
|
||||||
for (Status status : responseList) {
|
|
||||||
Blob blob = null;
|
|
||||||
try {
|
|
||||||
blob = new StatusToBlob(map).apply(status);
|
|
||||||
map.put(status.getId() + "", blob);
|
|
||||||
} catch (AuthorizationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweet %s (blob[%s]) on map %s/%s", status.getId(), blob, context, container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (nullToEmpty(request.getHeader(PLATFORM_REQUEST_ORIGINATOR_HEADER)).equals("taskqueue-twitter")) {
|
|
||||||
try {
|
|
||||||
String contextName = checkNotNull(request.getHeader("context"), "missing header context");
|
|
||||||
logger.info("retrieving tweets");
|
|
||||||
addMyTweets(contextName, client.getMentions());
|
|
||||||
logger.debug("done storing tweets");
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.domain;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class StoredTweetStatus implements Comparable<StoredTweetStatus>, Serializable {
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = -3257496189689220018L;
|
|
||||||
private final String service;
|
|
||||||
private final String host;
|
|
||||||
private final String container;
|
|
||||||
private final String id;
|
|
||||||
private final String from;
|
|
||||||
private final String tweet;
|
|
||||||
private final String status;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "StoredTweetStatus [container=" + container + ", from=" + from + ", host=" + host
|
|
||||||
+ ", id=" + id + ", service=" + service + ", status=" + status + ", tweet=" + tweet
|
|
||||||
+ "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredTweetStatus(String service, String host, String container, String id, String from,
|
|
||||||
String tweet, String status) {
|
|
||||||
this.service = service;
|
|
||||||
this.host = host;
|
|
||||||
this.container = container;
|
|
||||||
this.id = id;
|
|
||||||
this.from = from;
|
|
||||||
this.tweet = tweet;
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ((container == null) ? 0 : container.hashCode());
|
|
||||||
result = prime * result + ((from == null) ? 0 : from.hashCode());
|
|
||||||
result = prime * result + ((host == null) ? 0 : host.hashCode());
|
|
||||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
|
||||||
result = prime * result + ((service == null) ? 0 : service.hashCode());
|
|
||||||
result = prime * result + ((tweet == null) ? 0 : tweet.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
StoredTweetStatus other = (StoredTweetStatus) obj;
|
|
||||||
if (container == null) {
|
|
||||||
if (other.container != null)
|
|
||||||
return false;
|
|
||||||
} else if (!container.equals(other.container))
|
|
||||||
return false;
|
|
||||||
if (from == null) {
|
|
||||||
if (other.from != null)
|
|
||||||
return false;
|
|
||||||
} else if (!from.equals(other.from))
|
|
||||||
return false;
|
|
||||||
if (host == null) {
|
|
||||||
if (other.host != null)
|
|
||||||
return false;
|
|
||||||
} else if (!host.equals(other.host))
|
|
||||||
return false;
|
|
||||||
if (id == null) {
|
|
||||||
if (other.id != null)
|
|
||||||
return false;
|
|
||||||
} else if (!id.equals(other.id))
|
|
||||||
return false;
|
|
||||||
if (service == null) {
|
|
||||||
if (other.service != null)
|
|
||||||
return false;
|
|
||||||
} else if (!service.equals(other.service))
|
|
||||||
return false;
|
|
||||||
if (tweet == null) {
|
|
||||||
if (other.tweet != null)
|
|
||||||
return false;
|
|
||||||
} else if (!tweet.equals(other.tweet))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getService() {
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHost() {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContainer() {
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFrom() {
|
|
||||||
return from;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTweet() {
|
|
||||||
return tweet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int compareTo(StoredTweetStatus o) {
|
|
||||||
if (id == null)
|
|
||||||
return -1;
|
|
||||||
return (int) ((this == o) ? 0 : id.compareTo(o.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class KeyToStoredTweetStatus implements Function<String, StoredTweetStatus> {
|
|
||||||
private final String host;
|
|
||||||
private final BlobMap map;
|
|
||||||
private final String service;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
KeyToStoredTweetStatus(BlobMap map, String service, String host, String container) {
|
|
||||||
this.host = host;
|
|
||||||
this.map = map;
|
|
||||||
this.service = service;
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredTweetStatus apply(String id) {
|
|
||||||
String status;
|
|
||||||
String from;
|
|
||||||
String tweet;
|
|
||||||
try {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
Blob blob = map.get(id);
|
|
||||||
status = ((System.currentTimeMillis() - start) + "ms");
|
|
||||||
from = blob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME);
|
|
||||||
tweet = Strings2.toString(blob.getPayload());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error listing container %s//%s/%s", service, container, id);
|
|
||||||
status = (e.getMessage());
|
|
||||||
tweet = "";
|
|
||||||
from = "";
|
|
||||||
}
|
|
||||||
return new StoredTweetStatus(service, host, container, id, from, tweet, status);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.jclouds.Context;
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class ServiceToStoredTweetStatuses implements Function<String, Iterable<StoredTweetStatus>> {
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ServiceToStoredTweetStatuses(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container) {
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
public Iterable<StoredTweetStatus> apply(String service) {
|
|
||||||
BlobStoreContext context = contexts.get(service);
|
|
||||||
String host = URI.create(context.unwrap(Context.class).getProviderMetadata().getEndpoint()).getHost();
|
|
||||||
try {
|
|
||||||
BlobMap blobMap = context.createBlobMap(container);
|
|
||||||
Set<String> blobs = blobMap.keySet();
|
|
||||||
return Iterables.transform(blobs, new KeyToStoredTweetStatus(blobMap, service, host,
|
|
||||||
container));
|
|
||||||
} catch (Exception e) {
|
|
||||||
StoredTweetStatus result = new StoredTweetStatus(service, host, container, null, null,
|
|
||||||
null, e.getMessage());
|
|
||||||
logger.error(e, "Error listing service %s", service);
|
|
||||||
return ImmutableList.of(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.reference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration properties and constants used in TweetStore connections.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public interface TweetStoreConstants {
|
|
||||||
static final String PROPERTY_TWEETSTORE_BLOBSTORES = "jclouds.tweetstore.blobstores";
|
|
||||||
static final String PROPERTY_TWEETSTORE_CONTAINER = "jclouds.tweetstore.container";
|
|
||||||
/**
|
|
||||||
* Note that this has to conform to restrictions of all blobstores. for
|
|
||||||
* example, azure doesn't support periods.
|
|
||||||
*/
|
|
||||||
static final String SENDER_NAME = "sendername";
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.reference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration properties and constants used in Twitter connections.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public interface TwitterConstants {
|
|
||||||
static final String PROPERTY_TWITTER_CONSUMER_KEY = "twitter.consumer.identity";
|
|
||||||
static final String PROPERTY_TWITTER_CONSUMER_SECRET = "twitter.consumer.credential";
|
|
||||||
static final String PROPERTY_TWITTER_ACCESSTOKEN = "twitter.access.identity";
|
|
||||||
static final String PROPERTY_TWITTER_ACCESSTOKEN_SECRET = "twitter.access.credential";
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
# PaaS vendor specific files go in here
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<job-scheduling-data xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData http://www.quartz-scheduler.org/xml/job_scheduling_data_2_0.xsd"
|
|
||||||
version="2.0">
|
|
||||||
|
|
||||||
<schedule>
|
|
||||||
<job>
|
|
||||||
<name>enqueue-store-tweet-tasks</name>
|
|
||||||
<description>Enqueue 'store tweet' tasks for all contexts</description>
|
|
||||||
<job-class>org.jclouds.demo.paas.service.scheduler.HttpRequestJob</job-class>
|
|
||||||
<job-data-map>
|
|
||||||
<entry>
|
|
||||||
<key>url</key>
|
|
||||||
<value>/stores/do</value>
|
|
||||||
</entry>
|
|
||||||
</job-data-map>
|
|
||||||
</job>
|
|
||||||
|
|
||||||
<trigger>
|
|
||||||
<calendar-interval>
|
|
||||||
<name>submit-recurring-job</name>
|
|
||||||
<job-name>enqueue-store-tweet-tasks</job-name>
|
|
||||||
<repeat-interval>10</repeat-interval>
|
|
||||||
<repeat-interval-unit>MINUTE</repeat-interval-unit>
|
|
||||||
</calendar-interval>
|
|
||||||
</trigger>
|
|
||||||
</schedule>
|
|
||||||
</job-scheduling-data>
|
|
|
@ -1,28 +0,0 @@
|
||||||
#============================================================================
|
|
||||||
# Configure Main Scheduler Properties
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
org.quartz.scheduler.skipUpdateCheck: true
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Configure ThreadPool
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
|
|
||||||
org.quartz.threadPool.threadCount: 1
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Configure JobStore
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
|
|
||||||
|
|
||||||
#============================================================================
|
|
||||||
# Configure the Job Initialization Plugin
|
|
||||||
#============================================================================
|
|
||||||
|
|
||||||
org.quartz.plugin.jobInitializer.class: org.jclouds.demo.paas.service.scheduler.quartz.plugins.TransactionlessXmlSchedulingDataProcessorPlugin
|
|
||||||
org.quartz.plugin.jobInitializer.fileNames: jobs.xml
|
|
||||||
org.quartz.plugin.jobInitializer.failOnFileNotFound: true
|
|
||||||
org.quartz.plugin.jobInitializer.scanInterval: 0
|
|
||||||
#org.quartz.plugin.jobInitializer.wrapInUserTransaction: false
|
|
|
@ -1,78 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
|
|
||||||
version="2.5">
|
|
||||||
<display-name>jclouds-tweetstore</display-name>
|
|
||||||
|
|
||||||
<context-param>
|
|
||||||
<param-name>quartz:scheduler-context-servlet-context-key</param-name>
|
|
||||||
<param-value>servlet-context</param-value>
|
|
||||||
</context-param>
|
|
||||||
|
|
||||||
<!-- must be started first -->
|
|
||||||
<listener>
|
|
||||||
<listener-class>org.jclouds.demo.paas.config.PlatformServicesInitializer</listener-class>
|
|
||||||
</listener>
|
|
||||||
|
|
||||||
<listener>
|
|
||||||
<listener-class>org.quartz.ee.servlet.QuartzInitializerListener</listener-class>
|
|
||||||
</listener>
|
|
||||||
|
|
||||||
<!-- Servlets -->
|
|
||||||
<servlet>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
|
||||||
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
|
|
||||||
instead of the default XmlWebApplicationContext -->
|
|
||||||
<init-param>
|
|
||||||
<param-name>contextClass</param-name>
|
|
||||||
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
|
|
||||||
</init-param>
|
|
||||||
<init-param>
|
|
||||||
<param-name>contextConfigLocation</param-name>
|
|
||||||
<param-value>org.jclouds.demo.tweetstore.config.SpringServletConfig</param-value>
|
|
||||||
</init-param>
|
|
||||||
</servlet>
|
|
||||||
|
|
||||||
<!-- should be kept in sync with the mappings defined in SpringServletConfig -->
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<url-pattern>/store/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<url-pattern>/tweets/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<url-pattern>/stores/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<url-pattern>/clear/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<welcome-file-list>
|
|
||||||
<welcome-file>index.jsp</welcome-file>
|
|
||||||
</welcome-file-list>
|
|
||||||
</web-app>
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.9 KiB |
|
@ -1,30 +0,0 @@
|
||||||
<%--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
--%>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>jclouds: anyweight cloudware for java</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Welcome!</h2>
|
|
||||||
<p>Click <a href="/tweets/get">here</a> to see tweets about jclouds.</p>
|
|
||||||
<p><img src="/images/cloudfoundry-logo.png" alt="Powered by Cloud Foundry" /></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,108 +0,0 @@
|
||||||
<%--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
--%>
|
|
||||||
<%@ page buffer="20kb"%>
|
|
||||||
<%@ taglib uri="http://displaytag.sf.net" prefix="display"%>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>jclouds: anyweight cloudware for java</title>
|
|
||||||
<style type="text/css">
|
|
||||||
<!--
|
|
||||||
table.staticheader {
|
|
||||||
text-decoration: none;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
width: 98%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader th {
|
|
||||||
padding: 3px 3px 3px 3px !important;
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader td {
|
|
||||||
padding: 3px 3px 3px 3px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader thead tr {
|
|
||||||
position: relative;
|
|
||||||
height: 10px;
|
|
||||||
background-color: #D7E5F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody {
|
|
||||||
height:800px;
|
|
||||||
overflow-x:hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow:scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr {
|
|
||||||
height: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr.odd {
|
|
||||||
background-color: #eee
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr.tableRowEven,tr.even {
|
|
||||||
background-color: #ddd
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr td:last-child {
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody td {
|
|
||||||
padding: 2px 4px 2px 4px !important;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
div.TableContainer {
|
|
||||||
height: 800px;
|
|
||||||
overflow-x:hidden;
|
|
||||||
overflow-y:auto;
|
|
||||||
}
|
|
||||||
-->
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Tweets in Clouds</h2>
|
|
||||||
<table width="100%" border="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="TableContainer">
|
|
||||||
<display:table name="tweets" defaultsort="1" cellpadding="5" cellspacing="1" class="staticheader">
|
|
||||||
<display:column property="id" title="Tweet ID" />
|
|
||||||
<display:column property="from" title="Who Said it" />
|
|
||||||
<display:column property="tweet" title="Tweet" />
|
|
||||||
<display:column property="service" title="Cloud" />
|
|
||||||
<display:column property="host" title="Host" />
|
|
||||||
<display:column property="status" title="Status" />
|
|
||||||
</display:table>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="/images/cloudfoundry-logo.png" alt="Powered by Cloud Foundry" /></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,82 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config.util;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
import static org.testng.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector;
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector.Credential;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code CredentialsCollector}
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class CredentialsCollectorTest {
|
|
||||||
private CredentialsCollector collector = new CredentialsCollector();
|
|
||||||
|
|
||||||
public void testEmptyProperties() {
|
|
||||||
assertTrue(collector.apply(new Properties()).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNoCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("not-an-identity",
|
|
||||||
"v1", "not-a-credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Properties propertiesOf(Map<String, String> entries) {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
properties.putAll(entries);
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNonMatchingCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("non_matching.identity", "v1",
|
|
||||||
"non_matching.credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testIncompleteCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("acme.identity", "v1",
|
|
||||||
"acme-2.credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("acme.identity", "v1",
|
|
||||||
"acme.credential", "v2", "acme-2.identity", "v3",
|
|
||||||
"acme-2.credential", "v4"));
|
|
||||||
assertEquals(collector.apply(properties),
|
|
||||||
ImmutableMap.of("acme", new Credential("v1", "v2"),
|
|
||||||
"acme-2", new Credential("v3", "v4")));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
import org.testng.collections.Maps;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code AddTweetsController}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class AddTweetsControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createServices(String container) throws InterruptedException,
|
|
||||||
ExecutionException {
|
|
||||||
Map<String, BlobStoreContext> services = Maps.newHashMap();
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
for (String name : new String[] { "1", "2" }) {
|
|
||||||
BlobStoreContext context = ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class);
|
|
||||||
context.getAsyncBlobStore().createContainerInLocation(null, container).get();
|
|
||||||
Blob blob = context.getAsyncBlobStore().blobBuilder("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
context.getAsyncBlobStore().putBlob(container, blob).get();
|
|
||||||
services.put(name, context);
|
|
||||||
}
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String container = "container";
|
|
||||||
Map<String, BlobStoreContext> contexts = createServices(container);
|
|
||||||
|
|
||||||
ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container);
|
|
||||||
AddTweetsController controller = new AddTweetsController(contexts, function);
|
|
||||||
List<StoredTweetStatus> list = controller.apply(ImmutableSet.of("1", "2"));
|
|
||||||
assertEquals(list.size(), 2);
|
|
||||||
assertEquals(list, ImmutableList.of(new StoredTweetStatus("1", "localhost", container, "1",
|
|
||||||
"frank", "I love beans!", null), new StoredTweetStatus("2", "localhost", container,
|
|
||||||
"1", "frank", "I love beans!", null)));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code AddTweetsController}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class ClearTweetsControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createBlobStores(String container) throws InterruptedException, ExecutionException {
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
Map<String, BlobStoreContext> contexts = ImmutableMap.<String, BlobStoreContext>of(
|
|
||||||
"test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class),
|
|
||||||
"test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class));
|
|
||||||
for (BlobStoreContext blobstore : contexts.values()) {
|
|
||||||
blobstore.getBlobStore().createContainerInLocation(null, container);
|
|
||||||
Blob blob = blobstore.getAsyncBlobStore().blobBuilder("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
blobstore.getBlobStore().putBlob(container, blob);
|
|
||||||
}
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testClearTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String container = ClearTweetsControllerTest.class.getName() + "#container";
|
|
||||||
Map<String, BlobStoreContext> contexts = createBlobStores(container);
|
|
||||||
|
|
||||||
ClearTweetsController controller = new ClearTweetsController(contexts,
|
|
||||||
container);
|
|
||||||
controller.clearContainer("test1");
|
|
||||||
controller.clearContainer("test2");
|
|
||||||
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
assertEquals(context.getBlobStore().countBlobs(container), 0, context.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.easymock.EasyMock.*;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.demo.paas.RunnableHttpRequest;
|
|
||||||
import org.jclouds.demo.paas.RunnableHttpRequest.Factory;
|
|
||||||
import org.jclouds.demo.paas.service.taskqueue.TaskQueue;
|
|
||||||
import org.jclouds.http.HttpRequest;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
import com.google.common.collect.ImmutableMultimap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code EnqueueStoresController}
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class EnqueueStoresControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createBlobStores() {
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
Map<String, BlobStoreContext> contexts = ImmutableMap.<String, BlobStoreContext>of(
|
|
||||||
"test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class),
|
|
||||||
"test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class));
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEnqueueStores() {
|
|
||||||
Map<String, BlobStoreContext> stores = createBlobStores();
|
|
||||||
TaskQueue taskQueue = createMock(TaskQueue.class);
|
|
||||||
Factory httpRequestFactory = createMock(Factory.class);
|
|
||||||
EnqueueStoresController function = new EnqueueStoresController(stores,
|
|
||||||
taskQueue, "http://localhost:8080");
|
|
||||||
|
|
||||||
expect(taskQueue.getHttpRequestFactory()).andStubReturn(httpRequestFactory);
|
|
||||||
|
|
||||||
HttpRequest storeInTest1Request = HttpRequest.builder().endpoint(
|
|
||||||
URI.create("http://localhost:8080/store/do"))
|
|
||||||
.headers(ImmutableMultimap.of("context", "test1")).method("GET").build();
|
|
||||||
RunnableHttpRequest storeInTest1Task = null;
|
|
||||||
expect(httpRequestFactory.create(eq(storeInTest1Request))).andReturn(storeInTest1Task);
|
|
||||||
|
|
||||||
HttpRequest storeInTest2Request = HttpRequest.builder().endpoint(
|
|
||||||
URI.create("http://localhost:8080/store/do"))
|
|
||||||
.headers(ImmutableMultimap.of("context", "test2")).method("GET").build();
|
|
||||||
RunnableHttpRequest storeInTest2Task = null;
|
|
||||||
expect(httpRequestFactory.create(eq(storeInTest2Request))).andReturn(storeInTest2Task);
|
|
||||||
|
|
||||||
taskQueue.add(storeInTest1Task);
|
|
||||||
expectLastCall();
|
|
||||||
taskQueue.add(storeInTest2Task);
|
|
||||||
expectLastCall();
|
|
||||||
replay(httpRequestFactory, taskQueue);
|
|
||||||
|
|
||||||
function.enqueueStoreTweetTasks();
|
|
||||||
|
|
||||||
verify(taskQueue);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.easymock.EasyMock.createMock;
|
|
||||||
import static org.easymock.EasyMock.expect;
|
|
||||||
import static org.easymock.EasyMock.replay;
|
|
||||||
import static org.easymock.EasyMock.verify;
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import twitter4j.Status;
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
import twitter4j.User;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code StoreTweetsController}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class StoreTweetsControllerTest {
|
|
||||||
|
|
||||||
Twitter createTwitter() {
|
|
||||||
return createMock(Twitter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createBlobStores() throws InterruptedException, ExecutionException {
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
Map<String, BlobStoreContext> contexts = ImmutableMap.<String, BlobStoreContext>of(
|
|
||||||
"test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class),
|
|
||||||
"test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class));
|
|
||||||
for (BlobStoreContext blobstore : contexts.values()) {
|
|
||||||
blobstore.getAsyncBlobStore().createContainerInLocation(null, "favo").get();
|
|
||||||
}
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
Map<String, BlobStoreContext> stores = createBlobStores();
|
|
||||||
StoreTweetsController function = new StoreTweetsController(stores, "favo", createTwitter());
|
|
||||||
|
|
||||||
User frank = createMock(User.class);
|
|
||||||
expect(frank.getScreenName()).andReturn("frank").atLeastOnce();
|
|
||||||
|
|
||||||
Status frankStatus = createMock(Status.class);
|
|
||||||
expect(frankStatus.getId()).andReturn(1l).atLeastOnce();
|
|
||||||
expect(frankStatus.getUser()).andReturn(frank).atLeastOnce();
|
|
||||||
expect(frankStatus.getText()).andReturn("I love beans!").atLeastOnce();
|
|
||||||
|
|
||||||
User jimmy = createMock(User.class);
|
|
||||||
expect(jimmy.getScreenName()).andReturn("jimmy").atLeastOnce();
|
|
||||||
|
|
||||||
Status jimmyStatus = createMock(Status.class);
|
|
||||||
expect(jimmyStatus.getId()).andReturn(2l).atLeastOnce();
|
|
||||||
expect(jimmyStatus.getUser()).andReturn(jimmy).atLeastOnce();
|
|
||||||
expect(jimmyStatus.getText()).andReturn("cloud is king").atLeastOnce();
|
|
||||||
|
|
||||||
replay(frank);
|
|
||||||
replay(frankStatus);
|
|
||||||
replay(jimmy);
|
|
||||||
replay(jimmyStatus);
|
|
||||||
|
|
||||||
function.addMyTweets("test1", ImmutableList.of(frankStatus, jimmyStatus));
|
|
||||||
function.addMyTweets("test2", ImmutableList.of(frankStatus, jimmyStatus));
|
|
||||||
|
|
||||||
verify(frank);
|
|
||||||
verify(frankStatus);
|
|
||||||
verify(jimmy);
|
|
||||||
verify(jimmyStatus);
|
|
||||||
|
|
||||||
for (Entry<String, BlobStoreContext> entry : stores.entrySet()) {
|
|
||||||
BlobMap map = entry.getValue().createBlobMap("favo");
|
|
||||||
Blob frankBlob = map.get("1");
|
|
||||||
assertEquals(frankBlob.getMetadata().getName(), "1");
|
|
||||||
assertEquals(frankBlob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME), "frank");
|
|
||||||
assertEquals(frankBlob.getMetadata().getContentMetadata().getContentType(), "text/plain");
|
|
||||||
assertEquals(Strings2.toString(frankBlob.getPayload()), "I love beans!");
|
|
||||||
|
|
||||||
Blob jimmyBlob = map.get("2");
|
|
||||||
assertEquals(jimmyBlob.getMetadata().getName(), "2");
|
|
||||||
assertEquals(jimmyBlob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME), "jimmy");
|
|
||||||
assertEquals(jimmyBlob.getMetadata().getContentMetadata().getContentType(), "text/plain");
|
|
||||||
assertEquals(Strings2.toString(jimmyBlob.getPayload()), "cloud is king");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code KeyToStoredTweetStatus}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class KeyToStoredTweetStatusTest {
|
|
||||||
|
|
||||||
BlobMap createMap() throws InterruptedException, ExecutionException {
|
|
||||||
BlobStoreContext context =
|
|
||||||
ContextBuilder.newBuilder(TransientApiMetadata.builder().build()).build(BlobStoreContext.class);
|
|
||||||
context.getBlobStore().createContainerInLocation(null, "test1");
|
|
||||||
return context.createBlobMap("test1");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
BlobMap map = createMap();
|
|
||||||
Blob blob = map.blobBuilder().name("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
map.put("1", blob);
|
|
||||||
String host = "localhost";
|
|
||||||
String service = "stub";
|
|
||||||
String container = "tweetstore";
|
|
||||||
|
|
||||||
KeyToStoredTweetStatus function = new KeyToStoredTweetStatus(map, service, host, container);
|
|
||||||
StoredTweetStatus result = function.apply("1");
|
|
||||||
|
|
||||||
StoredTweetStatus expected = new StoredTweetStatus(service, host, container, "1", "frank",
|
|
||||||
"I love beans!", null);
|
|
||||||
|
|
||||||
assertEquals(result, expected);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
import org.testng.collections.Maps;
|
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code ServiceToStoredTweetStatuses}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class ServiceToStoredTweetStatusesTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createServices(String container) throws InterruptedException,
|
|
||||||
ExecutionException {
|
|
||||||
Map<String, BlobStoreContext> services = Maps.newHashMap();
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
for (String name : new String[] { "1", "2" }) {
|
|
||||||
BlobStoreContext context = ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class);
|
|
||||||
context.getAsyncBlobStore().createContainerInLocation(null, container).get();
|
|
||||||
Blob blob = context.getAsyncBlobStore().blobBuilder("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
context.getAsyncBlobStore().putBlob(container, blob).get();
|
|
||||||
services.put(name, context);
|
|
||||||
}
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String container = "container";
|
|
||||||
Map<String, BlobStoreContext> contexts = createServices(container);
|
|
||||||
|
|
||||||
ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container);
|
|
||||||
|
|
||||||
assertEquals(Iterables.getLast(function.apply("1")), new StoredTweetStatus("1", "localhost",
|
|
||||||
container, "1", "frank", "I love beans!", null));
|
|
||||||
|
|
||||||
assertEquals(Iterables.getLast(function.apply("2")), new StoredTweetStatus("2", "localhost",
|
|
||||||
container, "1", "frank", "I love beans!", null));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.integration;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.io.Closeables.closeQuietly;
|
|
||||||
import static java.lang.String.format;
|
|
||||||
import static org.jclouds.demo.tweetstore.integration.util.Zips.zipDir;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.cloudfoundry.client.lib.CloudApplication.AppState;
|
|
||||||
import org.cloudfoundry.client.lib.CloudFoundryClient;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic "server facade" functionality to deploy a WAR to Cloud Foundry.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public class CloudFoundryServer {
|
|
||||||
private static final String CLOUD_FOUNDRY_APPLICATION_URL_SUFFIX = ".cloudfoundry.com";
|
|
||||||
|
|
||||||
protected CloudFoundryClient client;
|
|
||||||
protected String appName;
|
|
||||||
|
|
||||||
public void writePropertiesAndStartServer(final String address, final String warfile,
|
|
||||||
String target, String username, String password, Properties props) throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String propsfile = String.format("%1$s/WEB-INF/jclouds.properties", warfile);
|
|
||||||
System.err.println("file: " + propsfile);
|
|
||||||
storeProperties(propsfile, props);
|
|
||||||
assert new File(propsfile).exists();
|
|
||||||
|
|
||||||
client = new CloudFoundryClient(username, password, target);
|
|
||||||
client.login();
|
|
||||||
appName = getAppName(address);
|
|
||||||
deploy(warfile);
|
|
||||||
client.logout();
|
|
||||||
TimeUnit.SECONDS.sleep(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deploy(String explodedWar) throws IOException {
|
|
||||||
File war = zipDir(explodedWar, format("%s-cloudfoundry.war", explodedWar));
|
|
||||||
client.uploadApplication(appName, war);
|
|
||||||
|
|
||||||
// adapted from https://github.com/cloudfoundry/vcap-java-client/blob/master/cloudfoundry-maven-plugin/src/main/java/org/cloudfoundry/maven/Update.java
|
|
||||||
AppState appState = client.getApplication(appName).getState();
|
|
||||||
switch (appState) {
|
|
||||||
case STOPPED:
|
|
||||||
client.startApplication(appName);
|
|
||||||
break;
|
|
||||||
case STARTED:
|
|
||||||
client.restartApplication(appName);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException(format("Unexpected application state '%s'", appState));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void storeProperties(String filename, Properties props)
|
|
||||||
throws IOException {
|
|
||||||
FileOutputStream targetFile = new FileOutputStream(filename);
|
|
||||||
try {
|
|
||||||
props.store(targetFile, "test");
|
|
||||||
} finally {
|
|
||||||
closeQuietly(targetFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getAppName(String applicationUrl) {
|
|
||||||
checkArgument(applicationUrl.endsWith(CLOUD_FOUNDRY_APPLICATION_URL_SUFFIX),
|
|
||||||
"Application URL '%s' does not end in '%s'", applicationUrl,
|
|
||||||
CLOUD_FOUNDRY_APPLICATION_URL_SUFFIX);
|
|
||||||
|
|
||||||
return applicationUrl.substring(0,
|
|
||||||
applicationUrl.length() - CLOUD_FOUNDRY_APPLICATION_URL_SUFFIX.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() throws Exception {
|
|
||||||
checkState(client != null, "'stop' called before 'writePropertiesAndStartServer'");
|
|
||||||
client.login();
|
|
||||||
client.stopApplication(appName);
|
|
||||||
client.logout();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.integration;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_BLOBSTORES;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN_SECRET;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_KEY;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_SECRET;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.jclouds.Context;
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.config.SpringServletConfig;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.StoreTweetsController;
|
|
||||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
import org.testng.annotations.AfterTest;
|
|
||||||
import org.testng.annotations.BeforeTest;
|
|
||||||
import org.testng.annotations.Parameters;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import twitter4j.ResponseList;
|
|
||||||
import twitter4j.Status;
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
import twitter4j.TwitterException;
|
|
||||||
import twitter4j.TwitterFactory;
|
|
||||||
import twitter4j.conf.Configuration;
|
|
||||||
import twitter4j.conf.ConfigurationBuilder;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.inject.Module;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts up the Google App Engine for Java Development environment and deploys an application which
|
|
||||||
* tests accesses twitter and blobstores.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "live", singleThreaded = true)
|
|
||||||
public class TweetStoreLiveTest {
|
|
||||||
|
|
||||||
CloudFoundryServer server;
|
|
||||||
private URL url;
|
|
||||||
private Map<String, BlobStoreContext> contexts;
|
|
||||||
private String container;
|
|
||||||
|
|
||||||
private static final Iterable<String> blobstores =
|
|
||||||
Splitter.on(',').split(System.getProperty(PROPERTY_TWEETSTORE_BLOBSTORES,
|
|
||||||
"cloudfiles-us,aws-s3,azureblob"));
|
|
||||||
private static final Properties props = new Properties();
|
|
||||||
|
|
||||||
@BeforeTest
|
|
||||||
void clearAndCreateContainers() throws InterruptedException, ExecutionException, TimeoutException, IOException,
|
|
||||||
TwitterException {
|
|
||||||
container = getRequiredSystemProperty(PROPERTY_TWEETSTORE_CONTAINER);
|
|
||||||
|
|
||||||
props.setProperty(PROPERTY_TWEETSTORE_CONTAINER, container);
|
|
||||||
props.setProperty(SpringServletConfig.PROPERTY_BLOBSTORE_CONTEXTS, Joiner.on(',').join(blobstores));
|
|
||||||
|
|
||||||
// put all identity/credential pairs into the client
|
|
||||||
addCredentialsForBlobStores(props);
|
|
||||||
|
|
||||||
// example of an ad-hoc client configuration
|
|
||||||
addConfigurationForTwitter(props);
|
|
||||||
|
|
||||||
// for testing, capture logs.
|
|
||||||
final Set<Module> wiring = ImmutableSet.<Module> of(new Log4JLoggingModule());
|
|
||||||
this.contexts = Maps.newConcurrentMap();
|
|
||||||
|
|
||||||
for (String provider : blobstores) {
|
|
||||||
contexts.put(provider, ContextBuilder.newBuilder(provider).modules(wiring)
|
|
||||||
.overrides(props).build(BlobStoreContext.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
Configuration conf = new ConfigurationBuilder()
|
|
||||||
.setOAuthConsumerKey(props.getProperty(PROPERTY_TWITTER_CONSUMER_KEY))
|
|
||||||
.setOAuthConsumerSecret(props.getProperty(PROPERTY_TWITTER_CONSUMER_SECRET))
|
|
||||||
.setOAuthAccessToken(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN))
|
|
||||||
.setOAuthAccessTokenSecret(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET))
|
|
||||||
.build();
|
|
||||||
Twitter client = new TwitterFactory(conf).getInstance();
|
|
||||||
StoreTweetsController controller = new StoreTweetsController(contexts, container, client);
|
|
||||||
|
|
||||||
ResponseList<Status> statuses = client.getMentions();
|
|
||||||
|
|
||||||
boolean deleted = false;
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
if (context.getBlobStore().containerExists(container)) {
|
|
||||||
System.err.printf("deleting container %s at %s%n", container,
|
|
||||||
context.unwrap(Context.class).getProviderMetadata().getEndpoint());
|
|
||||||
context.getBlobStore().deleteContainer(container);
|
|
||||||
deleted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (deleted) {
|
|
||||||
System.err.println("sleeping 60 seconds to allow containers to clear");
|
|
||||||
Thread.sleep(60000);
|
|
||||||
}
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
System.err.printf("creating container %s at %s%n", container,
|
|
||||||
context.unwrap(Context.class).getProviderMetadata().getEndpoint());
|
|
||||||
context.getBlobStore().createContainerInLocation(null, container);
|
|
||||||
}
|
|
||||||
if (deleted) {
|
|
||||||
System.err.println("sleeping 5 seconds to allow containers to create");
|
|
||||||
Thread.sleep(5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Entry<String, BlobStoreContext> entry : contexts.entrySet()) {
|
|
||||||
System.err.printf("filling container %s at %s%n", container, entry.getKey());
|
|
||||||
controller.addMyTweets(entry.getKey(), statuses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getRequiredSystemProperty(String key) {
|
|
||||||
return checkNotNull(System.getProperty(key), key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addConfigurationForTwitter(Properties props) {
|
|
||||||
props.setProperty(PROPERTY_TWITTER_CONSUMER_KEY,
|
|
||||||
getRequiredSystemProperty("test." + PROPERTY_TWITTER_CONSUMER_KEY));
|
|
||||||
props.setProperty(PROPERTY_TWITTER_CONSUMER_SECRET,
|
|
||||||
getRequiredSystemProperty("test." + PROPERTY_TWITTER_CONSUMER_SECRET));
|
|
||||||
props.setProperty(PROPERTY_TWITTER_ACCESSTOKEN,
|
|
||||||
getRequiredSystemProperty("test." + PROPERTY_TWITTER_ACCESSTOKEN));
|
|
||||||
props.setProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET,
|
|
||||||
getRequiredSystemProperty("test." + PROPERTY_TWITTER_ACCESSTOKEN_SECRET));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addCredentialsForBlobStores(Properties props) {
|
|
||||||
for (String provider : blobstores) {
|
|
||||||
props.setProperty(provider + ".identity",
|
|
||||||
getRequiredSystemProperty("test." + provider + ".identity"));
|
|
||||||
props.setProperty(provider + ".credential",
|
|
||||||
getRequiredSystemProperty("test." + provider + ".credential"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeTest
|
|
||||||
@Parameters({ "warfile", "cloudfoundry.address", "cloudfoundry.port", "cloudfoundry.target", "cloudfoundry.username", "cloudfoundry.password" })
|
|
||||||
public void startDevAppServer(final String warfile, final String address, final String port,
|
|
||||||
String target, String username, String password) throws Exception {
|
|
||||||
url = new URL(String.format("http://%s:%s", address, port));
|
|
||||||
server = new CloudFoundryServer();
|
|
||||||
server.writePropertiesAndStartServer(address, warfile, target, username, password, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldPass() throws InterruptedException, IOException {
|
|
||||||
InputStream i = url.openStream();
|
|
||||||
String string = Strings2.toStringAndClose(i);
|
|
||||||
assert string.indexOf("Welcome") >= 0 : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(dependsOnMethods = "shouldPass", expectedExceptions = IOException.class)
|
|
||||||
public void shouldFail() throws InterruptedException, IOException {
|
|
||||||
new URL(url, "/store/do").openStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(dependsOnMethods = "shouldFail")
|
|
||||||
public void testPrimeContainers() throws IOException, InterruptedException {
|
|
||||||
URL gurl = new URL(url, "/store/do");
|
|
||||||
for (String context : blobstores) {
|
|
||||||
System.out.println("storing at context: " + context);
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) gurl.openConnection();
|
|
||||||
connection.addRequestProperty("X-Platform-Originator", "taskqueue-twitter");
|
|
||||||
connection.addRequestProperty("context", context);
|
|
||||||
InputStream i = connection.getInputStream();
|
|
||||||
String string = Strings2.toStringAndClose(i);
|
|
||||||
assert string.indexOf("Done!") >= 0 : string;
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
System.err.println("sleeping 20 seconds to allow for eventual consistency delay");
|
|
||||||
Thread.sleep(20000);
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
assert context.createInputStreamMap(container).size() > 0 : context.unwrap(Context.class).getProviderMetadata().getEndpoint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(invocationCount = 5, dependsOnMethods = "testPrimeContainers")
|
|
||||||
public void testSerial() throws InterruptedException, IOException {
|
|
||||||
URL gurl = new URL(url, "/tweets/get");
|
|
||||||
InputStream i = gurl.openStream();
|
|
||||||
String string = Strings2.toStringAndClose(i);
|
|
||||||
assert string.indexOf("Tweets in Clouds") >= 0 : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(invocationCount = 10, dependsOnMethods = "testPrimeContainers", threadPoolSize = 3)
|
|
||||||
public void testParallel() throws InterruptedException, IOException {
|
|
||||||
URL gurl = new URL(url, "/tweets/get");
|
|
||||||
InputStream i = gurl.openStream();
|
|
||||||
String string = Strings2.toStringAndClose(i);
|
|
||||||
assert string.indexOf("Tweets in Clouds") >= 0 : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterTest
|
|
||||||
public void stopDevAppServer() throws Exception {
|
|
||||||
server.stop();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.integration.util;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.codehaus.plexus.archiver.zip.ZipArchiver;
|
|
||||||
|
|
||||||
public class Zips {
|
|
||||||
|
|
||||||
public static File zipDir(String dirToZip, String zipFile) throws IOException {
|
|
||||||
ZipArchiver archiver = new ZipArchiver();
|
|
||||||
archiver.addDirectory(new File(dirToZip));
|
|
||||||
File zip = new File(zipFile);
|
|
||||||
archiver.setDestFile(zip);
|
|
||||||
archiver.createArchive();
|
|
||||||
return zip;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
|
||||||
<!--
|
|
||||||
For more configuration infromation and examples see the Apache
|
|
||||||
Log4j website: http://logging.apache.org/log4j/
|
|
||||||
-->
|
|
||||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
|
|
||||||
debug="false">
|
|
||||||
|
|
||||||
<!-- A time/date based rolling appender -->
|
|
||||||
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
|
|
||||||
<param name="File" value="target/test-data/jclouds-wire.log" />
|
|
||||||
<param name="Append" value="true" />
|
|
||||||
|
|
||||||
<!-- Rollover at midnight each day -->
|
|
||||||
<param name="DatePattern" value="'.'yyyy-MM-dd" />
|
|
||||||
|
|
||||||
<param name="Threshold" value="TRACE" />
|
|
||||||
|
|
||||||
<layout class="org.apache.log4j.PatternLayout">
|
|
||||||
<!-- The default pattern: Date Priority [Category] Message\n -->
|
|
||||||
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The full pattern: Date MS Priority [Category]
|
|
||||||
(Thread:NDC) Message\n <param name="ConversionPattern"
|
|
||||||
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
|
|
||||||
-->
|
|
||||||
</layout>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- A time/date based rolling appender -->
|
|
||||||
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
|
|
||||||
<param name="File" value="target/test-data/jclouds.log" />
|
|
||||||
<param name="Append" value="true" />
|
|
||||||
|
|
||||||
<!-- Rollover at midnight each day -->
|
|
||||||
<param name="DatePattern" value="'.'yyyy-MM-dd" />
|
|
||||||
|
|
||||||
<param name="Threshold" value="TRACE" />
|
|
||||||
|
|
||||||
<layout class="org.apache.log4j.PatternLayout">
|
|
||||||
<!-- The default pattern: Date Priority [Category] Message\n -->
|
|
||||||
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The full pattern: Date MS Priority [Category]
|
|
||||||
(Thread:NDC) Message\n <param name="ConversionPattern"
|
|
||||||
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
|
|
||||||
-->
|
|
||||||
</layout>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
|
|
||||||
<appender-ref ref="FILE" />
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
|
|
||||||
<appender-ref ref="WIREFILE" />
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- ================ -->
|
|
||||||
<!-- Limit categories -->
|
|
||||||
<!-- ================ -->
|
|
||||||
|
|
||||||
<category name="org.jclouds">
|
|
||||||
<priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNC" />
|
|
||||||
</category>
|
|
||||||
|
|
||||||
<category name="jclouds.headers">
|
|
||||||
<priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNCWIRE" />
|
|
||||||
</category>
|
|
||||||
<category name="jclouds.wire">
|
|
||||||
<priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNCWIRE" />
|
|
||||||
</category>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
<category name="jclouds.wire"> <priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNCWIRE" /> </category> <category
|
|
||||||
name="jclouds.signature"> <priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNCWIRE" /> </category>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- ======================= -->
|
|
||||||
<!-- Setup the Root category -->
|
|
||||||
<!-- ======================= -->
|
|
||||||
|
|
||||||
<root>
|
|
||||||
<priority value="WARN" />
|
|
||||||
</root>
|
|
||||||
|
|
||||||
</log4j:configuration>
|
|
|
@ -1,64 +0,0 @@
|
||||||
====
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
====
|
|
||||||
|
|
||||||
This sample is a "port" of jclouds-demo-gae-tweetstore with the initial context setup
|
|
||||||
and wiring carried out with Spring. It is intended to demonstrate how to integrate
|
|
||||||
jclouds into your Spring application.
|
|
||||||
|
|
||||||
It should not be regarded as a sample of how to write a web application using Spring,
|
|
||||||
however! The original jclouds-demo-gae-tweetstore has been modified in as few places as
|
|
||||||
possible; it has not been rewritten in the style of a Spring MVC application.
|
|
||||||
|
|
||||||
A guide to generating Twitter consumer keys and access tokens is at http://tinyurl.com/2fhebgb
|
|
||||||
|
|
||||||
This sample uses the Google App Engine for Java SDK located at
|
|
||||||
http://code.google.com/p/googleappengine/downloads/list
|
|
||||||
|
|
||||||
Please unzip the above file and modify your maven settings.xml like below before
|
|
||||||
attempting to run 'mvn -Plive install'
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<id>appengine</id>
|
|
||||||
<activation>
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<appengine.sdk.root>/path/to/appengine-java-sdk-1.4.2</appengine.home>
|
|
||||||
<appengine.applicationid>yourappid</appengine.applicationid>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<id>keys</id>
|
|
||||||
<activation>
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<test.aws-s3.identity>YOUR_ACCESS_KEY_ID</test.aws-s3.identity>
|
|
||||||
<test.aws-s3.credential>YOUR_SECRET_KEY</test.aws-s3.credential>
|
|
||||||
<test.cloudfiles-us.identity>YOUR_USER</test.cloudfiles-us.identity>
|
|
||||||
<test.cloudfiles-us.credential>YOUR_HEX_KEY</test.cloudfiles-us.credential>
|
|
||||||
<test.azureblob.identity>YOUR_ACCOUNT</test.azureblob.identity>
|
|
||||||
<test.azureblob.credential>YOUR_BASE64_ENCODED_KEY</test.azureblob.credential>
|
|
||||||
<test.twitter.gae-tweetstore-spring.consumer.identity>YOUR_TWITTER_CONSUMER_KEY</test.twitter.gae-tweetstore-spring.consumer.identity>
|
|
||||||
<test.twitter.gae-tweetstore-spring.consumer.credential>YOUR_TWITTER_CONSUMER_SECRET</test.twitter.gae-tweetstore-spring.consumer.credential>
|
|
||||||
<test.twitter.gae-tweetstore-spring.access.identity>YOUR_TWITTER_ACCESSTOKEN</test.twitter.gae-tweetstore-spring.access.identity>
|
|
||||||
<test.twitter.gae-tweetstore-spring.access.credential>YOUR_TWITTER_ACCESSTOKEN_SECRET</test.twitter.gae-tweetstore-spring.access.credential>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
|
@ -1,156 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.jclouds</groupId>
|
|
||||||
<artifactId>jclouds-demos-tweetstore-project</artifactId>
|
|
||||||
<version>1.6.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>jclouds-demo-gae-tweetstore-spring</artifactId>
|
|
||||||
<packaging>war</packaging>
|
|
||||||
<name>jclouds TweetStore for Google App Engine (Spring)</name>
|
|
||||||
<description>JClouds TweetStore for Google App Engine using Spring for Dependency Injection</description>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<!--
|
|
||||||
note you must set the property ${appengine.sdk.root} to a valid
|
|
||||||
extraction of appengine-java-sdk
|
|
||||||
-->
|
|
||||||
<appengine.applicationid>jclouds-tweetstore-spring</appengine.applicationid>
|
|
||||||
<appengine.sdk.version>1.6.1</appengine.sdk.version>
|
|
||||||
<devappserver.address>localhost</devappserver.address>
|
|
||||||
<devappserver.port>8088</devappserver.port>
|
|
||||||
<jclouds.tweetstore.container>jclouds-gae-tweetstore-spring</jclouds.tweetstore.container>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jclouds.driver</groupId>
|
|
||||||
<artifactId>jclouds-gae</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-context</artifactId>
|
|
||||||
<version>3.0.5.RELEASE</version>
|
|
||||||
<!-- using the SLF4J/commons-logging bridge -->
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>commons-logging</groupId>
|
|
||||||
<artifactId>commons-logging</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-webmvc</artifactId>
|
|
||||||
<version>3.0.5.RELEASE</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>cglib</groupId>
|
|
||||||
<artifactId>cglib-nodep</artifactId>
|
|
||||||
<version>2.2</version>
|
|
||||||
<scope>runtime</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Google App Engine API -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.appengine</groupId>
|
|
||||||
<artifactId>appengine-api-1.0-sdk</artifactId>
|
|
||||||
<version>${appengine.sdk.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.appengine</groupId>
|
|
||||||
<artifactId>appengine-tools-sdk</artifactId>
|
|
||||||
<version>${appengine.sdk.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>live</id>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>integration</id>
|
|
||||||
<phase>integration-test</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>test</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<systemPropertyVariables>
|
|
||||||
<test.twitter.consumer.identity>${test.twitter.gae-tweetstore-spring.consumer.identity}</test.twitter.consumer.identity>
|
|
||||||
<test.twitter.consumer.credential>${test.twitter.gae-tweetstore-spring.consumer.credential}</test.twitter.consumer.credential>
|
|
||||||
<test.twitter.access.identity>${test.twitter.gae-tweetstore-spring.access.identity}</test.twitter.access.identity>
|
|
||||||
<test.twitter.access.credential>${test.twitter.gae-tweetstore-spring.access.credential}</test.twitter.access.credential>
|
|
||||||
<test.azureblob.identity>${test.azureblob.identity}</test.azureblob.identity>
|
|
||||||
<test.azureblob.credential>${test.azureblob.credential}</test.azureblob.credential>
|
|
||||||
<test.cloudfiles-us.identity>${test.cloudfiles-us.identity}</test.cloudfiles-us.identity>
|
|
||||||
<test.cloudfiles-us.credential>${test.cloudfiles-us.credential}</test.cloudfiles-us.credential>
|
|
||||||
<test.aws-s3.identity>${test.aws-s3.identity}</test.aws-s3.identity>
|
|
||||||
<test.aws-s3.credential>${test.aws-s3.credential}</test.aws-s3.credential>
|
|
||||||
<test.cloudonestorage.identity>${test.cloudonestorage.identity}</test.cloudonestorage.identity>
|
|
||||||
<test.cloudonestorage.credential>${test.cloudonestorage.credential}</test.cloudonestorage.credential>
|
|
||||||
<test.ninefold-storage.identity>${test.ninefold-storage.identity}</test.ninefold-storage.identity>
|
|
||||||
<test.ninefold-storage.credential>${test.ninefold-storage.credential}</test.ninefold-storage.credential>
|
|
||||||
<appengine.sdk.root>${appengine.sdk.root}</appengine.sdk.root>
|
|
||||||
<devappserver.address>${devappserver.address}</devappserver.address>
|
|
||||||
<devappserver.port>${devappserver.port}</devappserver.port>
|
|
||||||
<jclouds.tweetstore.blobstores>${jclouds.tweetstore.blobstores}</jclouds.tweetstore.blobstores>
|
|
||||||
<jclouds.tweetstore.container>test.${jclouds.tweetstore.container}</jclouds.tweetstore.container>
|
|
||||||
<warfile>${project.build.directory}/${project.artifactId}</warfile>
|
|
||||||
</systemPropertyVariables>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<id>deploy</id>
|
|
||||||
<properties>
|
|
||||||
<!-- classifier to choose the correct jclouds.properties file -->
|
|
||||||
<tweetstore.instance>gae-tweetstore-spring</tweetstore.instance>
|
|
||||||
</properties>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>net.kindleit</groupId>
|
|
||||||
<artifactId>maven-gae-plugin</artifactId>
|
|
||||||
<version>0.9.2</version>
|
|
||||||
<configuration>
|
|
||||||
<serverId>google-appengine</serverId>
|
|
||||||
<sdkDir>${appengine.sdk.root}</sdkDir>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</project>
|
|
|
@ -1,32 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config;
|
|
||||||
|
|
||||||
import static org.jclouds.logging.LoggingModules.firstOrJDKLoggingModule;
|
|
||||||
|
|
||||||
import org.jclouds.logging.Logger.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring config that provides a logger.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
abstract class LoggingConfig {
|
|
||||||
protected static final LoggerFactory LOGGER_FACTORY = firstOrJDKLoggingModule().createLoggerFactory();
|
|
||||||
}
|
|
|
@ -1,241 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.base.Predicates.in;
|
|
||||||
import static com.google.common.collect.ImmutableSet.copyOf;
|
|
||||||
import static com.google.common.collect.Sets.filter;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_BLOBSTORES;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN_SECRET;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_KEY;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_SECRET;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.annotation.PreDestroy;
|
|
||||||
import javax.servlet.Servlet;
|
|
||||||
import javax.servlet.ServletConfig;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.AddTweetsController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.ClearTweetsController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.EnqueueStoresController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.StoreTweetsController;
|
|
||||||
import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses;
|
|
||||||
import org.jclouds.gae.config.GoogleAppEngineConfigurationModule;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.context.ServletConfigAware;
|
|
||||||
import org.springframework.web.servlet.HandlerAdapter;
|
|
||||||
import org.springframework.web.servlet.HandlerMapping;
|
|
||||||
import org.springframework.web.servlet.handler.SimpleServletHandlerAdapter;
|
|
||||||
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
|
|
||||||
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
import twitter4j.TwitterFactory;
|
|
||||||
import twitter4j.conf.ConfigurationBuilder;
|
|
||||||
|
|
||||||
import com.google.appengine.api.taskqueue.Queue;
|
|
||||||
import com.google.appengine.api.taskqueue.QueueFactory;
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.io.Closeables;
|
|
||||||
import com.google.inject.Module;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates servlets (using resources from the {@link SpringAppConfig}) and mappings.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
* @see SpringAppConfig
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
public class SpringServletConfig extends LoggingConfig implements ServletConfigAware {
|
|
||||||
public static final String PROPERTY_BLOBSTORE_CONTEXTS = "blobstore.contexts";
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LOGGER_FACTORY.getLogger(SpringServletConfig.class.getName());
|
|
||||||
|
|
||||||
private ServletConfig servletConfig;
|
|
||||||
|
|
||||||
private Map<String, BlobStoreContext> providerTypeToBlobStoreMap;
|
|
||||||
private Twitter twitterClient;
|
|
||||||
private String container;
|
|
||||||
private Queue queue;
|
|
||||||
|
|
||||||
@PostConstruct
|
|
||||||
public void initialize() throws IOException {
|
|
||||||
Properties props = loadJCloudsProperties();
|
|
||||||
LOGGER.trace("About to initialize members.");
|
|
||||||
|
|
||||||
Module googleModule = new GoogleAppEngineConfigurationModule();
|
|
||||||
Set<Module> modules = ImmutableSet.<Module> of(googleModule);
|
|
||||||
// shared across all blobstores and used to retrieve tweets
|
|
||||||
try {
|
|
||||||
twitter4j.conf.Configuration twitterConf = new ConfigurationBuilder()
|
|
||||||
.setOAuthConsumerKey(props.getProperty(PROPERTY_TWITTER_CONSUMER_KEY))
|
|
||||||
.setOAuthConsumerSecret(props.getProperty(PROPERTY_TWITTER_CONSUMER_SECRET))
|
|
||||||
.setOAuthAccessToken(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN))
|
|
||||||
.setOAuthAccessTokenSecret(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET))
|
|
||||||
.build();
|
|
||||||
twitterClient = new TwitterFactory(twitterConf).getInstance();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new IllegalArgumentException("properties for twitter not configured properly in " + props.toString(), e);
|
|
||||||
}
|
|
||||||
// common namespace for storing tweets
|
|
||||||
container = checkNotNull(props.getProperty(PROPERTY_TWEETSTORE_CONTAINER), PROPERTY_TWEETSTORE_CONTAINER);
|
|
||||||
|
|
||||||
// instantiate and store references to all blobstores by provider name
|
|
||||||
providerTypeToBlobStoreMap = Maps.newHashMap();
|
|
||||||
for (String hint : getBlobstoreContexts(props)) {
|
|
||||||
providerTypeToBlobStoreMap.put(hint, ContextBuilder.newBuilder(hint)
|
|
||||||
.modules(modules).overrides(props).build(BlobStoreContext.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a queue for submitting store tweet requests
|
|
||||||
queue = QueueFactory.getQueue("twitter");
|
|
||||||
|
|
||||||
LOGGER.trace("Members initialized. Twitter: '%s', container: '%s', provider types: '%s'", twitterClient,
|
|
||||||
container, providerTypeToBlobStoreMap.keySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Iterable<String> getBlobstoreContexts(Properties props) {
|
|
||||||
Set<String> contexts = new CredentialsCollector().apply(props).keySet();
|
|
||||||
String explicitContexts = props.getProperty(PROPERTY_TWEETSTORE_BLOBSTORES);
|
|
||||||
if (explicitContexts != null) {
|
|
||||||
contexts = filter(contexts, in(copyOf(Splitter.on(',').split(explicitContexts))));
|
|
||||||
}
|
|
||||||
checkState(!contexts.isEmpty(), "no credentials available for any requested context");
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Properties loadJCloudsProperties() {
|
|
||||||
LOGGER.trace("About to read properties from '%s'", "/WEB-INF/jclouds.properties");
|
|
||||||
Properties props = new Properties();
|
|
||||||
InputStream input = servletConfig.getServletContext().getResourceAsStream("/WEB-INF/jclouds.properties");
|
|
||||||
try {
|
|
||||||
props.load(input);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
Closeables.closeQuietly(input);
|
|
||||||
}
|
|
||||||
LOGGER.trace("Properties successfully read.");
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public StoreTweetsController storeTweetsController() {
|
|
||||||
StoreTweetsController controller = new StoreTweetsController(providerTypeToBlobStoreMap, container, twitterClient);
|
|
||||||
injectServletConfig(controller);
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public AddTweetsController addTweetsController() {
|
|
||||||
AddTweetsController controller = new AddTweetsController(providerTypeToBlobStoreMap,
|
|
||||||
serviceToStoredTweetStatuses());
|
|
||||||
injectServletConfig(controller);
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public EnqueueStoresController enqueueStoresController() {
|
|
||||||
return new EnqueueStoresController(providerTypeToBlobStoreMap, queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ClearTweetsController clearTweetsController() {
|
|
||||||
return new ClearTweetsController(providerTypeToBlobStoreMap, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void injectServletConfig(Servlet servlet) {
|
|
||||||
LOGGER.trace("About to inject servlet config '%s'", servletConfig);
|
|
||||||
try {
|
|
||||||
servlet.init(checkNotNull(servletConfig));
|
|
||||||
} catch (ServletException exception) {
|
|
||||||
throw new BeanCreationException("Unable to instantiate " + servlet, exception);
|
|
||||||
}
|
|
||||||
LOGGER.trace("Successfully injected servlet config.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ServiceToStoredTweetStatuses serviceToStoredTweetStatuses() {
|
|
||||||
return new ServiceToStoredTweetStatuses(providerTypeToBlobStoreMap, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public HandlerMapping handlerMapping() {
|
|
||||||
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
|
|
||||||
Map<String, Object> urlMap = Maps.newHashMapWithExpectedSize(2);
|
|
||||||
urlMap.put("/store/*", storeTweetsController());
|
|
||||||
urlMap.put("/tweets/*", addTweetsController());
|
|
||||||
urlMap.put("/stores/*", enqueueStoresController());
|
|
||||||
urlMap.put("/clear/*", clearTweetsController());
|
|
||||||
mapping.setUrlMap(urlMap);
|
|
||||||
/*
|
|
||||||
* "/store", "/tweets" and "/stores" are part of the servlet mapping and thus
|
|
||||||
* stripped by the mapping if using default settings.
|
|
||||||
*/
|
|
||||||
mapping.setAlwaysUseFullPath(true);
|
|
||||||
return mapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public HandlerAdapter servletHandlerAdapter() {
|
|
||||||
return new SimpleServletHandlerAdapter();
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void destroy() throws Exception {
|
|
||||||
LOGGER.trace("About to close contexts.");
|
|
||||||
for (BlobStoreContext context : providerTypeToBlobStoreMap.values()) {
|
|
||||||
context.close();
|
|
||||||
}
|
|
||||||
LOGGER.trace("Contexts closed.");
|
|
||||||
LOGGER.trace("About to purge request queue.");
|
|
||||||
queue.purge();
|
|
||||||
LOGGER.trace("Request queue purged.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* (non-Javadoc)
|
|
||||||
*
|
|
||||||
* @see
|
|
||||||
* org.springframework.web.context.ServletConfigAware#setServletConfig(javax.servlet.ServletConfig
|
|
||||||
* )
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setServletConfig(ServletConfig servletConfig) {
|
|
||||||
this.servletConfig = servletConfig;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config.util;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.base.Predicates.notNull;
|
|
||||||
import static com.google.common.collect.Collections2.filter;
|
|
||||||
import static com.google.common.collect.Collections2.transform;
|
|
||||||
import static com.google.common.collect.ImmutableSet.copyOf;
|
|
||||||
import static com.google.common.collect.Maps.filterValues;
|
|
||||||
import static org.jclouds.util.Maps2.fromKeys;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector.Credential;
|
|
||||||
|
|
||||||
import com.google.common.annotations.GwtIncompatible;
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads provider credentials from a {@link Properties} bag.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class CredentialsCollector implements Function<Properties, Map<String, Credential>> {
|
|
||||||
private static final String IDENTITY_PROPERTY_SUFFIX = ".identity";
|
|
||||||
private static final String CREDENTIAL_PROPERTY_SUFFIX = ".credential";
|
|
||||||
|
|
||||||
// using the identity for provider name extraction
|
|
||||||
private static final Pattern IDENTITY_PROPERTY_PATTERN =
|
|
||||||
Pattern.compile("([a-zA-Z0-9-]+)" + Pattern.quote(IDENTITY_PROPERTY_SUFFIX));
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Credential> apply(final Properties properties) {
|
|
||||||
Collection<String> providerNames = transform(
|
|
||||||
filter(properties.stringPropertyNames(), MatchesPattern.matches(IDENTITY_PROPERTY_PATTERN)),
|
|
||||||
new Function<String, String>() {
|
|
||||||
@Override
|
|
||||||
public String apply(String input) {
|
|
||||||
Matcher matcher = IDENTITY_PROPERTY_PATTERN.matcher(input);
|
|
||||||
// as a side-effect, sets the matching group!
|
|
||||||
checkState(matcher.matches(), "'%s' should match '%s'", input, IDENTITY_PROPERTY_PATTERN);
|
|
||||||
return matcher.group(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
* Providers without a credential property result in null values, which are
|
|
||||||
* removed from the returned map.
|
|
||||||
*/
|
|
||||||
return filterValues(fromKeys(copyOf(providerNames), new Function<String, Credential>() {
|
|
||||||
@Override
|
|
||||||
public Credential apply(String providerName) {
|
|
||||||
String identity = properties.getProperty(providerName + IDENTITY_PROPERTY_SUFFIX);
|
|
||||||
String credential = properties.getProperty(providerName + CREDENTIAL_PROPERTY_SUFFIX);
|
|
||||||
return (((identity != null) && (credential != null))
|
|
||||||
? new Credential(identity, credential)
|
|
||||||
: null);
|
|
||||||
}
|
|
||||||
}), notNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Credential {
|
|
||||||
private final String identity;
|
|
||||||
private final String credential;
|
|
||||||
|
|
||||||
public Credential(String identity, String credential) {
|
|
||||||
this.identity = checkNotNull(identity, "identity");
|
|
||||||
this.credential = checkNotNull(credential, "credential");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result
|
|
||||||
+ ((credential == null) ? 0 : credential.hashCode());
|
|
||||||
result = prime * result
|
|
||||||
+ ((identity == null) ? 0 : identity.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
Credential other = (Credential) obj;
|
|
||||||
if (credential == null) {
|
|
||||||
if (other.credential != null)
|
|
||||||
return false;
|
|
||||||
} else if (!credential.equals(other.credential))
|
|
||||||
return false;
|
|
||||||
if (identity == null) {
|
|
||||||
if (other.identity != null)
|
|
||||||
return false;
|
|
||||||
} else if (!identity.equals(other.identity))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIdentity() {
|
|
||||||
return identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCredential() {
|
|
||||||
return credential;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@GwtIncompatible(value = "java.util.regex.Pattern")
|
|
||||||
private static class MatchesPattern implements Predicate<String> {
|
|
||||||
private final Pattern pattern;
|
|
||||||
|
|
||||||
private MatchesPattern(Pattern pattern) {
|
|
||||||
this.pattern = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(String input) {
|
|
||||||
return pattern.matcher(input).matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MatchesPattern matches(Pattern pattern) {
|
|
||||||
return new MatchesPattern(pattern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.RequestDispatcher;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.appengine.repackaged.com.google.common.collect.Lists;
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows an example of how to use @{link BlobStoreContext} injected with Guice.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class AddTweetsController extends HttpServlet implements
|
|
||||||
Function<Set<String>, List<StoredTweetStatus>> {
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 3888348023150822683L;
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final ServiceToStoredTweetStatuses blobStoreContextToContainerResult;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public AddTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
ServiceToStoredTweetStatuses blobStoreContextToContainerResult) {
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.blobStoreContextToContainerResult = blobStoreContextToContainerResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
|
||||||
throws ServletException, IOException {
|
|
||||||
try {
|
|
||||||
addMyTweetsToRequest(request);
|
|
||||||
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/tweets.jsp");
|
|
||||||
dispatcher.forward(request, response);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error listing containers");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addMyTweetsToRequest(HttpServletRequest request) throws InterruptedException,
|
|
||||||
ExecutionException, TimeoutException {
|
|
||||||
request.setAttribute("tweets", apply(contexts.keySet()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<StoredTweetStatus> apply(Set<String> in) {
|
|
||||||
List<StoredTweetStatus> statuses = Lists.newArrayList();
|
|
||||||
for (Iterable<StoredTweetStatus> list : Iterables.transform(in,
|
|
||||||
blobStoreContextToContainerResult)) {
|
|
||||||
Iterables.addAll(statuses, list);
|
|
||||||
}
|
|
||||||
return statuses;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Strings.nullToEmpty;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grab tweets related to me and store them into blobstores
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class ClearTweetsController extends HttpServlet {
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@VisibleForTesting
|
|
||||||
public ClearTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container) {
|
|
||||||
this.container = container;
|
|
||||||
this.contexts = contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void clearContainer(String contextName) {
|
|
||||||
BlobStoreContext context = checkNotNull(contexts.get(contextName),
|
|
||||||
"no context for %s in %s", contextName, contexts.keySet());
|
|
||||||
try {
|
|
||||||
context.getBlobStore().clearContainer(container);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error clearing tweets in %s/%s", container, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (nullToEmpty(request.getHeader("X-Originator")).equals("admin")) {
|
|
||||||
try {
|
|
||||||
String contextName = checkNotNull(request.getHeader("context"), "missing header context");
|
|
||||||
logger.info("clearing tweets in %s/%s", container, contextName);
|
|
||||||
clearContainer(contextName);
|
|
||||||
logger.debug("done clearing tweets");
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error clearing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
|
|
||||||
import static com.google.appengine.repackaged.com.google.common.base.Strings.nullToEmpty;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.appengine.api.taskqueue.Queue;
|
|
||||||
import com.google.appengine.api.taskqueue.TaskOptions.Method;
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds tasks to retrieve and store tweets in all registered contexts to an async
|
|
||||||
* task queue.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
* @see StoreTweetsController
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class EnqueueStoresController extends HttpServlet {
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Set<String> contextNames;
|
|
||||||
private final Queue taskQueue;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public EnqueueStoresController(Map<String, BlobStoreContext> contexts,
|
|
||||||
Queue taskQueue) {
|
|
||||||
contextNames = contexts.keySet();
|
|
||||||
this.taskQueue = taskQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void enqueueStoreTweetTasks() {
|
|
||||||
for (String contextName : contextNames) {
|
|
||||||
logger.debug("enqueuing task to store tweets in blobstore '%s'", contextName);
|
|
||||||
taskQueue.add(withUrl("/store/do").header("context", contextName).method(Method.GET));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (!nullToEmpty(request.getHeader("X-AppEngine-Cron")).equals("true")) {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
enqueueStoreTweetTasks();
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.jclouds.rest.AuthorizationException;
|
|
||||||
|
|
||||||
import twitter4j.Status;
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grab tweets related to me and store them into blobstores
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class StoreTweetsController extends HttpServlet {
|
|
||||||
|
|
||||||
private static final class StatusToBlob implements Function<Status, Blob> {
|
|
||||||
private final BlobMap map;
|
|
||||||
|
|
||||||
private StatusToBlob(BlobMap map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Blob apply(Status from) {
|
|
||||||
Blob to = map.blobBuilder().name(from.getId() + "").build();
|
|
||||||
to.setPayload(from.getText());
|
|
||||||
to.getPayload().getContentMetadata().setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
to.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, from.getUser().getScreenName());
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final Twitter client;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@VisibleForTesting
|
|
||||||
public StoreTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container, Twitter client) {
|
|
||||||
this.container = container;
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void addMyTweets(String contextName, Iterable<Status> responseList) {
|
|
||||||
BlobStoreContext context = checkNotNull(contexts.get(contextName), "no context for " + contextName + " in "
|
|
||||||
+ contexts.keySet());
|
|
||||||
BlobMap map = context.createBlobMap(container);
|
|
||||||
for (Status status : responseList) {
|
|
||||||
Blob blob = null;
|
|
||||||
try {
|
|
||||||
blob = new StatusToBlob(map).apply(status);
|
|
||||||
map.put(status.getId() + "", blob);
|
|
||||||
} catch (AuthorizationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweet %s (blob[%s]) on map %s/%s", status.getId(), blob, context, container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (request.getHeader("X-AppEngine-QueueName") != null
|
|
||||||
&& request.getHeader("X-AppEngine-QueueName").equals("twitter")) {
|
|
||||||
try {
|
|
||||||
String contextName = checkNotNull(request.getHeader("context"), "missing header context");
|
|
||||||
logger.info("retrieving tweets");
|
|
||||||
addMyTweets(contextName, client.getMentions());
|
|
||||||
logger.debug("done storing tweets");
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.domain;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class StoredTweetStatus implements Comparable<StoredTweetStatus>, Serializable {
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = -3257496189689220018L;
|
|
||||||
private final String service;
|
|
||||||
private final String host;
|
|
||||||
private final String container;
|
|
||||||
private final String id;
|
|
||||||
private final String from;
|
|
||||||
private final String tweet;
|
|
||||||
private final String status;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "StoredTweetStatus [container=" + container + ", from=" + from + ", host=" + host
|
|
||||||
+ ", id=" + id + ", service=" + service + ", status=" + status + ", tweet=" + tweet
|
|
||||||
+ "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredTweetStatus(String service, String host, String container, String id, String from,
|
|
||||||
String tweet, String status) {
|
|
||||||
this.service = service;
|
|
||||||
this.host = host;
|
|
||||||
this.container = container;
|
|
||||||
this.id = id;
|
|
||||||
this.from = from;
|
|
||||||
this.tweet = tweet;
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ((container == null) ? 0 : container.hashCode());
|
|
||||||
result = prime * result + ((from == null) ? 0 : from.hashCode());
|
|
||||||
result = prime * result + ((host == null) ? 0 : host.hashCode());
|
|
||||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
|
||||||
result = prime * result + ((service == null) ? 0 : service.hashCode());
|
|
||||||
result = prime * result + ((tweet == null) ? 0 : tweet.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
StoredTweetStatus other = (StoredTweetStatus) obj;
|
|
||||||
if (container == null) {
|
|
||||||
if (other.container != null)
|
|
||||||
return false;
|
|
||||||
} else if (!container.equals(other.container))
|
|
||||||
return false;
|
|
||||||
if (from == null) {
|
|
||||||
if (other.from != null)
|
|
||||||
return false;
|
|
||||||
} else if (!from.equals(other.from))
|
|
||||||
return false;
|
|
||||||
if (host == null) {
|
|
||||||
if (other.host != null)
|
|
||||||
return false;
|
|
||||||
} else if (!host.equals(other.host))
|
|
||||||
return false;
|
|
||||||
if (id == null) {
|
|
||||||
if (other.id != null)
|
|
||||||
return false;
|
|
||||||
} else if (!id.equals(other.id))
|
|
||||||
return false;
|
|
||||||
if (service == null) {
|
|
||||||
if (other.service != null)
|
|
||||||
return false;
|
|
||||||
} else if (!service.equals(other.service))
|
|
||||||
return false;
|
|
||||||
if (tweet == null) {
|
|
||||||
if (other.tweet != null)
|
|
||||||
return false;
|
|
||||||
} else if (!tweet.equals(other.tweet))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getService() {
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHost() {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContainer() {
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFrom() {
|
|
||||||
return from;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTweet() {
|
|
||||||
return tweet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int compareTo(StoredTweetStatus o) {
|
|
||||||
if (id == null)
|
|
||||||
return -1;
|
|
||||||
return (int) ((this == o) ? 0 : id.compareTo(o.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class KeyToStoredTweetStatus implements Function<String, StoredTweetStatus> {
|
|
||||||
private final String host;
|
|
||||||
private final BlobMap map;
|
|
||||||
private final String service;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
KeyToStoredTweetStatus(BlobMap map, String service, String host, String container) {
|
|
||||||
this.host = host;
|
|
||||||
this.map = map;
|
|
||||||
this.service = service;
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredTweetStatus apply(String id) {
|
|
||||||
String status;
|
|
||||||
String from;
|
|
||||||
String tweet;
|
|
||||||
try {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
Blob blob = map.get(id);
|
|
||||||
status = ((System.currentTimeMillis() - start) + "ms");
|
|
||||||
from = blob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME);
|
|
||||||
tweet = Strings2.toString(blob.getPayload());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error listing container %s//%s/%s", service, container, id);
|
|
||||||
status = (e.getMessage());
|
|
||||||
tweet = "";
|
|
||||||
from = "";
|
|
||||||
}
|
|
||||||
return new StoredTweetStatus(service, host, container, id, from, tweet, status);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.jclouds.Context;
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class ServiceToStoredTweetStatuses implements Function<String, Iterable<StoredTweetStatus>> {
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ServiceToStoredTweetStatuses(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container) {
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
public Iterable<StoredTweetStatus> apply(String service) {
|
|
||||||
BlobStoreContext context = contexts.get(service);
|
|
||||||
String host = URI.create(context.unwrap(Context.class).getProviderMetadata().getEndpoint()).getHost();
|
|
||||||
try {
|
|
||||||
BlobMap blobMap = context.createBlobMap(container);
|
|
||||||
Set<String> blobs = blobMap.keySet();
|
|
||||||
return Iterables.transform(blobs, new KeyToStoredTweetStatus(blobMap, service, host,
|
|
||||||
container));
|
|
||||||
} catch (Exception e) {
|
|
||||||
StoredTweetStatus result = new StoredTweetStatus(service, host, container, null, null,
|
|
||||||
null, e.getMessage());
|
|
||||||
logger.error(e, "Error listing service %s", service);
|
|
||||||
return ImmutableList.of(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.reference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration properties and constants used in TweetStore connections.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public interface TweetStoreConstants {
|
|
||||||
static final String PROPERTY_TWEETSTORE_BLOBSTORES = "jclouds.tweetstore.blobstores";
|
|
||||||
static final String PROPERTY_TWEETSTORE_CONTAINER = "jclouds.tweetstore.container";
|
|
||||||
/**
|
|
||||||
* Note that this has to conform to restrictions of all blobstores. for
|
|
||||||
* example, azure doesn't support periods.
|
|
||||||
*/
|
|
||||||
static final String SENDER_NAME = "sendername";
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.reference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration properties and constants used in Twitter connections.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public interface TwitterConstants {
|
|
||||||
static final String PROPERTY_TWITTER_CONSUMER_KEY = "twitter.consumer.identity";
|
|
||||||
static final String PROPERTY_TWITTER_CONSUMER_SECRET = "twitter.consumer.credential";
|
|
||||||
static final String PROPERTY_TWITTER_ACCESSTOKEN = "twitter.access.identity";
|
|
||||||
static final String PROPERTY_TWITTER_ACCESSTOKEN_SECRET = "twitter.access.credential";
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
|
||||||
<application>${appengine.applicationid}</application>
|
|
||||||
<version>1</version>
|
|
||||||
<system-properties>
|
|
||||||
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
|
|
||||||
</system-properties>
|
|
||||||
</appengine-web-app>
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<cronentries>
|
|
||||||
<cron>
|
|
||||||
<url>/stores/do</url>
|
|
||||||
<description>Enqueue 'store tweet' tasks for all contexts</description>
|
|
||||||
<schedule>every 10 minutes</schedule>
|
|
||||||
</cron>
|
|
||||||
</cronentries>
|
|
|
@ -1,50 +0,0 @@
|
||||||
#
|
|
||||||
# Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
# contributor license agreements. See the NOTICE file
|
|
||||||
# distributed with this work for additional information
|
|
||||||
# regarding copyright ownership. jclouds 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
# A default java.util.logging configuration.
|
|
||||||
# (All App Engine logging is through java.util.logging by default).
|
|
||||||
#
|
|
||||||
# To use this configuration, copy it into your application's WEB-INF
|
|
||||||
# folder and add the following to your appengine-web.xml:
|
|
||||||
#
|
|
||||||
# <system-properties>
|
|
||||||
# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
|
|
||||||
# </system-properties>
|
|
||||||
#
|
|
||||||
|
|
||||||
# Set the default logging level for all loggers to INFO
|
|
||||||
.level = INFO
|
|
||||||
|
|
||||||
org.jclouds.level=INFO
|
|
||||||
org.jclouds.demo.tweetstore.config.level=FINEST
|
|
||||||
|
|
||||||
# Set the default logging level for ORM, specifically, to WARNING
|
|
||||||
DataNucleus.JDO.level=WARNING
|
|
||||||
DataNucleus.Persistence.level=WARNING
|
|
||||||
DataNucleus.Cache.level=WARNING
|
|
||||||
DataNucleus.MetaData.level=WARNING
|
|
||||||
DataNucleus.General.level=WARNING
|
|
||||||
DataNucleus.Utility.level=WARNING
|
|
||||||
DataNucleus.Transaction.level=WARNING
|
|
||||||
DataNucleus.Datastore.level=WARNING
|
|
||||||
DataNucleus.ClassLoading.level=WARNING
|
|
||||||
DataNucleus.Plugin.level=WARNING
|
|
||||||
DataNucleus.ValueGeneration.level=WARNING
|
|
||||||
DataNucleus.Enhancer.level=WARNING
|
|
||||||
DataNucleus.SchemaTool.level=WARNING
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
|
|
||||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
|
||||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
|
|
||||||
|
|
||||||
<!-- the usual <context:annotation-config/> can't be used because the
|
|
||||||
CommonAnnotationBeanPostProcessor causes a security exception in GAE when it
|
|
||||||
tries to load javax.annotation.Resource -->
|
|
||||||
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
|
|
||||||
<bean class="org.springframework.context.annotation.ConfigurationClassPostProcessor" />
|
|
||||||
<bean class="org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor">
|
|
||||||
<property name="initAnnotationType" value="javax.annotation.PostConstruct" />
|
|
||||||
<property name="destroyAnnotationType" value="javax.annotation.PreDestroy" />
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean class="org.jclouds.demo.tweetstore.config.SpringServletConfig" />
|
|
||||||
</beans>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<queue-entries>
|
|
||||||
<queue>
|
|
||||||
<name>twitter</name>
|
|
||||||
<!-- poll every 30s, but only allow one request at a time to save CPU -->
|
|
||||||
<rate>2/m</rate>
|
|
||||||
<max-concurrent-requests>1</max-concurrent-requests>
|
|
||||||
</queue>
|
|
||||||
</queue-entries>
|
|
|
@ -1,66 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
|
|
||||||
version="2.5">
|
|
||||||
<display-name>jclouds-tweetstore-spring</display-name>
|
|
||||||
|
|
||||||
<!-- Servlets -->
|
|
||||||
<servlet>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
|
||||||
<!-- cannot use the AnnotationConfigWebApplicationContext as it causes a security
|
|
||||||
exception in GAE by trying to load javax.annotation.Resource -->
|
|
||||||
</servlet>
|
|
||||||
|
|
||||||
<!-- should be kept in sync with the mappings defined in SpringServletConfig -->
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<url-pattern>/store/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<url-pattern>/tweets/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<url-pattern>/stores/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>dispatcher</servlet-name>
|
|
||||||
<url-pattern>/clear/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<!-- limit submission of storage tasks to the cron job -->
|
|
||||||
<security-constraint>
|
|
||||||
<web-resource-collection>
|
|
||||||
<url-pattern>/stores/*</url-pattern>
|
|
||||||
</web-resource-collection>
|
|
||||||
<auth-constraint>
|
|
||||||
<role-name>admin</role-name>
|
|
||||||
</auth-constraint>
|
|
||||||
</security-constraint>
|
|
||||||
|
|
||||||
<welcome-file-list>
|
|
||||||
<welcome-file>index.jsp</welcome-file>
|
|
||||||
</welcome-file-list>
|
|
||||||
</web-app>
|
|
|
@ -1,31 +0,0 @@
|
||||||
<%--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
--%>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>jclouds: anyweight cloudware for java</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Welcome!</h2>
|
|
||||||
<p>Click <a href="/tweets/get">here</a> to see tweets about jclouds.</p>
|
|
||||||
<p><img src="http://code.google.com/appengine/images/appengine-noborder-120x30.gif"
|
|
||||||
alt="Powered by Google App Engine" /></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,109 +0,0 @@
|
||||||
<%--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
--%>
|
|
||||||
<%@ page buffer="20kb"%>
|
|
||||||
<%@ taglib uri="http://displaytag.sf.net" prefix="display"%>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>jclouds: anyweight cloudware for java</title>
|
|
||||||
<style type="text/css">
|
|
||||||
<!--
|
|
||||||
table.staticheader {
|
|
||||||
text-decoration: none;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
width: 98%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader th {
|
|
||||||
padding: 3px 3px 3px 3px !important;
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader td {
|
|
||||||
padding: 3px 3px 3px 3px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader thead tr {
|
|
||||||
position: relative;
|
|
||||||
height: 10px;
|
|
||||||
background-color: #D7E5F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody {
|
|
||||||
height:800px;
|
|
||||||
overflow-x:hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow:scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr {
|
|
||||||
height: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr.odd {
|
|
||||||
background-color: #eee
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr.tableRowEven,tr.even {
|
|
||||||
background-color: #ddd
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr td:last-child {
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody td {
|
|
||||||
padding: 2px 4px 2px 4px !important;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
div.TableContainer {
|
|
||||||
height: 800px;
|
|
||||||
overflow-x:hidden;
|
|
||||||
overflow-y:auto;
|
|
||||||
}
|
|
||||||
-->
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Tweets in Clouds</h2>
|
|
||||||
<table width="100%" border="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="TableContainer">
|
|
||||||
<display:table name="tweets" defaultsort="1" cellpadding="5" cellspacing="1" class="staticheader">
|
|
||||||
<display:column property="id" title="Tweet ID" />
|
|
||||||
<display:column property="from" title="Who Said it" />
|
|
||||||
<display:column property="tweet" title="Tweet" />
|
|
||||||
<display:column property="service" title="Cloud" />
|
|
||||||
<display:column property="host" title="Host" />
|
|
||||||
<display:column property="status" title="Status" />
|
|
||||||
</display:table>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="http://code.google.com/appengine/images/appengine-noborder-120x30.gif"
|
|
||||||
alt="Powered by Google App Engine" /></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,82 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config.util;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
import static org.testng.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector;
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector.Credential;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code CredentialsCollector}
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class CredentialsCollectorTest {
|
|
||||||
private CredentialsCollector collector = new CredentialsCollector();
|
|
||||||
|
|
||||||
public void testEmptyProperties() {
|
|
||||||
assertTrue(collector.apply(new Properties()).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNoCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("not-an-identity",
|
|
||||||
"v1", "not-a-credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Properties propertiesOf(Map<String, String> entries) {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
properties.putAll(entries);
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNonMatchingCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("non_matching.identity", "v1",
|
|
||||||
"non_matching.credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testIncompleteCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("acme.identity", "v1",
|
|
||||||
"acme-2.credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("acme.identity", "v1",
|
|
||||||
"acme.credential", "v2", "acme-2.identity", "v3",
|
|
||||||
"acme-2.credential", "v4"));
|
|
||||||
assertEquals(collector.apply(properties),
|
|
||||||
ImmutableMap.of("acme", new Credential("v1", "v2"),
|
|
||||||
"acme-2", new Credential("v3", "v4")));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
import org.testng.collections.Maps;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code AddTweetsController}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class AddTweetsControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createServices(String container) throws InterruptedException,
|
|
||||||
ExecutionException {
|
|
||||||
Map<String, BlobStoreContext> services = Maps.newHashMap();
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
for (String name : new String[] { "1", "2" }) {
|
|
||||||
BlobStoreContext context = ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class);
|
|
||||||
context.getAsyncBlobStore().createContainerInLocation(null, container).get();
|
|
||||||
Blob blob = context.getAsyncBlobStore().blobBuilder("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
context.getAsyncBlobStore().putBlob(container, blob).get();
|
|
||||||
services.put(name, context);
|
|
||||||
}
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String container = "container";
|
|
||||||
Map<String, BlobStoreContext> contexts = createServices(container);
|
|
||||||
|
|
||||||
ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container);
|
|
||||||
AddTweetsController controller = new AddTweetsController(contexts, function);
|
|
||||||
List<StoredTweetStatus> list = controller.apply(ImmutableSet.of("1", "2"));
|
|
||||||
assertEquals(list.size(), 2);
|
|
||||||
assertEquals(list, ImmutableList.of(new StoredTweetStatus("1", "localhost", container, "1",
|
|
||||||
"frank", "I love beans!", null), new StoredTweetStatus("2", "localhost", container,
|
|
||||||
"1", "frank", "I love beans!", null)));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code AddTweetsController}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class ClearTweetsControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createBlobStores(String container) throws InterruptedException, ExecutionException {
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
Map<String, BlobStoreContext> contexts = ImmutableMap.<String, BlobStoreContext>of(
|
|
||||||
"test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class),
|
|
||||||
"test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class));
|
|
||||||
for (BlobStoreContext blobstore : contexts.values()) {
|
|
||||||
blobstore.getBlobStore().createContainerInLocation(null, container);
|
|
||||||
Blob blob = blobstore.getAsyncBlobStore().blobBuilder("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
blobstore.getBlobStore().putBlob(container, blob);
|
|
||||||
}
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testClearTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String container = ClearTweetsControllerTest.class.getName() + "#container";
|
|
||||||
Map<String, BlobStoreContext> contexts = createBlobStores(container);
|
|
||||||
|
|
||||||
ClearTweetsController controller = new ClearTweetsController(contexts,
|
|
||||||
container);
|
|
||||||
controller.clearContainer("test1");
|
|
||||||
controller.clearContainer("test2");
|
|
||||||
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
assertEquals(context.getBlobStore().countBlobs(container), 0, context.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
|
|
||||||
import static org.easymock.EasyMock.createMock;
|
|
||||||
import static org.easymock.EasyMock.expect;
|
|
||||||
import static org.easymock.EasyMock.replay;
|
|
||||||
import static org.easymock.EasyMock.verify;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.appengine.api.taskqueue.Queue;
|
|
||||||
import com.google.appengine.api.taskqueue.TaskOptions.Method;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@link EnqueueStoresController}
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class EnqueueStoresControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createBlobStores() {
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
Map<String, BlobStoreContext> contexts = ImmutableMap.<String, BlobStoreContext>of(
|
|
||||||
"test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class),
|
|
||||||
"test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class));
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEnqueueStores() {
|
|
||||||
Map<String, BlobStoreContext> stores = createBlobStores();
|
|
||||||
Queue taskQueue = createMock(Queue.class);
|
|
||||||
EnqueueStoresController function = new EnqueueStoresController(stores, taskQueue);
|
|
||||||
|
|
||||||
expect(taskQueue.add(withUrl("/store/do").header("context", "test1").method(Method.GET))).andReturn(null);
|
|
||||||
expect(taskQueue.add(withUrl("/store/do").header("context", "test2").method(Method.GET))).andReturn(null);
|
|
||||||
replay(taskQueue);
|
|
||||||
|
|
||||||
function.enqueueStoreTweetTasks();
|
|
||||||
|
|
||||||
verify(taskQueue);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.easymock.EasyMock.createMock;
|
|
||||||
import static org.easymock.EasyMock.expect;
|
|
||||||
import static org.easymock.EasyMock.replay;
|
|
||||||
import static org.easymock.EasyMock.verify;
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import twitter4j.Status;
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
import twitter4j.User;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code StoreTweetsController}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class StoreTweetsControllerTest {
|
|
||||||
|
|
||||||
Twitter createTwitter() {
|
|
||||||
return createMock(Twitter.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createBlobStores() throws InterruptedException, ExecutionException {
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
Map<String, BlobStoreContext> contexts = ImmutableMap.<String, BlobStoreContext>of(
|
|
||||||
"test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class),
|
|
||||||
"test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class));
|
|
||||||
for (BlobStoreContext blobstore : contexts.values()) {
|
|
||||||
blobstore.getAsyncBlobStore().createContainerInLocation(null, "favo").get();
|
|
||||||
}
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
Map<String, BlobStoreContext> stores = createBlobStores();
|
|
||||||
StoreTweetsController function = new StoreTweetsController(stores, "favo", createTwitter());
|
|
||||||
|
|
||||||
User frank = createMock(User.class);
|
|
||||||
expect(frank.getScreenName()).andReturn("frank").atLeastOnce();
|
|
||||||
|
|
||||||
Status frankStatus = createMock(Status.class);
|
|
||||||
expect(frankStatus.getId()).andReturn(1l).atLeastOnce();
|
|
||||||
expect(frankStatus.getUser()).andReturn(frank).atLeastOnce();
|
|
||||||
expect(frankStatus.getText()).andReturn("I love beans!").atLeastOnce();
|
|
||||||
|
|
||||||
User jimmy = createMock(User.class);
|
|
||||||
expect(jimmy.getScreenName()).andReturn("jimmy").atLeastOnce();
|
|
||||||
|
|
||||||
Status jimmyStatus = createMock(Status.class);
|
|
||||||
expect(jimmyStatus.getId()).andReturn(2l).atLeastOnce();
|
|
||||||
expect(jimmyStatus.getUser()).andReturn(jimmy).atLeastOnce();
|
|
||||||
expect(jimmyStatus.getText()).andReturn("cloud is king").atLeastOnce();
|
|
||||||
|
|
||||||
replay(frank);
|
|
||||||
replay(frankStatus);
|
|
||||||
replay(jimmy);
|
|
||||||
replay(jimmyStatus);
|
|
||||||
|
|
||||||
function.addMyTweets("test1", ImmutableList.of(frankStatus, jimmyStatus));
|
|
||||||
function.addMyTweets("test2", ImmutableList.of(frankStatus, jimmyStatus));
|
|
||||||
|
|
||||||
verify(frank);
|
|
||||||
verify(frankStatus);
|
|
||||||
verify(jimmy);
|
|
||||||
verify(jimmyStatus);
|
|
||||||
|
|
||||||
for (Entry<String, BlobStoreContext> entry : stores.entrySet()) {
|
|
||||||
BlobMap map = entry.getValue().createBlobMap("favo");
|
|
||||||
Blob frankBlob = map.get("1");
|
|
||||||
assertEquals(frankBlob.getMetadata().getName(), "1");
|
|
||||||
assertEquals(frankBlob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME), "frank");
|
|
||||||
assertEquals(frankBlob.getMetadata().getContentMetadata().getContentType(), "text/plain");
|
|
||||||
assertEquals(Strings2.toString(frankBlob.getPayload()), "I love beans!");
|
|
||||||
|
|
||||||
Blob jimmyBlob = map.get("2");
|
|
||||||
assertEquals(jimmyBlob.getMetadata().getName(), "2");
|
|
||||||
assertEquals(jimmyBlob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME), "jimmy");
|
|
||||||
assertEquals(jimmyBlob.getMetadata().getContentMetadata().getContentType(), "text/plain");
|
|
||||||
assertEquals(Strings2.toString(jimmyBlob.getPayload()), "cloud is king");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code KeyToStoredTweetStatus}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class KeyToStoredTweetStatusTest {
|
|
||||||
|
|
||||||
BlobMap createMap() throws InterruptedException, ExecutionException {
|
|
||||||
BlobStoreContext context =
|
|
||||||
ContextBuilder.newBuilder(TransientApiMetadata.builder().build()).build(BlobStoreContext.class);
|
|
||||||
context.getBlobStore().createContainerInLocation(null, "test1");
|
|
||||||
return context.createBlobMap("test1");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
BlobMap map = createMap();
|
|
||||||
Blob blob = map.blobBuilder().name("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
map.put("1", blob);
|
|
||||||
String host = "localhost";
|
|
||||||
String service = "stub";
|
|
||||||
String container = "tweetstore";
|
|
||||||
|
|
||||||
KeyToStoredTweetStatus function = new KeyToStoredTweetStatus(map, service, host, container);
|
|
||||||
StoredTweetStatus result = function.apply("1");
|
|
||||||
|
|
||||||
StoredTweetStatus expected = new StoredTweetStatus(service, host, container, "1", "frank",
|
|
||||||
"I love beans!", null);
|
|
||||||
|
|
||||||
assertEquals(result, expected);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
import org.testng.collections.Maps;
|
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code ServiceToStoredTweetStatuses}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class ServiceToStoredTweetStatusesTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createServices(String container) throws InterruptedException,
|
|
||||||
ExecutionException {
|
|
||||||
Map<String, BlobStoreContext> services = Maps.newHashMap();
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
for (String name : new String[] { "1", "2" }) {
|
|
||||||
BlobStoreContext context = ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class);
|
|
||||||
context.getAsyncBlobStore().createContainerInLocation(null, container).get();
|
|
||||||
Blob blob = context.getAsyncBlobStore().blobBuilder("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
context.getAsyncBlobStore().putBlob(container, blob).get();
|
|
||||||
services.put(name, context);
|
|
||||||
}
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String container = "container";
|
|
||||||
Map<String, BlobStoreContext> contexts = createServices(container);
|
|
||||||
|
|
||||||
ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container);
|
|
||||||
|
|
||||||
assertEquals(Iterables.getLast(function.apply("1")), new StoredTweetStatus("1", "localhost",
|
|
||||||
container, "1", "frank", "I love beans!", null));
|
|
||||||
|
|
||||||
assertEquals(Iterables.getLast(function.apply("2")), new StoredTweetStatus("2", "localhost",
|
|
||||||
container, "1", "frank", "I love beans!", null));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.integration;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.io.Closeables.closeQuietly;
|
|
||||||
import static java.lang.String.format;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import com.google.appengine.tools.KickStart;
|
|
||||||
import com.google.appengine.tools.info.SdkInfo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic functionality to start a local google app engine instance.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class GoogleDevServer {
|
|
||||||
|
|
||||||
Thread server;
|
|
||||||
|
|
||||||
public void writePropertiesAndStartServer(final String address,
|
|
||||||
final String port, final String warfile, Properties props)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
String filename = String.format(
|
|
||||||
"%1$s/WEB-INF/jclouds.properties", warfile);
|
|
||||||
System.err.println("file: " + filename);
|
|
||||||
storeProperties(filename, props);
|
|
||||||
assert new File(filename).exists();
|
|
||||||
this.server = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String sdkRoot = checkNotNull(System.getProperty(SdkInfo.SDK_ROOT_PROPERTY), SdkInfo.SDK_ROOT_PROPERTY);
|
|
||||||
KickStart.main(new String[] {
|
|
||||||
KickStarter.systemProperty("java.util.logging.config.file",
|
|
||||||
format("%s/WEB-INF/logging.properties", warfile)),
|
|
||||||
KickStarter.systemProperty(SdkInfo.SDK_ROOT_PROPERTY, sdkRoot),
|
|
||||||
"com.google.appengine.tools.development.DevAppServerMain",
|
|
||||||
"--disable_update_check",
|
|
||||||
format("--sdk_root=%s", sdkRoot),
|
|
||||||
"-a", address, "-p", port, warfile });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
server.start();
|
|
||||||
TimeUnit.SECONDS.sleep(30);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void storeProperties(String filename, Properties props) throws IOException {
|
|
||||||
FileOutputStream targetFile = new FileOutputStream(filename);
|
|
||||||
try {
|
|
||||||
props.store(targetFile, "test");
|
|
||||||
} finally {
|
|
||||||
closeQuietly(targetFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() throws Exception {
|
|
||||||
// KickStart.main opens a process and calls process.waitFor(), which is interruptable
|
|
||||||
server.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class KickStarter {
|
|
||||||
private static String systemProperty(String key, String value) {
|
|
||||||
return format("--jvm_flag=-D%s=%s", key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.integration;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_BLOBSTORES;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN_SECRET;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_KEY;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_SECRET;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.jclouds.Context;
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.config.SpringServletConfig;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.StoreTweetsController;
|
|
||||||
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
|
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
import org.testng.annotations.AfterTest;
|
|
||||||
import org.testng.annotations.BeforeTest;
|
|
||||||
import org.testng.annotations.Parameters;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import twitter4j.ResponseList;
|
|
||||||
import twitter4j.Status;
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
import twitter4j.TwitterException;
|
|
||||||
import twitter4j.TwitterFactory;
|
|
||||||
import twitter4j.conf.Configuration;
|
|
||||||
import twitter4j.conf.ConfigurationBuilder;
|
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.inject.Module;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts up the Google App Engine for Java Development environment and deploys an application which
|
|
||||||
* tests accesses twitter and blobstores.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "live", singleThreaded = true)
|
|
||||||
public class TweetStoreLiveTest {
|
|
||||||
|
|
||||||
GoogleDevServer server;
|
|
||||||
private URL url;
|
|
||||||
private Map<String, BlobStoreContext> contexts;
|
|
||||||
private String container;
|
|
||||||
|
|
||||||
private static final Iterable<String> blobstores =
|
|
||||||
Splitter.on(',').split(System.getProperty(PROPERTY_TWEETSTORE_BLOBSTORES,
|
|
||||||
"cloudfiles-us,aws-s3,azureblob"));
|
|
||||||
private static final Properties props = new Properties();
|
|
||||||
|
|
||||||
@BeforeTest
|
|
||||||
void clearAndCreateContainers() throws InterruptedException, ExecutionException, TimeoutException, IOException,
|
|
||||||
TwitterException {
|
|
||||||
container = getRequiredSystemProperty(PROPERTY_TWEETSTORE_CONTAINER);
|
|
||||||
|
|
||||||
props.setProperty(PROPERTY_TWEETSTORE_CONTAINER, container);
|
|
||||||
props.setProperty(SpringServletConfig.PROPERTY_BLOBSTORE_CONTEXTS, Joiner.on(',').join(blobstores));
|
|
||||||
|
|
||||||
// put all identity/credential pairs into the client
|
|
||||||
addCredentialsForBlobStores(props);
|
|
||||||
|
|
||||||
// example of an ad-hoc client configuration
|
|
||||||
addConfigurationForTwitter(props);
|
|
||||||
|
|
||||||
// for testing, capture logs.
|
|
||||||
final Set<Module> wiring = ImmutableSet.<Module> of(new Log4JLoggingModule());
|
|
||||||
this.contexts = Maps.newConcurrentMap();
|
|
||||||
|
|
||||||
for (String provider : blobstores) {
|
|
||||||
contexts.put(provider, ContextBuilder.newBuilder(provider)
|
|
||||||
.modules(wiring).overrides(props).build(BlobStoreContext.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
Configuration conf = new ConfigurationBuilder()
|
|
||||||
.setOAuthConsumerKey(props.getProperty(PROPERTY_TWITTER_CONSUMER_KEY))
|
|
||||||
.setOAuthConsumerSecret(props.getProperty(PROPERTY_TWITTER_CONSUMER_SECRET))
|
|
||||||
.setOAuthAccessToken(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN))
|
|
||||||
.setOAuthAccessTokenSecret(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET))
|
|
||||||
.build();
|
|
||||||
Twitter client = new TwitterFactory(conf).getInstance();
|
|
||||||
StoreTweetsController controller = new StoreTweetsController(contexts, container, client);
|
|
||||||
|
|
||||||
ResponseList<Status> statuses = client.getMentions();
|
|
||||||
|
|
||||||
boolean deleted = false;
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
if (context.getBlobStore().containerExists(container)) {
|
|
||||||
System.err.printf("deleting container %s at %s%n", container,
|
|
||||||
context.unwrap(Context.class).getProviderMetadata().getEndpoint());
|
|
||||||
context.getBlobStore().deleteContainer(container);
|
|
||||||
deleted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (deleted) {
|
|
||||||
System.err.println("sleeping 60 seconds to allow containers to clear");
|
|
||||||
Thread.sleep(60000);
|
|
||||||
}
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
System.err.printf("creating container %s at %s%n", container,
|
|
||||||
context.unwrap(Context.class).getProviderMetadata().getEndpoint());
|
|
||||||
context.getBlobStore().createContainerInLocation(null, container);
|
|
||||||
}
|
|
||||||
if (deleted) {
|
|
||||||
System.err.println("sleeping 5 seconds to allow containers to create");
|
|
||||||
Thread.sleep(5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Entry<String, BlobStoreContext> entry : contexts.entrySet()) {
|
|
||||||
System.err.printf("filling container %s at %s%n", container, entry.getKey());
|
|
||||||
controller.addMyTweets(entry.getKey(), statuses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getRequiredSystemProperty(String key) {
|
|
||||||
return checkNotNull(System.getProperty(key), key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addConfigurationForTwitter(Properties props) {
|
|
||||||
props.setProperty(PROPERTY_TWITTER_CONSUMER_KEY,
|
|
||||||
getRequiredSystemProperty("test." + PROPERTY_TWITTER_CONSUMER_KEY));
|
|
||||||
props.setProperty(PROPERTY_TWITTER_CONSUMER_SECRET,
|
|
||||||
getRequiredSystemProperty("test." + PROPERTY_TWITTER_CONSUMER_SECRET));
|
|
||||||
props.setProperty(PROPERTY_TWITTER_ACCESSTOKEN,
|
|
||||||
getRequiredSystemProperty("test." + PROPERTY_TWITTER_ACCESSTOKEN));
|
|
||||||
props.setProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET,
|
|
||||||
getRequiredSystemProperty("test." + PROPERTY_TWITTER_ACCESSTOKEN_SECRET));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addCredentialsForBlobStores(Properties props) {
|
|
||||||
for (String provider : blobstores) {
|
|
||||||
props.setProperty(provider + ".identity",
|
|
||||||
getRequiredSystemProperty("test." + provider + ".identity"));
|
|
||||||
props.setProperty(provider + ".credential",
|
|
||||||
getRequiredSystemProperty("test." + provider + ".credential"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeTest
|
|
||||||
@Parameters({ "warfile", "devappserver.address", "devappserver.port" })
|
|
||||||
public void startDevAppServer(final String warfile, final String address, final String port) throws Exception {
|
|
||||||
url = new URL(String.format("http://%s:%s", address, port));
|
|
||||||
server = new GoogleDevServer();
|
|
||||||
server.writePropertiesAndStartServer(address, port, warfile, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void shouldPass() throws InterruptedException, IOException {
|
|
||||||
InputStream i = url.openStream();
|
|
||||||
String string = Strings2.toStringAndClose(i);
|
|
||||||
assert string.indexOf("Welcome") >= 0 : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(dependsOnMethods = "shouldPass", expectedExceptions = IOException.class)
|
|
||||||
public void shouldFail() throws InterruptedException, IOException {
|
|
||||||
new URL(url, "/store/do").openStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(dependsOnMethods = "shouldFail")
|
|
||||||
public void testPrimeContainers() throws IOException, InterruptedException {
|
|
||||||
URL gurl = new URL(url, "/store/do");
|
|
||||||
// WATCH THIS, you need to add a context each time
|
|
||||||
for (String context : blobstores) {
|
|
||||||
System.out.println("storing at context: " + context);
|
|
||||||
HttpURLConnection connection = (HttpURLConnection) gurl.openConnection();
|
|
||||||
connection.addRequestProperty("X-AppEngine-QueueName", "twitter");
|
|
||||||
connection.addRequestProperty("context", context);
|
|
||||||
InputStream i = connection.getInputStream();
|
|
||||||
String string = Strings2.toStringAndClose(i);
|
|
||||||
assert string.indexOf("Done!") >= 0 : string;
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
System.err.println("sleeping 20 seconds to allow for eventual consistency delay");
|
|
||||||
Thread.sleep(20000);
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
assert context.createInputStreamMap(container).size() > 0 : context.unwrap(Context.class).getProviderMetadata().getEndpoint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(invocationCount = 5, dependsOnMethods = "testPrimeContainers")
|
|
||||||
public void testSerial() throws InterruptedException, IOException {
|
|
||||||
URL gurl = new URL(url, "/tweets/get");
|
|
||||||
InputStream i = gurl.openStream();
|
|
||||||
String string = Strings2.toStringAndClose(i);
|
|
||||||
assert string.indexOf("Tweets in Clouds") >= 0 : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(invocationCount = 10, dependsOnMethods = "testPrimeContainers", threadPoolSize = 3)
|
|
||||||
public void testParallel() throws InterruptedException, IOException {
|
|
||||||
URL gurl = new URL(url, "/tweets/get");
|
|
||||||
InputStream i = gurl.openStream();
|
|
||||||
String string = Strings2.toStringAndClose(i);
|
|
||||||
assert string.indexOf("Tweets in Clouds") >= 0 : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterTest
|
|
||||||
public void stopDevAppServer() throws Exception {
|
|
||||||
server.stop();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,95 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
|
||||||
<!--
|
|
||||||
For more configuration infromation and examples see the Apache
|
|
||||||
Log4j website: http://logging.apache.org/log4j/
|
|
||||||
-->
|
|
||||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
|
|
||||||
debug="false">
|
|
||||||
|
|
||||||
<!-- A time/date based rolling appender -->
|
|
||||||
<appender name="WIREFILE" class="org.apache.log4j.DailyRollingFileAppender">
|
|
||||||
<param name="File" value="target/test-data/jclouds-wire.log" />
|
|
||||||
<param name="Append" value="true" />
|
|
||||||
|
|
||||||
<!-- Rollover at midnight each day -->
|
|
||||||
<param name="DatePattern" value="'.'yyyy-MM-dd" />
|
|
||||||
|
|
||||||
<param name="Threshold" value="TRACE" />
|
|
||||||
|
|
||||||
<layout class="org.apache.log4j.PatternLayout">
|
|
||||||
<!-- The default pattern: Date Priority [Category] Message\n -->
|
|
||||||
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The full pattern: Date MS Priority [Category]
|
|
||||||
(Thread:NDC) Message\n <param name="ConversionPattern"
|
|
||||||
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
|
|
||||||
-->
|
|
||||||
</layout>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- A time/date based rolling appender -->
|
|
||||||
<appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender">
|
|
||||||
<param name="File" value="target/test-data/jclouds.log" />
|
|
||||||
<param name="Append" value="true" />
|
|
||||||
|
|
||||||
<!-- Rollover at midnight each day -->
|
|
||||||
<param name="DatePattern" value="'.'yyyy-MM-dd" />
|
|
||||||
|
|
||||||
<param name="Threshold" value="TRACE" />
|
|
||||||
|
|
||||||
<layout class="org.apache.log4j.PatternLayout">
|
|
||||||
<!-- The default pattern: Date Priority [Category] Message\n -->
|
|
||||||
<param name="ConversionPattern" value="%d %-5p [%c] (%t) %m%n" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The full pattern: Date MS Priority [Category]
|
|
||||||
(Thread:NDC) Message\n <param name="ConversionPattern"
|
|
||||||
value="%d %-5r %-5p [%c] (%t:%x) %m%n"/>
|
|
||||||
-->
|
|
||||||
</layout>
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
|
|
||||||
<appender-ref ref="FILE" />
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<appender name="ASYNCWIRE" class="org.apache.log4j.AsyncAppender">
|
|
||||||
<appender-ref ref="WIREFILE" />
|
|
||||||
</appender>
|
|
||||||
|
|
||||||
<!-- ================ -->
|
|
||||||
<!-- Limit categories -->
|
|
||||||
<!-- ================ -->
|
|
||||||
|
|
||||||
<category name="org.jclouds">
|
|
||||||
<priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNC" />
|
|
||||||
</category>
|
|
||||||
|
|
||||||
<category name="jclouds.headers">
|
|
||||||
<priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNCWIRE" />
|
|
||||||
</category>
|
|
||||||
<category name="jclouds.wire">
|
|
||||||
<priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNCWIRE" />
|
|
||||||
</category>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
<category name="jclouds.wire"> <priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNCWIRE" /> </category> <category
|
|
||||||
name="jclouds.signature"> <priority value="DEBUG" />
|
|
||||||
<appender-ref ref="ASYNCWIRE" /> </category>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- ======================= -->
|
|
||||||
<!-- Setup the Root category -->
|
|
||||||
<!-- ======================= -->
|
|
||||||
|
|
||||||
<root>
|
|
||||||
<priority value="WARN" />
|
|
||||||
</root>
|
|
||||||
|
|
||||||
</log4j:configuration>
|
|
|
@ -1,56 +0,0 @@
|
||||||
====
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
====
|
|
||||||
|
|
||||||
A guide to generating Twitter consumer keys and access tokens is at http://tinyurl.com/2fhebgb
|
|
||||||
|
|
||||||
This sample uses the Google App Engine for Java SDK located at
|
|
||||||
http://code.google.com/p/googleappengine/downloads/list
|
|
||||||
|
|
||||||
Please unzip the above file and modify your maven settings.xml like below before
|
|
||||||
attempting to run 'mvn -Plive install'
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<id>appengine</id>
|
|
||||||
<activation>
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<appengine.sdk.root>/path/to/appengine-java-sdk-1.4.2</appengine.home>
|
|
||||||
<appengine.applicationid>yourappid</appengine.applicationid>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<id>keys</id>
|
|
||||||
<activation>
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<test.aws-s3.identity>YOUR_ACCESS_KEY_ID</test.aws-s3.identity>
|
|
||||||
<test.aws-s3.credential>YOUR_SECRET_KEY</test.aws-s3.credential>
|
|
||||||
<test.cloudfiles-us.identity>YOUR_USER</test.cloudfiles-us.identity>
|
|
||||||
<test.cloudfiles-us.credential>YOUR_HEX_KEY</test.cloudfiles-us.credential>
|
|
||||||
<test.azureblob.identity>YOUR_ACCOUNT</test.azureblob.identity>
|
|
||||||
<test.azureblob.credential>YOUR_BASE64_ENCODED_KEY</test.azureblob.credential>
|
|
||||||
<test.twitter.gae-tweetstore.consumer.identity>YOUR_TWITTER_CONSUMER_KEY</test.twitter.gae-tweetstore.consumer.identity>
|
|
||||||
<test.twitter.gae-tweetstore.consumer.credential>YOUR_TWITTER_CONSUMER_SECRET</test.twitter.gae-tweetstore.consumer.credential>
|
|
||||||
<test.twitter.gae-tweetstore.access.identity>YOUR_TWITTER_ACCESSTOKEN</test.twitter.gae-tweetstore.access.identity>
|
|
||||||
<test.twitter.gae-tweetstore.access.credential>YOUR_TWITTER_ACCESSTOKEN_SECRET</test.twitter.gae-tweetstore.access.credential>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
|
@ -1,138 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.jclouds</groupId>
|
|
||||||
<artifactId>jclouds-demos-tweetstore-project</artifactId>
|
|
||||||
<version>1.6.0-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>jclouds-demo-gae-tweetstore</artifactId>
|
|
||||||
<packaging>war</packaging>
|
|
||||||
<name>jclouds TweetStore for Google App Engine</name>
|
|
||||||
<description>jclouds TweetStore for Google App Engine using Guice for Dependency Injection</description>
|
|
||||||
|
|
||||||
<properties>
|
|
||||||
<!--
|
|
||||||
note you must set the property ${appengine.sdk.root} to a valid
|
|
||||||
extraction of appengine-java-sdk
|
|
||||||
-->
|
|
||||||
<appengine.applicationid>jclouds-tweetstore</appengine.applicationid>
|
|
||||||
<appengine.sdk.version>1.6.1</appengine.sdk.version>
|
|
||||||
<devappserver.address>localhost</devappserver.address>
|
|
||||||
<devappserver.port>8088</devappserver.port>
|
|
||||||
<jclouds.tweetstore.container>jclouds-gae-tweetstore</jclouds.tweetstore.container>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jclouds.driver</groupId>
|
|
||||||
<artifactId>jclouds-gae</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.inject.extensions</groupId>
|
|
||||||
<artifactId>guice-servlet</artifactId>
|
|
||||||
<version>3.0</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Google App Engine API -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.appengine</groupId>
|
|
||||||
<artifactId>appengine-api-1.0-sdk</artifactId>
|
|
||||||
<version>${appengine.sdk.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.appengine</groupId>
|
|
||||||
<artifactId>appengine-tools-sdk</artifactId>
|
|
||||||
<version>${appengine.sdk.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>live</id>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>integration</id>
|
|
||||||
<phase>integration-test</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>test</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<systemPropertyVariables>
|
|
||||||
<test.twitter.consumer.identity>${test.twitter.gae-tweetstore.consumer.identity}</test.twitter.consumer.identity>
|
|
||||||
<test.twitter.consumer.credential>${test.twitter.gae-tweetstore.consumer.credential}</test.twitter.consumer.credential>
|
|
||||||
<test.twitter.access.identity>${test.twitter.gae-tweetstore.access.identity}</test.twitter.access.identity>
|
|
||||||
<test.twitter.access.credential>${test.twitter.gae-tweetstore.access.credential}</test.twitter.access.credential>
|
|
||||||
<test.azureblob.identity>${test.azureblob.identity}</test.azureblob.identity>
|
|
||||||
<test.azureblob.credential>${test.azureblob.credential}</test.azureblob.credential>
|
|
||||||
<test.cloudfiles-us.identity>${test.cloudfiles-us.identity}</test.cloudfiles-us.identity>
|
|
||||||
<test.cloudfiles-us.credential>${test.cloudfiles-us.credential}</test.cloudfiles-us.credential>
|
|
||||||
<test.aws-s3.identity>${test.aws-s3.identity}</test.aws-s3.identity>
|
|
||||||
<test.aws-s3.credential>${test.aws-s3.credential}</test.aws-s3.credential>
|
|
||||||
<appengine.sdk.root>${appengine.sdk.root}</appengine.sdk.root>
|
|
||||||
<test.cloudonestorage.identity>${test.cloudonestorage.identity}</test.cloudonestorage.identity>
|
|
||||||
<test.cloudonestorage.credential>${test.cloudonestorage.credential}</test.cloudonestorage.credential>
|
|
||||||
<test.ninefold-storage.identity>${test.ninefold-storage.identity}</test.ninefold-storage.identity>
|
|
||||||
<test.ninefold-storage.credential>${test.ninefold-storage.credential}</test.ninefold-storage.credential>
|
|
||||||
<devappserver.address>${devappserver.address}</devappserver.address>
|
|
||||||
<devappserver.port>${devappserver.port}</devappserver.port>
|
|
||||||
<jclouds.tweetstore.blobstores>${jclouds.tweetstore.blobstores}</jclouds.tweetstore.blobstores>
|
|
||||||
<jclouds.tweetstore.container>test.${jclouds.tweetstore.container}</jclouds.tweetstore.container>
|
|
||||||
<warfile>${project.build.directory}/${project.artifactId}</warfile>
|
|
||||||
</systemPropertyVariables>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
|
|
||||||
<profile>
|
|
||||||
<id>deploy</id>
|
|
||||||
<properties>
|
|
||||||
<!-- classifier to choose the correct jclouds.properties file -->
|
|
||||||
<tweetstore.instance>gae-tweetstore</tweetstore.instance>
|
|
||||||
</properties>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>net.kindleit</groupId>
|
|
||||||
<artifactId>maven-gae-plugin</artifactId>
|
|
||||||
<version>0.9.2</version>
|
|
||||||
<configuration>
|
|
||||||
<serverId>google-appengine</serverId>
|
|
||||||
<sdkDir>${appengine.sdk.root}</sdkDir>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</project>
|
|
|
@ -1,165 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.base.Predicates.in;
|
|
||||||
import static com.google.common.collect.ImmutableSet.copyOf;
|
|
||||||
import static com.google.common.collect.Sets.filter;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_BLOBSTORES;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_ACCESSTOKEN_SECRET;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_KEY;
|
|
||||||
import static org.jclouds.demo.tweetstore.reference.TwitterConstants.PROPERTY_TWITTER_CONSUMER_SECRET;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContextEvent;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.AddTweetsController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.ClearTweetsController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.EnqueueStoresController;
|
|
||||||
import org.jclouds.demo.tweetstore.controller.StoreTweetsController;
|
|
||||||
import org.jclouds.gae.config.GoogleAppEngineConfigurationModule;
|
|
||||||
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
import twitter4j.TwitterFactory;
|
|
||||||
import twitter4j.conf.Configuration;
|
|
||||||
import twitter4j.conf.ConfigurationBuilder;
|
|
||||||
|
|
||||||
import com.google.appengine.api.taskqueue.Queue;
|
|
||||||
import com.google.appengine.api.taskqueue.QueueFactory;
|
|
||||||
import com.google.appengine.repackaged.com.google.common.base.Splitter;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.io.Closeables;
|
|
||||||
import com.google.inject.Guice;
|
|
||||||
import com.google.inject.Injector;
|
|
||||||
import com.google.inject.Module;
|
|
||||||
import com.google.inject.TypeLiteral;
|
|
||||||
import com.google.inject.name.Names;
|
|
||||||
import com.google.inject.servlet.GuiceServletContextListener;
|
|
||||||
import com.google.inject.servlet.ServletModule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup Logging and create Injector for use in testing S3.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class GuiceServletConfig extends GuiceServletContextListener {
|
|
||||||
public static final String PROPERTY_BLOBSTORE_CONTEXTS = "blobstore.contexts";
|
|
||||||
|
|
||||||
private Map<String, BlobStoreContext> providerTypeToBlobStoreMap;
|
|
||||||
private Twitter twitterClient;
|
|
||||||
private String container;
|
|
||||||
private Queue queue;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextInitialized(ServletContextEvent servletContextEvent) {
|
|
||||||
Properties props = loadJCloudsProperties(servletContextEvent);
|
|
||||||
|
|
||||||
Module googleModule = new GoogleAppEngineConfigurationModule();
|
|
||||||
Set<Module> modules = ImmutableSet.<Module> of(googleModule);
|
|
||||||
// shared across all blobstores and used to retrieve tweets
|
|
||||||
try {
|
|
||||||
Configuration twitterConf = new ConfigurationBuilder()
|
|
||||||
.setOAuthConsumerKey(props.getProperty(PROPERTY_TWITTER_CONSUMER_KEY))
|
|
||||||
.setOAuthConsumerSecret(props.getProperty(PROPERTY_TWITTER_CONSUMER_SECRET))
|
|
||||||
.setOAuthAccessToken(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN))
|
|
||||||
.setOAuthAccessTokenSecret(props.getProperty(PROPERTY_TWITTER_ACCESSTOKEN_SECRET))
|
|
||||||
.build();
|
|
||||||
twitterClient = new TwitterFactory(twitterConf).getInstance();
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new IllegalArgumentException("properties for twitter not configured properly in " + props.toString(), e);
|
|
||||||
}
|
|
||||||
// common namespace for storing tweets
|
|
||||||
container = checkNotNull(props.getProperty(PROPERTY_TWEETSTORE_CONTAINER), PROPERTY_TWEETSTORE_CONTAINER);
|
|
||||||
|
|
||||||
// instantiate and store references to all blobstores by provider name
|
|
||||||
providerTypeToBlobStoreMap = Maps.newHashMap();
|
|
||||||
for (String hint : getBlobstoreContexts(props)) {
|
|
||||||
providerTypeToBlobStoreMap.put(hint, ContextBuilder.newBuilder(hint)
|
|
||||||
.modules(modules).overrides(props).build(BlobStoreContext.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a queue for submitting store tweet requests
|
|
||||||
queue = QueueFactory.getQueue("twitter");
|
|
||||||
|
|
||||||
super.contextInitialized(servletContextEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Iterable<String> getBlobstoreContexts(Properties props) {
|
|
||||||
Set<String> contexts = new CredentialsCollector().apply(props).keySet();
|
|
||||||
String explicitContexts = props.getProperty(PROPERTY_TWEETSTORE_BLOBSTORES);
|
|
||||||
if (explicitContexts != null) {
|
|
||||||
contexts = filter(contexts, in(copyOf(Splitter.on(',').split(explicitContexts))));
|
|
||||||
}
|
|
||||||
checkState(!contexts.isEmpty(), "no credentials available for any requested context");
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Properties loadJCloudsProperties(ServletContextEvent servletContextEvent) {
|
|
||||||
InputStream input = servletContextEvent.getServletContext().getResourceAsStream("/WEB-INF/jclouds.properties");
|
|
||||||
Properties props = new Properties();
|
|
||||||
try {
|
|
||||||
props.load(input);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
Closeables.closeQuietly(input);
|
|
||||||
}
|
|
||||||
return props;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Injector getInjector() {
|
|
||||||
return Guice.createInjector(new ServletModule() {
|
|
||||||
@Override
|
|
||||||
protected void configureServlets() {
|
|
||||||
bind(new TypeLiteral<Map<String, BlobStoreContext>>() {
|
|
||||||
}).toInstance(providerTypeToBlobStoreMap);
|
|
||||||
bind(Twitter.class).toInstance(twitterClient);
|
|
||||||
bind(Queue.class).toInstance(queue);
|
|
||||||
bindConstant().annotatedWith(Names.named(PROPERTY_TWEETSTORE_CONTAINER)).to(container);
|
|
||||||
serve("/store/*").with(StoreTweetsController.class);
|
|
||||||
serve("/tweets/*").with(AddTweetsController.class);
|
|
||||||
serve("/stores/*").with(EnqueueStoresController.class);
|
|
||||||
serve("/clear/*").with(ClearTweetsController.class);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contextDestroyed(ServletContextEvent servletContextEvent) {
|
|
||||||
for (BlobStoreContext context : providerTypeToBlobStoreMap.values()) {
|
|
||||||
context.close();
|
|
||||||
}
|
|
||||||
queue.purge();
|
|
||||||
super.contextDestroyed(servletContextEvent);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,153 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config.util;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
|
||||||
import static com.google.common.base.Predicates.notNull;
|
|
||||||
import static com.google.common.collect.Collections2.filter;
|
|
||||||
import static com.google.common.collect.Collections2.transform;
|
|
||||||
import static com.google.common.collect.ImmutableSet.copyOf;
|
|
||||||
import static com.google.common.collect.Maps.filterValues;
|
|
||||||
import static org.jclouds.util.Maps2.fromKeys;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector.Credential;
|
|
||||||
|
|
||||||
import com.google.common.annotations.GwtIncompatible;
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads provider credentials from a {@link Properties} bag.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class CredentialsCollector implements Function<Properties, Map<String, Credential>> {
|
|
||||||
private static final String IDENTITY_PROPERTY_SUFFIX = ".identity";
|
|
||||||
private static final String CREDENTIAL_PROPERTY_SUFFIX = ".credential";
|
|
||||||
|
|
||||||
// using the identity for provider name extraction
|
|
||||||
private static final Pattern IDENTITY_PROPERTY_PATTERN =
|
|
||||||
Pattern.compile("([a-zA-Z0-9-]+)" + Pattern.quote(IDENTITY_PROPERTY_SUFFIX));
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Credential> apply(final Properties properties) {
|
|
||||||
Collection<String> providerNames = transform(
|
|
||||||
filter(properties.stringPropertyNames(), MatchesPattern.matches(IDENTITY_PROPERTY_PATTERN)),
|
|
||||||
new Function<String, String>() {
|
|
||||||
@Override
|
|
||||||
public String apply(String input) {
|
|
||||||
Matcher matcher = IDENTITY_PROPERTY_PATTERN.matcher(input);
|
|
||||||
// as a side-effect, sets the matching group!
|
|
||||||
checkState(matcher.matches(), "'%s' should match '%s'", input, IDENTITY_PROPERTY_PATTERN);
|
|
||||||
return matcher.group(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
* Providers without a credential property result in null values, which are
|
|
||||||
* removed from the returned map.
|
|
||||||
*/
|
|
||||||
return filterValues(fromKeys(copyOf(providerNames), new Function<String, Credential>() {
|
|
||||||
@Override
|
|
||||||
public Credential apply(String providerName) {
|
|
||||||
String identity = properties.getProperty(providerName + IDENTITY_PROPERTY_SUFFIX);
|
|
||||||
String credential = properties.getProperty(providerName + CREDENTIAL_PROPERTY_SUFFIX);
|
|
||||||
return (((identity != null) && (credential != null))
|
|
||||||
? new Credential(identity, credential)
|
|
||||||
: null);
|
|
||||||
}
|
|
||||||
}), notNull());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Credential {
|
|
||||||
private final String identity;
|
|
||||||
private final String credential;
|
|
||||||
|
|
||||||
public Credential(String identity, String credential) {
|
|
||||||
this.identity = checkNotNull(identity, "identity");
|
|
||||||
this.credential = checkNotNull(credential, "credential");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result
|
|
||||||
+ ((credential == null) ? 0 : credential.hashCode());
|
|
||||||
result = prime * result
|
|
||||||
+ ((identity == null) ? 0 : identity.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
Credential other = (Credential) obj;
|
|
||||||
if (credential == null) {
|
|
||||||
if (other.credential != null)
|
|
||||||
return false;
|
|
||||||
} else if (!credential.equals(other.credential))
|
|
||||||
return false;
|
|
||||||
if (identity == null) {
|
|
||||||
if (other.identity != null)
|
|
||||||
return false;
|
|
||||||
} else if (!identity.equals(other.identity))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIdentity() {
|
|
||||||
return identity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCredential() {
|
|
||||||
return credential;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@GwtIncompatible(value = "java.util.regex.Pattern")
|
|
||||||
private static class MatchesPattern implements Predicate<String> {
|
|
||||||
private final Pattern pattern;
|
|
||||||
|
|
||||||
private MatchesPattern(Pattern pattern) {
|
|
||||||
this.pattern = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean apply(String input) {
|
|
||||||
return pattern.matcher(input).matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static MatchesPattern matches(Pattern pattern) {
|
|
||||||
return new MatchesPattern(pattern);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.RequestDispatcher;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.appengine.repackaged.com.google.common.collect.Lists;
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows an example of how to use @{link BlobStoreContext} injected with Guice.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class AddTweetsController extends HttpServlet implements
|
|
||||||
Function<Set<String>, List<StoredTweetStatus>> {
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 3888348023150822683L;
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final ServiceToStoredTweetStatuses blobStoreContextToContainerResult;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
AddTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
ServiceToStoredTweetStatuses blobStoreContextToContainerResult) {
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.blobStoreContextToContainerResult = blobStoreContextToContainerResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response)
|
|
||||||
throws ServletException, IOException {
|
|
||||||
try {
|
|
||||||
addMyTweetsToRequest(request);
|
|
||||||
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/tweets.jsp");
|
|
||||||
dispatcher.forward(request, response);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error listing containers");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addMyTweetsToRequest(HttpServletRequest request) throws InterruptedException,
|
|
||||||
ExecutionException, TimeoutException {
|
|
||||||
request.setAttribute("tweets", apply(contexts.keySet()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<StoredTweetStatus> apply(Set<String> in) {
|
|
||||||
List<StoredTweetStatus> statuses = Lists.newArrayList();
|
|
||||||
for (Iterable<StoredTweetStatus> list : Iterables.transform(in,
|
|
||||||
blobStoreContextToContainerResult)) {
|
|
||||||
Iterables.addAll(statuses, list);
|
|
||||||
}
|
|
||||||
return statuses;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
import static com.google.common.base.Strings.nullToEmpty;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grab tweets related to me and store them into blobstores
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class ClearTweetsController extends HttpServlet {
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@VisibleForTesting
|
|
||||||
public ClearTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container) {
|
|
||||||
this.container = container;
|
|
||||||
this.contexts = contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void clearContainer(String contextName) {
|
|
||||||
BlobStoreContext context = checkNotNull(contexts.get(contextName),
|
|
||||||
"no context for %s in %s", contextName, contexts.keySet());
|
|
||||||
try {
|
|
||||||
context.getBlobStore().clearContainer(container);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error clearing tweets in %s/%s", container, context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (nullToEmpty(request.getHeader("X-Originator")).equals("admin")) {
|
|
||||||
try {
|
|
||||||
String contextName = checkNotNull(request.getHeader("context"), "missing header context");
|
|
||||||
logger.info("clearing tweets in %s/%s", container, contextName);
|
|
||||||
clearContainer(contextName);
|
|
||||||
logger.debug("done clearing tweets");
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error clearing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
|
|
||||||
import static com.google.appengine.repackaged.com.google.common.base.Strings.nullToEmpty;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.appengine.api.taskqueue.Queue;
|
|
||||||
import com.google.appengine.api.taskqueue.TaskOptions.Method;
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds tasks to retrieve and store tweets in all registered contexts to an async
|
|
||||||
* task queue.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
* @see StoreTweetsController
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class EnqueueStoresController extends HttpServlet {
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Set<String> contextNames;
|
|
||||||
private final Queue taskQueue;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public EnqueueStoresController(Map<String, BlobStoreContext> contexts,
|
|
||||||
Queue taskQueue) {
|
|
||||||
contextNames = contexts.keySet();
|
|
||||||
this.taskQueue = taskQueue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void enqueueStoreTweetTasks() {
|
|
||||||
for (String contextName : contextNames) {
|
|
||||||
logger.debug("enqueuing task to store tweets in blobstore '%s'", contextName);
|
|
||||||
taskQueue.add(withUrl("/store/do").header("context", contextName).method(Method.GET));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (!nullToEmpty(request.getHeader("X-AppEngine-Cron")).equals("true")) {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
enqueueStoreTweetTasks();
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServlet;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import javax.ws.rs.core.MediaType;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.jclouds.rest.AuthorizationException;
|
|
||||||
|
|
||||||
import twitter4j.Status;
|
|
||||||
import twitter4j.Twitter;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grab tweets related to me and store them into blobstores
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class StoreTweetsController extends HttpServlet {
|
|
||||||
|
|
||||||
private static final class StatusToBlob implements Function<Status, Blob> {
|
|
||||||
private final BlobMap map;
|
|
||||||
|
|
||||||
private StatusToBlob(BlobMap map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Blob apply(Status from) {
|
|
||||||
Blob to = map.blobBuilder().name(from.getId() + "").build();
|
|
||||||
to.setPayload(from.getText());
|
|
||||||
to.getPayload().getContentMetadata().setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
to.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, from.getUser().getScreenName());
|
|
||||||
return to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = 7215420527854203714L;
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final Twitter client;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
@VisibleForTesting
|
|
||||||
public StoreTweetsController(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container, Twitter client) {
|
|
||||||
this.container = container;
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.client = client;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void addMyTweets(String contextName, Iterable<Status> responseList) {
|
|
||||||
BlobStoreContext context = checkNotNull(contexts.get(contextName), "no context for " + contextName + " in "
|
|
||||||
+ contexts.keySet());
|
|
||||||
BlobMap map = context.createBlobMap(container);
|
|
||||||
for (Status status : responseList) {
|
|
||||||
Blob blob = null;
|
|
||||||
try {
|
|
||||||
blob = new StatusToBlob(map).apply(status);
|
|
||||||
map.put(status.getId() + "", blob);
|
|
||||||
} catch (AuthorizationException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweet %s (blob[%s]) on map %s/%s", status.getId(), blob, context, container);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
if (request.getHeader("X-AppEngine-QueueName") != null
|
|
||||||
&& request.getHeader("X-AppEngine-QueueName").equals("twitter")) {
|
|
||||||
try {
|
|
||||||
String contextName = checkNotNull(request.getHeader("context"), "missing header context");
|
|
||||||
logger.info("retrieving tweets");
|
|
||||||
addMyTweets(contextName, client.getMentions());
|
|
||||||
logger.debug("done storing tweets");
|
|
||||||
response.setContentType(MediaType.TEXT_PLAIN);
|
|
||||||
response.getWriter().println("Done!");
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error storing tweets");
|
|
||||||
throw new ServletException(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
response.sendError(401);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.domain;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class StoredTweetStatus implements Comparable<StoredTweetStatus>, Serializable {
|
|
||||||
|
|
||||||
/** The serialVersionUID */
|
|
||||||
private static final long serialVersionUID = -3257496189689220018L;
|
|
||||||
private final String service;
|
|
||||||
private final String host;
|
|
||||||
private final String container;
|
|
||||||
private final String id;
|
|
||||||
private final String from;
|
|
||||||
private final String tweet;
|
|
||||||
private final String status;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "StoredTweetStatus [container=" + container + ", from=" + from + ", host=" + host
|
|
||||||
+ ", id=" + id + ", service=" + service + ", status=" + status + ", tweet=" + tweet
|
|
||||||
+ "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredTweetStatus(String service, String host, String container, String id, String from,
|
|
||||||
String tweet, String status) {
|
|
||||||
this.service = service;
|
|
||||||
this.host = host;
|
|
||||||
this.container = container;
|
|
||||||
this.id = id;
|
|
||||||
this.from = from;
|
|
||||||
this.tweet = tweet;
|
|
||||||
this.status = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ((container == null) ? 0 : container.hashCode());
|
|
||||||
result = prime * result + ((from == null) ? 0 : from.hashCode());
|
|
||||||
result = prime * result + ((host == null) ? 0 : host.hashCode());
|
|
||||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
|
||||||
result = prime * result + ((service == null) ? 0 : service.hashCode());
|
|
||||||
result = prime * result + ((tweet == null) ? 0 : tweet.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
StoredTweetStatus other = (StoredTweetStatus) obj;
|
|
||||||
if (container == null) {
|
|
||||||
if (other.container != null)
|
|
||||||
return false;
|
|
||||||
} else if (!container.equals(other.container))
|
|
||||||
return false;
|
|
||||||
if (from == null) {
|
|
||||||
if (other.from != null)
|
|
||||||
return false;
|
|
||||||
} else if (!from.equals(other.from))
|
|
||||||
return false;
|
|
||||||
if (host == null) {
|
|
||||||
if (other.host != null)
|
|
||||||
return false;
|
|
||||||
} else if (!host.equals(other.host))
|
|
||||||
return false;
|
|
||||||
if (id == null) {
|
|
||||||
if (other.id != null)
|
|
||||||
return false;
|
|
||||||
} else if (!id.equals(other.id))
|
|
||||||
return false;
|
|
||||||
if (service == null) {
|
|
||||||
if (other.service != null)
|
|
||||||
return false;
|
|
||||||
} else if (!service.equals(other.service))
|
|
||||||
return false;
|
|
||||||
if (tweet == null) {
|
|
||||||
if (other.tweet != null)
|
|
||||||
return false;
|
|
||||||
} else if (!tweet.equals(other.tweet))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getService() {
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHost() {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContainer() {
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFrom() {
|
|
||||||
return from;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTweet() {
|
|
||||||
return tweet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int compareTo(StoredTweetStatus o) {
|
|
||||||
if (id == null)
|
|
||||||
return -1;
|
|
||||||
return (int) ((this == o) ? 0 : id.compareTo(o.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
import org.jclouds.util.Strings2;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class KeyToStoredTweetStatus implements Function<String, StoredTweetStatus> {
|
|
||||||
private final String host;
|
|
||||||
private final BlobMap map;
|
|
||||||
private final String service;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
KeyToStoredTweetStatus(BlobMap map, String service, String host, String container) {
|
|
||||||
this.host = host;
|
|
||||||
this.map = map;
|
|
||||||
this.service = service;
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredTweetStatus apply(String id) {
|
|
||||||
String status;
|
|
||||||
String from;
|
|
||||||
String tweet;
|
|
||||||
try {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
Blob blob = map.get(id);
|
|
||||||
status = ((System.currentTimeMillis() - start) + "ms");
|
|
||||||
from = blob.getMetadata().getUserMetadata().get(TweetStoreConstants.SENDER_NAME);
|
|
||||||
tweet = Strings2.toString(blob.getPayload());
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error(e, "Error listing container %s//%s/%s", service, container, id);
|
|
||||||
status = (e.getMessage());
|
|
||||||
tweet = "";
|
|
||||||
from = "";
|
|
||||||
}
|
|
||||||
return new StoredTweetStatus(service, host, container, id, from, tweet, status);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.functions;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import org.jclouds.Context;
|
|
||||||
import org.jclouds.blobstore.BlobMap;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.jclouds.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class ServiceToStoredTweetStatuses implements Function<String, Iterable<StoredTweetStatus>> {
|
|
||||||
|
|
||||||
private final Map<String, BlobStoreContext> contexts;
|
|
||||||
private final String container;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ServiceToStoredTweetStatuses(Map<String, BlobStoreContext> contexts,
|
|
||||||
@Named(TweetStoreConstants.PROPERTY_TWEETSTORE_CONTAINER) String container) {
|
|
||||||
this.contexts = contexts;
|
|
||||||
this.container = container;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
protected Logger logger = Logger.NULL;
|
|
||||||
|
|
||||||
public Iterable<StoredTweetStatus> apply(String service) {
|
|
||||||
BlobStoreContext context = contexts.get(service);
|
|
||||||
String host = URI.create(context.unwrap(Context.class).getProviderMetadata()
|
|
||||||
.getEndpoint()).getHost();
|
|
||||||
try {
|
|
||||||
BlobMap blobMap = context.createBlobMap(container);
|
|
||||||
Set<String> blobs = blobMap.keySet();
|
|
||||||
return Iterables.transform(blobs, new KeyToStoredTweetStatus(blobMap, service, host,
|
|
||||||
container));
|
|
||||||
} catch (Exception e) {
|
|
||||||
StoredTweetStatus result = new StoredTweetStatus(service, host, container, null, null,
|
|
||||||
null, e.getMessage());
|
|
||||||
logger.error(e, "Error listing service %s", service);
|
|
||||||
return ImmutableList.of(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.reference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration properties and constants used in TweetStore connections.
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public interface TweetStoreConstants {
|
|
||||||
static final String PROPERTY_TWEETSTORE_BLOBSTORES = "jclouds.tweetstore.blobstores";
|
|
||||||
static final String PROPERTY_TWEETSTORE_CONTAINER = "jclouds.tweetstore.container";
|
|
||||||
/**
|
|
||||||
* Note that this has to conform to restrictions of all blobstores. for example, azure doesn't
|
|
||||||
* support periods.
|
|
||||||
*/
|
|
||||||
static final String SENDER_NAME = "sendername";
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.reference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration properties and constants used in Twitter connections.
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
public interface TwitterConstants {
|
|
||||||
static final String PROPERTY_TWITTER_CONSUMER_KEY = "twitter.consumer.identity";
|
|
||||||
static final String PROPERTY_TWITTER_CONSUMER_SECRET = "twitter.consumer.credential";
|
|
||||||
static final String PROPERTY_TWITTER_ACCESSTOKEN = "twitter.access.identity";
|
|
||||||
static final String PROPERTY_TWITTER_ACCESSTOKEN_SECRET = "twitter.access.credential";
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
|
||||||
<application>${appengine.applicationid}</application>
|
|
||||||
<version>1</version>
|
|
||||||
<system-properties>
|
|
||||||
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
|
|
||||||
</system-properties>
|
|
||||||
</appengine-web-app>
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<cronentries>
|
|
||||||
<cron>
|
|
||||||
<url>/stores/do</url>
|
|
||||||
<description>Enqueue 'store tweet' tasks for all contexts</description>
|
|
||||||
<schedule>every 10 minutes</schedule>
|
|
||||||
</cron>
|
|
||||||
</cronentries>
|
|
|
@ -1,37 +0,0 @@
|
||||||
#
|
|
||||||
# Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
# contributor license agreements. See the NOTICE file
|
|
||||||
# distributed with this work for additional information
|
|
||||||
# regarding copyright ownership. jclouds 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Set the default logging level for all loggers to WARNING
|
|
||||||
.level = INFO
|
|
||||||
|
|
||||||
# Set the default logging level for ORM, specifically, to WARNING
|
|
||||||
org.jclouds.level=INFO
|
|
||||||
DataNucleus.JDO.level=WARNING
|
|
||||||
DataNucleus.Persistence.level=WARNING
|
|
||||||
DataNucleus.Cache.level=WARNING
|
|
||||||
DataNucleus.MetaData.level=WARNING
|
|
||||||
DataNucleus.General.level=WARNING
|
|
||||||
DataNucleus.Utility.level=WARNING
|
|
||||||
DataNucleus.Transaction.level=WARNING
|
|
||||||
DataNucleus.Datastore.level=WARNING
|
|
||||||
DataNucleus.ClassLoading.level=WARNING
|
|
||||||
DataNucleus.Plugin.level=WARNING
|
|
||||||
DataNucleus.ValueGeneration.level=WARNING
|
|
||||||
DataNucleus.Enhancer.level=WARNING
|
|
||||||
DataNucleus.SchemaTool.level=WARNING
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<queue-entries>
|
|
||||||
<queue>
|
|
||||||
<name>twitter</name>
|
|
||||||
<!-- poll every 30s, but only allow one request at a time to save CPU -->
|
|
||||||
<rate>2/m</rate>
|
|
||||||
<max-concurrent-requests>1</max-concurrent-requests>
|
|
||||||
</queue>
|
|
||||||
</queue-entries>
|
|
|
@ -1,57 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
|
|
||||||
version="2.5">
|
|
||||||
<display-name>jclouds-tweetstore</display-name>
|
|
||||||
|
|
||||||
<!-- Servlets -->
|
|
||||||
<filter>
|
|
||||||
<filter-name>guiceFilter</filter-name>
|
|
||||||
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
|
|
||||||
</filter>
|
|
||||||
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>guiceFilter</filter-name>
|
|
||||||
<url-pattern>/*</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
|
|
||||||
<listener>
|
|
||||||
<listener-class>org.jclouds.demo.tweetstore.config.GuiceServletConfig
|
|
||||||
</listener-class>
|
|
||||||
</listener>
|
|
||||||
|
|
||||||
<!-- limit submission of storage tasks to the cron job -->
|
|
||||||
<security-constraint>
|
|
||||||
<web-resource-collection>
|
|
||||||
<url-pattern>/stores/*</url-pattern>
|
|
||||||
</web-resource-collection>
|
|
||||||
<auth-constraint>
|
|
||||||
<role-name>admin</role-name>
|
|
||||||
</auth-constraint>
|
|
||||||
</security-constraint>
|
|
||||||
|
|
||||||
<welcome-file-list>
|
|
||||||
<welcome-file>index.jsp</welcome-file>
|
|
||||||
</welcome-file-list>
|
|
||||||
|
|
||||||
</web-app>
|
|
|
@ -1,31 +0,0 @@
|
||||||
<%--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
--%>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>jclouds: anyweight cloudware for java</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Welcome!</h2>
|
|
||||||
<p>Click <a href="/tweets/get">here</a> to see tweets about jclouds.</p>
|
|
||||||
<p><img src="http://code.google.com/appengine/images/appengine-noborder-120x30.gif"
|
|
||||||
alt="Powered by Google App Engine" /></p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,109 +0,0 @@
|
||||||
<%--
|
|
||||||
|
|
||||||
Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. jclouds 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.
|
|
||||||
|
|
||||||
--%>
|
|
||||||
<%@ page buffer="20kb"%>
|
|
||||||
<%@ taglib uri="http://displaytag.sf.net" prefix="display"%>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>jclouds: anyweight cloudware for java</title>
|
|
||||||
<style type="text/css">
|
|
||||||
<!--
|
|
||||||
table.staticheader {
|
|
||||||
text-decoration: none;
|
|
||||||
border: 1px solid #CCC;
|
|
||||||
width: 98%;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader th {
|
|
||||||
padding: 3px 3px 3px 3px !important;
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader td {
|
|
||||||
padding: 3px 3px 3px 3px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader thead tr {
|
|
||||||
position: relative;
|
|
||||||
height: 10px;
|
|
||||||
background-color: #D7E5F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody {
|
|
||||||
height:800px;
|
|
||||||
overflow-x:hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow:scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr {
|
|
||||||
height: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr.odd {
|
|
||||||
background-color: #eee
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr.tableRowEven,tr.even {
|
|
||||||
background-color: #ddd
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody tr td:last-child {
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.staticheader tbody td {
|
|
||||||
padding: 2px 4px 2px 4px !important;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
div.TableContainer {
|
|
||||||
height: 800px;
|
|
||||||
overflow-x:hidden;
|
|
||||||
overflow-y:auto;
|
|
||||||
}
|
|
||||||
-->
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h2>Tweets in Clouds</h2>
|
|
||||||
<table width="100%" border="0">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="TableContainer">
|
|
||||||
<display:table name="tweets" defaultsort="1" cellpadding="5" cellspacing="1" class="staticheader">
|
|
||||||
<display:column property="id" title="Tweet ID" />
|
|
||||||
<display:column property="from" title="Who Said it" />
|
|
||||||
<display:column property="tweet" title="Tweet" />
|
|
||||||
<display:column property="service" title="Cloud" />
|
|
||||||
<display:column property="host" title="Host" />
|
|
||||||
<display:column property="status" title="Status" />
|
|
||||||
</display:table>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="http://code.google.com/appengine/images/appengine-noborder-120x30.gif"
|
|
||||||
alt="Powered by Google App Engine" /></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,82 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.config.util;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
import static org.testng.Assert.assertTrue;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector;
|
|
||||||
import org.jclouds.demo.tweetstore.config.util.CredentialsCollector.Credential;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code CredentialsCollector}
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class CredentialsCollectorTest {
|
|
||||||
private CredentialsCollector collector = new CredentialsCollector();
|
|
||||||
|
|
||||||
public void testEmptyProperties() {
|
|
||||||
assertTrue(collector.apply(new Properties()).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNoCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("not-an-identity",
|
|
||||||
"v1", "not-a-credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Properties propertiesOf(Map<String, String> entries) {
|
|
||||||
Properties properties = new Properties();
|
|
||||||
properties.putAll(entries);
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testNonMatchingCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("non_matching.identity", "v1",
|
|
||||||
"non_matching.credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testIncompleteCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("acme.identity", "v1",
|
|
||||||
"acme-2.credential", "v2"));
|
|
||||||
assertTrue(collector.apply(properties).isEmpty(),
|
|
||||||
"Expected returned map to be empty");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCredentials() {
|
|
||||||
Properties properties = propertiesOf(ImmutableMap.of("acme.identity", "v1",
|
|
||||||
"acme.credential", "v2", "acme-2.identity", "v3",
|
|
||||||
"acme-2.credential", "v4"));
|
|
||||||
assertEquals(collector.apply(properties),
|
|
||||||
ImmutableMap.of("acme", new Credential("v1", "v2"),
|
|
||||||
"acme-2", new Credential("v3", "v4")));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.domain.StoredTweetStatus;
|
|
||||||
import org.jclouds.demo.tweetstore.functions.ServiceToStoredTweetStatuses;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
import org.testng.collections.Maps;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code AddTweetsController}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class AddTweetsControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createServices(String container) throws InterruptedException,
|
|
||||||
ExecutionException {
|
|
||||||
Map<String, BlobStoreContext> services = Maps.newHashMap();
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
for (String name : new String[] { "1", "2" }) {
|
|
||||||
BlobStoreContext context = ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class);
|
|
||||||
context.getAsyncBlobStore().createContainerInLocation(null, container).get();
|
|
||||||
Blob blob = context.getAsyncBlobStore().blobBuilder("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
context.getAsyncBlobStore().putBlob(container, blob).get();
|
|
||||||
services.put(name, context);
|
|
||||||
}
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testStoreTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String container = "container";
|
|
||||||
Map<String, BlobStoreContext> contexts = createServices(container);
|
|
||||||
|
|
||||||
ServiceToStoredTweetStatuses function = new ServiceToStoredTweetStatuses(contexts, container);
|
|
||||||
AddTweetsController controller = new AddTweetsController(contexts, function);
|
|
||||||
List<StoredTweetStatus> list = controller.apply(ImmutableSet.of("1", "2"));
|
|
||||||
assertEquals(list.size(), 2);
|
|
||||||
assertEquals(list, ImmutableList.of(new StoredTweetStatus("1", "localhost", container, "1",
|
|
||||||
"frank", "I love beans!", null), new StoredTweetStatus("2", "localhost", container,
|
|
||||||
"1", "frank", "I love beans!", null)));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static org.testng.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.jclouds.blobstore.domain.Blob;
|
|
||||||
import org.jclouds.demo.tweetstore.reference.TweetStoreConstants;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@code AddTweetsController}
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class ClearTweetsControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createBlobStores(String container) throws InterruptedException, ExecutionException {
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
Map<String, BlobStoreContext> contexts = ImmutableMap.<String, BlobStoreContext>of(
|
|
||||||
"test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class),
|
|
||||||
"test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class));
|
|
||||||
for (BlobStoreContext blobstore : contexts.values()) {
|
|
||||||
blobstore.getBlobStore().createContainerInLocation(null, container);
|
|
||||||
Blob blob = blobstore.getAsyncBlobStore().blobBuilder("1").build();
|
|
||||||
blob.getMetadata().getUserMetadata().put(TweetStoreConstants.SENDER_NAME, "frank");
|
|
||||||
blob.setPayload("I love beans!");
|
|
||||||
blobstore.getBlobStore().putBlob(container, blob);
|
|
||||||
}
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testClearTweets() throws IOException, InterruptedException, ExecutionException {
|
|
||||||
String container = ClearTweetsControllerTest.class.getName() + "#container";
|
|
||||||
Map<String, BlobStoreContext> contexts = createBlobStores(container);
|
|
||||||
|
|
||||||
ClearTweetsController controller = new ClearTweetsController(contexts,
|
|
||||||
container);
|
|
||||||
controller.clearContainer("test1");
|
|
||||||
controller.clearContainer("test2");
|
|
||||||
|
|
||||||
for (BlobStoreContext context : contexts.values()) {
|
|
||||||
assertEquals(context.getBlobStore().countBlobs(container), 0, context.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
/**
|
|
||||||
* Licensed to jclouds, Inc. (jclouds) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. jclouds 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.jclouds.demo.tweetstore.controller;
|
|
||||||
|
|
||||||
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
|
|
||||||
import static org.easymock.EasyMock.createMock;
|
|
||||||
import static org.easymock.EasyMock.expect;
|
|
||||||
import static org.easymock.EasyMock.replay;
|
|
||||||
import static org.easymock.EasyMock.verify;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jclouds.ContextBuilder;
|
|
||||||
import org.jclouds.blobstore.BlobStoreContext;
|
|
||||||
import org.jclouds.blobstore.TransientApiMetadata;
|
|
||||||
import org.testng.annotations.Test;
|
|
||||||
|
|
||||||
import com.google.appengine.api.taskqueue.Queue;
|
|
||||||
import com.google.appengine.api.taskqueue.TaskOptions.Method;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests behavior of {@link EnqueueStoresController}
|
|
||||||
*
|
|
||||||
* @author Andrew Phillips
|
|
||||||
*/
|
|
||||||
@Test(groups = "unit")
|
|
||||||
public class EnqueueStoresControllerTest {
|
|
||||||
|
|
||||||
Map<String, BlobStoreContext> createBlobStores() {
|
|
||||||
TransientApiMetadata transientApiMetadata = TransientApiMetadata.builder().build();
|
|
||||||
Map<String, BlobStoreContext> contexts = ImmutableMap.<String, BlobStoreContext>of(
|
|
||||||
"test1", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class),
|
|
||||||
"test2", ContextBuilder.newBuilder(transientApiMetadata).build(BlobStoreContext.class));
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEnqueueStores() {
|
|
||||||
Map<String, BlobStoreContext> stores = createBlobStores();
|
|
||||||
Queue taskQueue = createMock(Queue.class);
|
|
||||||
EnqueueStoresController function = new EnqueueStoresController(stores, taskQueue);
|
|
||||||
|
|
||||||
expect(taskQueue.add(withUrl("/store/do").header("context", "test1").method(Method.GET))).andReturn(null);
|
|
||||||
expect(taskQueue.add(withUrl("/store/do").header("context", "test2").method(Method.GET))).andReturn(null);
|
|
||||||
replay(taskQueue);
|
|
||||||
|
|
||||||
function.enqueueStoreTweetTasks();
|
|
||||||
|
|
||||||
verify(taskQueue);
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue