Skip to content

Commit

Permalink
multitenancy (#195)
Browse files Browse the repository at this point in the history
  • Loading branch information
lesv committed Apr 26, 2016
1 parent a460072 commit 04f5219
Show file tree
Hide file tree
Showing 19 changed files with 1,099 additions and 0 deletions.
19 changes: 19 additions & 0 deletions appengine/multitenancy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Multitenancy Java sample

Shows the usage of the Namespaces API.

An App Engine guestbook using Java, Maven, and Objectify.

Data access using [Objectify](https://github.com/objectify/objectify)

Please ask questions on [Stackoverflow](http://stackoverflow.com/questions/tagged/google-app-engine)

## Running Locally

How do I, as a developer, start working on the project?

1. `mvn clean appengine:devserver`

## Deploying

1. `mvn clean appengine:update -Dappengine.appId=PROJECT -Dappengine.version=VERSION`
122 changes: 122 additions & 0 deletions appengine/multitenancy/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>

<groupId>com.example.appengine</groupId>
<artifactId>appengine-multitenancy</artifactId>

<properties>
<objectify.version>5.1.5</objectify.version>
<guava.version>18.0</guava.version>
</properties>
<parent>
<groupId>com.google.cloud</groupId>
<artifactId>doc-samples</artifactId>
<version>1.0.0</version>
<relativePath>../..</relativePath>
</parent>

<!-- [START set_versions] -->
<prerequisites>
<maven>3.3.9</maven>
</prerequisites>
<!-- [END set_versions] -->

<dependencies>
<!-- Compile/runtime dependencies -->
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>${appengine.sdk.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<!-- [START Objectify_Dependencies] -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>com.googlecode.objectify</groupId>
<artifactId>objectify</artifactId>
<version>${objectify.version}</version>
</dependency>
<!-- [END Objectify_Dependencies] -->

<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-testing</artifactId>
<version>${appengine.sdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-stubs</artifactId>
<version>${appengine.sdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-tools-sdk</artifactId>
<version>${appengine.sdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<version>0.28</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<!-- for hot reload of the web application-->
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/classes</outputDirectory>
<plugins>
<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>${appengine.sdk.version}</version>
<configuration>
<enableJarClasses>false</enableJarClasses>
<!-- Comment in the below snippet to bind to all IPs instead of just localhost -->
<!-- address>0.0.0.0</address>
<port>8080</port -->
<!-- Comment in the below snippet to enable local debugging with a remote debugger
like those included with Eclipse or IntelliJ -->
<!-- jvmFlags>
<jvmFlag>-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n</jvmFlag>
</jvmFlags -->
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Copyright 2014-2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

//[START all]
package com.example.appengine;

import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
import com.googlecode.objectify.annotation.Parent;

import java.util.Date;

/**
* The @Entity tells Objectify about our entity. We also register it in {@link OfyHelper}
* Our primary key @Id is set automatically by the Google Datastore for us.
*
* We add a @Parent to tell the object about its ancestor. We are doing this to support many
* guestbooks. Objectify, unlike the AppEngine library requires that you specify the fields you
* want to index using @Index. Only indexing the fields you need can lead to substantial gains in
* performance -- though if not indexing your data from the start will require indexing it later.
*
* NOTE - all the properties are PUBLIC so that can keep the code simple.
**/
@Entity
public class Greeting {
@Parent Key<Guestbook> theBook;
@Id public Long id;

public String authorEmail;
public String authorId;
public String content;
@Index public Date date;

/**
* Simple constructor just sets the date.
**/
public Greeting() {
date = new Date();
}

/**
* A convenience constructor.
**/
public Greeting(String book, String content) {
this();
if ( book != null ) {
theBook = Key.create(Guestbook.class, book); // Creating the Ancestor key
} else {
theBook = Key.create(Guestbook.class, "default");
}
this.content = content;
}

public Greeting(String book, String content, String id, String email) {
this(book, content);
authorEmail = email;
authorId = id;
}

}
//[END all]
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Copyright 2014-2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

//[START all]
package com.example.appengine;

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;

/**
* The @Entity tells Objectify about our entity. We also register it in
* OfyHelper.java -- very important.
*
* This is never actually created, but gives a hint to Objectify about our Ancestor key.
*/
@Entity
public class Guestbook {
@Id public String book;
}
//[END all]
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.appengine;

import com.google.appengine.api.NamespaceManager;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.SearchService;
import com.google.appengine.api.search.SearchServiceConfig;
import com.google.appengine.api.search.SearchServiceFactory;
import com.google.appengine.api.users.UserServiceFactory;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// [START example]
@SuppressWarnings("serial")
public class MultitenancyServlet extends HttpServlet {

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String namespace;

PrintWriter out = resp.getWriter();
out.println("Code Snippets -- not yet fully runnable as an app");

// [START temp_namespace]
// Set the namepace temporarily to "abc"
String oldNamespace = NamespaceManager.get();
NamespaceManager.set("abc");
try {
// ... perform operation using current namespace ...
} finally {
NamespaceManager.set(oldNamespace);
}
// [END temp_namespace]

// [START per_user_namespace]
if (com.google.appengine.api.NamespaceManager.get() == null) {
// Assuming there is a logged in user.
namespace = UserServiceFactory.getUserService().getCurrentUser().getUserId();
NamespaceManager.set(namespace);
}
// [END per_user_namespace]
String value = "something here";

// [START ns_memcache]
// Create a MemcacheService that uses the current namespace by
// calling NamespaceManager.get() for every access.
MemcacheService current = MemcacheServiceFactory.getMemcacheService();

// stores value in namespace "abc"
oldNamespace = NamespaceManager.get();
NamespaceManager.set("abc");
try {
current.put("key", value); // stores value in namespace “abc”
} finally {
NamespaceManager.set(oldNamespace);
}
// [END ns_memcache]

// [START specific_memcache]
// Create a MemcacheService that uses the namespace "abc".
MemcacheService explicit = MemcacheServiceFactory.getMemcacheService("abc");
explicit.put("key", value); // stores value in namespace "abc"
// [END specific_memcache]

//[START searchns]
// Set the current namespace to "aSpace"
NamespaceManager.set("aSpace");
// Create a SearchService with the namespace "aSpace"
SearchService searchService = SearchServiceFactory.getSearchService();
// Create an IndexSpec
IndexSpec indexSpec = IndexSpec.newBuilder().setName("myIndex").build();
// Create an Index with the namespace "aSpace"
Index index = searchService.getIndex(indexSpec);
// [END searchns]

// [START searchns_2]
// Create a SearchServiceConfig, specifying the namespace "anotherSpace"
SearchServiceConfig config = SearchServiceConfig.newBuilder()
.setNamespace("anotherSpace").build();
// Create a SearchService with the namespace "anotherSpace"
searchService = SearchServiceFactory.getSearchService(config);
// Create an IndexSpec
indexSpec = IndexSpec.newBuilder().setName("myindex").build();
// Create an Index with the namespace "anotherSpace"
index = searchService.getIndex(indexSpec);
// [END searchns_2]

}



}
// [END example]
Loading

0 comments on commit 04f5219

Please sign in to comment.