SlideShare una empresa de Scribd logo
1 de 52
Descargar para leer sin conexión
Using the Google App Engine
with Java

Michael Parker
michael.g.parker@gmail.com
App Engine Introduction
●

Upload your web app to sandbox, and it's
ready to go
–

–

●

The good: little maintenance, scalable
transactional storage, secure and reliable
environment, standard APIs used
The bad/unfamiliar: not a relational DB,
sandboxed filesystem and sockets, no longrunning responses

Free quota: 500MB of storage, and CPU and
bandwidth for 5M pageviews per month
Services
Service

Java Standard

Google Infrastructure

Authentication

Servlet API

Google Accounts

Datastore

JPA, JDO

Bigtable

Caching

javax.cache

memcacheg

E-mail

javax.mail

Gmail gateway

URLFetch

URLConnection

Caching HTTP Proxy

●

Other services:
–

Java servlet 2.5 implementation, image
manipulation, asynchronous task scheduling
Development
●

●
●

Apache Ant component to simplify common
App Engine tasks
Google Plugin for Eclipse
Local development server simulates the
sandbox restrictions, datastore, and services
–

LRU memcache

–

Disk-backed datastore

–

Jakarta Commons HttpClient-backed URL
Fetch
Sandboxing
●

●

●

●

Can read all application files uploaded with the
app; for read-write, use the datastore.
No “direct” network access; use the URL fetch
service for HTTP/HTTPS access
No spawning new threads or processes; must
use cron service
Servlet requests can take up to 30s to respond
before a throwing
DeadlineExceededException
Datastore with JDO
●

●

●

●

JDO (JSR 243) defines annotations for Java
objects, retrieving objects with queries, and
interacting with a database using transactions
Post-compilation "enhancement" step on
compiled classes associates them with the
JDO implementation
The PersistenceManager is the interface to
the underlying JDO implementation
Datastore implementation is scalable with an
emphasis on reads and queries
Datastore Entities
●

●

●

A entity has one or more properties, which are
ints, floats, strings, dates, blobs, or references
to other entites
Each entity has a key; entities are fetched
using their corresponding key, or by a query
that matches its properties.
Entities are schemaless; must enforce at the
application level
Annotating an Entity with JDO
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private String firstName;
@Persistent
private String lastName;
@Persistent
private Date hireDate;
Public Employee(String firstName, String lastname,
Date hireDate) { … }
}

/* accessors and other methods here */
Entity Keys
●

●

Unique and identified by the @PrimaryKey
annotation.
Keys are a kind (class name) and:
–

A long automatically generated by the
datastore, e.g. the unique message ID for an
e-mail

–

A string specified by the client, e.g. the
username belonging to an account
Creating Keys
●

A Key instance combines the long or string
fields with key representing the entity group
ancestors, if any

Key keyFromString = KeyFactory.createKey(
Employee.class.getSimpleName(), "Alfred.Smith@example.com");
Key keyFromLong = KeyFactory.createKey(
Employee.class.getSimpleName(), 52234);
Key keyWithParent = new KeyFactory
.Builder(Employee.class.getSimpleName(), 52234)
.addChild(ExpenseReport.class.getSimpleName(), "A23Z79")
.getKey();
Atomic Storage Operations
PersistenceManager pm = pmfInstance.getPersistenceManager();
Employee e = new Employee("Alfred", "Smith", new Date());
try {
// Create
pm.makePersistent(e);
// Update
Key key = KeyFactory.createKey(
Employee.class.getSimpleName(),
"Alfred.Smith@example.com");
Employee copy = pm.getObjectById(Employee.class, key);
// Delete
pm.deletePersistent(copy);
} finally {
pm.close();
}
Queries
●

A query specifies
–
–

●

Zero or more conditions based on their
property values

–
●

An entity kind

Zero or more sort orders

Once executed, can return all entities meeting
these criteria in the given sort order, or just
their keys
JDO has its own query language, like SQL,
with two different calling styles
JDOQL Calling Styles
●

String style:

Query query = pm.newQuery("select from Employee " +
"where lastName == lastNameParam " +
"order by hireDate desc " +
"parameters String lastNameParam")
List<Employee> results = (List<Employee>) query.execute("Smith");
●

Method style:

Query query = pm.newQuery(Employee.class);
// select from
query.setFilter("lastName == lastNameParam");
// where
query.setOrdering("hireDate desc");
// order by
query.declareParameters("String lastNameParam"); // parameters
List<Employee> results = (List<Employee>) query.execute("Smith");
Query Caveats
●

Filters have a field name, an operator, and a
value
–
–

The operator must be in < <= == >= >

–

Only logical and is supported for multiple filters

–
●

The value must be provided by the app

Cannot test inequality on multiple properties

A query can specify a range of results to be
returned to the application.
–

Datastore must retrieve and discard all results
before to the starting offset
Indexes
●

●

An application has an index for each
combination of kind, filter property and
operator, and sort order used in a query.
Given a query, the datastore identifies the
index to use
–

●

all results for every possible query that uses an
index are in consecutive rows in the table

An index will sort entities first by value type,
then by an order appropriate to the type.
–

Watch out! 38 (int) < 37.5 (float)
Custom Indexes
●

In production, a query with no suitable index
will fail, but the development web server can
create the configuration for an index and
succeed
–

●

Indexes specified in datastore-indexes.xml

Must specify an index to be built for queries
like:
–

queries with multiple sort orders

–

queries with a sort order on keys in
descending order
Custom Indexes Code
●

The XML configuration:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes
xmlns="http://appengine.google.com/ns/datastore-indexes/1.0"
autoGenerate="true">
<datastore-index kind="Person" ancestor="false">
<property name="lastName" direction="asc" />
<property name="height" direction="desc" />
</datastore-index>
</datastore-indexes>

supports:
select from Person where lastName = 'Smith'
&& height < 72
order by height desc
Exploding Indexes
●

A property value for an entity is stored in every
custom index that refers to the property
–

●

●

The more indexes that refer to a property, the
longer it takes to update a property

For properties with multiple values, an index
has a row for every permutation of values for
every property
To keep updates quick, datastore limits the
number of index entries an entity can have
–

Insertion or update will fail with an exception
Exploding Indexes Example
●

Custom index:

<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes>
<datastore-index kind="MyModel">
<property name="x" direction="asc" />
<property name="y" direction="asc" />
</datastore-index>
</datastore-indexes>
●

Adding an entity:

MyModel m = new MyModel();
m.setX(Arrays.asList("one", "two"));
m.setY(Arrays.asList("three", "four"));
pm.makePersistent(m);

Built-in on x:
one, two
two, one
Built-in on y:
three, four
four, three
Custom index:
one, two

three, four

one, two

four, three

two, one

three, four

two, one

four, three
Relationships
●

Relationship dimensions:
–
–

One-to-one versus one-to-many

–
●

Owned versus unowned
Unidirectional and bidirectional

Implementation of the JDO can model owned
one-to-one and owned one-to-many
relationships, both unidirectional and
bidirectional
–

Unowned is possible with some manual
bookkeeping, allows many-to-many
Owned, one-to-one
●

Have a parent (the owner) and a child
–
–

●

●
●

Follows from encapsulation in code
Child key uses the parent key as its entity
group parent

When the parent is retrieved, the child is
retrieved
In unidirectional case, child has Key for parent
In bidirectional case, child has reference to
parent
–

When child is retrieved, parent is retrieved
One-to-one unidirectional code
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ContactInfo /* the child */ {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
}

// ...

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee /* the parent */ {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private ContactInfo contactInfo;
// …
}
One-to-one bidirectional code
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ContactInfo /* the child */ {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent(mappedBy = "contactInfo")
private Employee employee;
}
●
●

// ...

Note that the Key member is still present
The argument to mappedBy must be the name
of the child in the parent class
One-to-many bidirectional code
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class ContactInfo /* the child */ {
// ...
@Persistent
private Employee employee;
// ...
}
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee /* the parent */ {
// …
@Persistent(mappedBy = “employee”)
private List<ContactInfo> contactInfoSets;
// …
}
●

Note that mappedBy is on the parent class, its
argument is its name in the child class
Owned collections
●

●

Can use any Set, List, or built-in collection
implementation for owned one-to-many
Order is preserved by storing a position
property for every element
–

●

If an element is added or deleted, positions of
subsequent elements must be updated

If you do not need to preserve arbitrary order,
use the @Order annotation:

@Persistent
@Order(extensions = @Extension(vendorName="datanucleus",
key="list-ordering", value="state asc, city asc"))
private List<ContactInfo> contactInfoSets = new List<ContactInfo>();
Unowned relationships
●

●

Use Key instances instead of instances or a
collection of instances
Easy to model any relationship, but
–

No referential integrity is enforced

–

In some cases, entities on different sides of the
relationship belong to different entity groups,
disallowing atomic updates
Unowned many-to-many code
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Person {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;

}

@Persistent
private Set<Key> favoriteFoods;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Food {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;

}

@Persistent
private Set<Key> foodFans;
Relationships and Transactions
Employee e = new Employee();
ContactInfo ci = new ContactInfo();
e.setContactInfo(ci);
pm.makePersistent(e);
●

Without a transaction, entities are created in
separate atomic actions, not a single one:

Transaction tx = null;
try {
tx = pm.currentTransaction();
tx.begin();
pm.makePersistent(e);
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
}
Low Level Datastore API
●

There is a lower-level API if you don't like the
abstraction that JDO provides you
–

●

This is the API that the App Engine JDO
implementation uses

Data store operations:
–

get for set of keys with optional transaction

–

put for set of values with optional transaction

–

delete for set of keys with optional transaction

–

query preparation and execution
Entity Groups
●

●

The fundamental data unit in a transaction is
the entity group; a single transaction can only
manipulate data in one entity group.
Each entity group is a hierarchy:
–
–

An entity that is a parent for another entity can
also have a parent.

–

●

An entity without a parent is a root entity.

Every entity with a given root entity as an
ancestor is in the same entity group.

All entities in a group are stored in the same
datastore node.
Creating a Hierarchy
●

●

Creating a hierarchical data model is very
different from using SQL
For example, given a online photo album, can
define the user as the root
–

children can be preferences and photo albums

–

children of albums can be images, which can
be further broken down into EXIF data and
comments, etc.
Hierarchies with JDO
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class AccountInfo {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;

}

public void setKey(Key key) {
this.key = key;
}

public static void createAccount(String customerId, String accountId) {
KeyFactory.Builder keyBuilder = new KeyFactory.Builder(
Customer.class.getSimpleName(), customerId);
keyBuilder.addChild(AccountInfo.class.getSimpleName(),
accountId);
Key accountKey = keyBuilder.getKey();
return new AccountInfo(customerId, accountId);
}
Transactions
●

●

When a transaction commits, all writes
succeed, or else the transaction fails and must
be retried.
A transaction uses optimistic concurrency
control:
–

When creating the transaction, get the time the
entity group was last updated

–

On every read within the group, succeed if the
entity group time is unchanged

–

On committing, or writing, succeed if the entity
group time is unchanged
Transaction Help
●

With optimistic concurrency, can need to try a
transaction several times; JDO throws a
JDODataStore exception and gives up
–

●

Consider bundling the transaction logic into a
Runnable or Callable, and have a helper
method

Make them happen quickly
–

Prepare keys and data outside the transaction
Transactions with JDO
for (int i = 0; i < NUM_RETRIES; i++) {
pm.currentTransaction().begin();
ClubMembers members = pm.getObjectById(
ClubMembers.class, "k12345");
members.incrementCounterBy(1);
try {
pm.currentTransaction().commit();
break;

}

} catch (JDOCanRetryException ex) {
if (i == (NUM_RETRIES - 1)) {
throw ex;
}
}
Memcache
●

●

Implementation of JCache (JSR 107) atop of
memcache
Use when you would a traditional cache:
–
–

●

Returned data can be potentially stale

–

●

The data is popular or query is expensive
If the cached data is unavailable, the
application performs fine

Entries evicted in LRU order when low on
memory, or an expiration time can be provided
Like the datastore, has a low-level API
Memcache code
●

Behaves like java.util.Map:

String key = "key";
byte[] value = "value".getBytes(Charset.forName(“UTF-8”));
// Put the value into the cache.
cache.put(key, value);
// Get the value from the cache.
value = (byte[]) cache.get(key);
●

●

Has other familiar methods, like putAll,
containsKey, size, isEmpty, remove, and clear
Can set the policy when a value exists
–

Has “only replace” as well as “only add”
URL Fetch
●

●

Synchronous HTTP or HTTPS retrieval
allowing GET, POST, PUT, HEAD, and
DELETE through a HTTP/1.1-compliant proxy
Can set HTTP headers on outgoing requests
–

●

Use Google Secure Data Connector to access
intranet URLs
–

●

Some exceptions, e.g. Host and Referer

Restricts to users signed in using an Apps
account for your domain

Like the memcache, has a low-level API
URL Fetch Code
●

URL.openStream() transparently uses URL
fetch:

URL url = new URL("http://www.example.com/atom.xml");
BufferedReader reader = new BufferedReader(
new InputStreamReader(url.openStream()));
●

As will URL.openConnection():

URL url = new URL("http://www.example.com/comment");
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
●

URLConnection is not persistent; buffers
request until the client accesses the response,
and once received, closes the connection.
Mail
●

●

To not use the administrator's e-mail account
as sender, create a new account and add it as
the administrator for the application
When mail service is called, message is
enqueued and call returns immediately
–

●

●

Application receives no notification of whether
delivery succeeded or failed

Sender is the application developer or the
address of the Google Accounts user
Like URL fetch, has a low-level API
Google Accounts
●

Authentication with Google Accounts is
optional
–
–

●
●

Address from Apps domain, or gmail.com
Allows development of admin-only site parts

Not SSO from other Google applications
Datastore supports storing the User object as
a special value type, but don't rely on them as
stable user identifiers
–

If a user changes his or her e-mail address,
new User is different than what is stored
Google Accounts Code
UserService userService = UserServiceFactory.getUserService();
String thisURL = request.getRequestURI();
if (request.getUserPrincipal() != null) {
response.getWriter().println("<p>Hello, " +
request.getUserPrincipal().getName() +
"! You can <a href="" +
userService.createLogoutURL(thisURL) +
"">sign out</a>.</p>");
} else {
response.getWriter().println("<p>Please <a href="" +
userService.createLoginURL(thisURL) +
"">sign in</a>.</p>");
}
Deployment Descriptor
●

The web.xml in the application's WAR file
under the WEB-INF/ directory defines URL to
servlet mappings, which URLs require auth,
and other properties
–

●

Part of the servlet standard, many references

App Engine supports automatic compilation
and URL mapping for JSPs, and the JSP
Standard Tag Library
Deployment Security
●

A user role of * requires a Google Account:

<security-constraint>
<web-resource-collection>
<url-pattern>/profile/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
Application Configuration
●

An appengine-web.xml file specifies additional
application properties
–

The application identifier

–

The version identifier of the latest code

–

Static files (publically served) and resource
files (application private)

–

System properties and environment variables

–

Toggling of SSL and sessions
Scheduled Tasks
●
●

No Executor service for task scheduling yet...
The cron service invokes a URL at a specified
time of day
–
–

●

Scheduled tasks can access admin-only URLs
Requests have the HTTP header XAppEngine-Cron: true

Time in a simple English-like format:
–

every 5 minutes

–

2nd,third mon,wed,thu of march 17:00

–

every day 00:00
Scheduled Tasks cron.xml
●

Timezone is UTC (i.e. GMT) by default:

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
<cron>
<url>/recache</url>
<description>Repopulate the cache every 2 minutes</description>
<schedule>every 2 minutes</schedule>
</cron>
<cron>
<url>/weeklyreport</url>
<description>Mail out a weekly report</description>
<schedule>every monday 08:30</schedule>
<timezone>America/New_York</timezone>
</cron>
</cronentries>
Service Implementation
Standard Interface
JDO, JPA, JCache, JavaMail
Proprietary Interface
com.google.appengine.api.*
Language Neutral Interface
ApiProxy, protocol buffers
Implementation
Google Servers, SDK stubs
●

All calls go through ApiProxy, which in turn
invokes a registered delegate
Profiling with ApiProxy
class ProfilingDelegate extends Delegate {
Delegate parent;
public ProfilingDelegate(Delegate parent) {
this.parent = parent;
}
public byte[] makeSyncCall(Environment env, String pkg,
String method, byte[] request) {
long start = System.nanoTime();
byte[] result = parent.makeSyncCall(env, pkg, method, request);
log.log(INFO,
pkg + “.” + method + “: “ + System.nanoTime() - start);
return result;
}
}
ApiProxy.setDelegate(new ProfilingDelegate(ApiProxy.getDelegate()));
Defining the Test Environment
●

Implement ApiProxy.Environment to return
information that appengine-web.xml returns:

class TestEnvironment implements ApiProxy.Environment {
public String getAppId() {
return "Unit Tests";
}
public String getVersionId() {
return "1.0";
}
public String getAuthDomain() {
return "gmail.com";
}
}

// ...
Creating a Test Harness
●

Specify the local implementations of all
services, so you do not use method stubs to a
remote server:

public class LocalServiceTestCase extends TestCase {
@Override
public void setUp() throws Exception {
super.setUp();
ApiProxy.setEnvironmentForCurrentThread(
new TestEnvironment());
ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")));
}
}

// ...
Using the Test Harness
●

Cast services to their local implementations:

public void testEmailGetsSent() {
ApiProxyLocalImpl proxy =
(ApiProxyLocalImpl) ApiProxy.getDelegate();
LocalMailService mailService =
(LocalMailService) proxy.getService("mail");
mailService.clearSentMessages();
Bug b = new Bug();
b.setSeverity(Severity.LOW);
b.setText("NullPointerException when updating phone number.");
b.setOwner("max");
new BugDAO().createBug(b);

}

assertEquals(1, mailService.getSentMessages().size());
// ... test the content and recipient of the email

Más contenido relacionado

La actualidad más candente

Entity Persistence with JPA
Entity Persistence with JPAEntity Persistence with JPA
Entity Persistence with JPASubin Sugunan
 
Hibernate tutorial for beginners
Hibernate tutorial for beginnersHibernate tutorial for beginners
Hibernate tutorial for beginnersRahul Jain
 
Hibernate Presentation
Hibernate  PresentationHibernate  Presentation
Hibernate Presentationguest11106b
 
Hibernate Tutorial
Hibernate TutorialHibernate Tutorial
Hibernate TutorialRam132
 
Java persistence api
Java persistence api Java persistence api
Java persistence api Luis Goldster
 
Drupal 8 entities & felds
Drupal 8 entities & feldsDrupal 8 entities & felds
Drupal 8 entities & feldsAndy Postnikov
 
Hibernate Developer Reference
Hibernate Developer ReferenceHibernate Developer Reference
Hibernate Developer ReferenceMuthuselvam RS
 
Introduction to Hibernate Framework
Introduction to Hibernate FrameworkIntroduction to Hibernate Framework
Introduction to Hibernate FrameworkRaveendra R
 
Hibernate ppt
Hibernate pptHibernate ppt
Hibernate pptAneega
 
Java Hibernate Programming with Architecture Diagram and Example
Java Hibernate Programming with Architecture Diagram and ExampleJava Hibernate Programming with Architecture Diagram and Example
Java Hibernate Programming with Architecture Diagram and Examplekamal kotecha
 

La actualidad más candente (20)

JPA For Beginner's
JPA For Beginner'sJPA For Beginner's
JPA For Beginner's
 
Entity Persistence with JPA
Entity Persistence with JPAEntity Persistence with JPA
Entity Persistence with JPA
 
Hibernate in Action
Hibernate in ActionHibernate in Action
Hibernate in Action
 
Hibernate
HibernateHibernate
Hibernate
 
hibernate with JPA
hibernate with JPAhibernate with JPA
hibernate with JPA
 
Jpa
JpaJpa
Jpa
 
Hibernate tutorial for beginners
Hibernate tutorial for beginnersHibernate tutorial for beginners
Hibernate tutorial for beginners
 
JPA and Hibernate
JPA and HibernateJPA and Hibernate
JPA and Hibernate
 
Hibernate Presentation
Hibernate  PresentationHibernate  Presentation
Hibernate Presentation
 
JPA Best Practices
JPA Best PracticesJPA Best Practices
JPA Best Practices
 
Hibernate Tutorial
Hibernate TutorialHibernate Tutorial
Hibernate Tutorial
 
Jpa 2.1 Application Development
Jpa 2.1 Application DevelopmentJpa 2.1 Application Development
Jpa 2.1 Application Development
 
Java persistence api
Java persistence api Java persistence api
Java persistence api
 
Jpa
JpaJpa
Jpa
 
Drupal 8 entities & felds
Drupal 8 entities & feldsDrupal 8 entities & felds
Drupal 8 entities & felds
 
Hibernate Developer Reference
Hibernate Developer ReferenceHibernate Developer Reference
Hibernate Developer Reference
 
Introduction to Hibernate Framework
Introduction to Hibernate FrameworkIntroduction to Hibernate Framework
Introduction to Hibernate Framework
 
Hibernate ppt
Hibernate pptHibernate ppt
Hibernate ppt
 
Java Hibernate Programming with Architecture Diagram and Example
Java Hibernate Programming with Architecture Diagram and ExampleJava Hibernate Programming with Architecture Diagram and Example
Java Hibernate Programming with Architecture Diagram and Example
 
JavaEE Spring Seam
JavaEE Spring SeamJavaEE Spring Seam
JavaEE Spring Seam
 

Destacado

Repaso3er grado2015rm
Repaso3er grado2015rmRepaso3er grado2015rm
Repaso3er grado2015rmCegama Paoa
 
Introduction to CouchDB - LA Hacker News
Introduction to CouchDB - LA Hacker NewsIntroduction to CouchDB - LA Hacker News
Introduction to CouchDB - LA Hacker NewsMichael Parker
 
Introduction to Redis - LA Hacker News
Introduction to Redis - LA Hacker NewsIntroduction to Redis - LA Hacker News
Introduction to Redis - LA Hacker NewsMichael Parker
 
Calendario servicio profesional docente
Calendario servicio profesional docenteCalendario servicio profesional docente
Calendario servicio profesional docenteCegama Paoa
 
Libro100juegosydinamicas 110323211245-phpapp02
Libro100juegosydinamicas 110323211245-phpapp02Libro100juegosydinamicas 110323211245-phpapp02
Libro100juegosydinamicas 110323211245-phpapp02Cegama Paoa
 
Descubre el coworking
Descubre el coworkingDescubre el coworking
Descubre el coworkingAna Seijo
 
Get the Jump of Spring with Brian O'Neill Sarasota
Get the Jump of Spring with Brian O'Neill SarasotaGet the Jump of Spring with Brian O'Neill Sarasota
Get the Jump of Spring with Brian O'Neill SarasotaBrian O'Neill
 
A To-do Web App on Google App Engine
A To-do Web App on Google App EngineA To-do Web App on Google App Engine
A To-do Web App on Google App EngineMichael Parker
 
Views from the Wild with Brian O'Neill Sarasota
Views from the Wild with Brian O'Neill Sarasota Views from the Wild with Brian O'Neill Sarasota
Views from the Wild with Brian O'Neill Sarasota Brian O'Neill
 
Repaso4to grado2015rm
Repaso4to grado2015rmRepaso4to grado2015rm
Repaso4to grado2015rmCegama Paoa
 
Sexto 4bim-15-16
Sexto 4bim-15-16Sexto 4bim-15-16
Sexto 4bim-15-16Cegama Paoa
 
Matemáticas act iniciar bien dia
Matemáticas act iniciar bien diaMatemáticas act iniciar bien dia
Matemáticas act iniciar bien diaCegama Paoa
 
Tablas de multiplicar
Tablas de multiplicarTablas de multiplicar
Tablas de multiplicarCegama Paoa
 
fichas de silabas
fichas de silabasfichas de silabas
fichas de silabasCegama Paoa
 

Destacado (17)

Repaso3er grado2015rm
Repaso3er grado2015rmRepaso3er grado2015rm
Repaso3er grado2015rm
 
Introduction to CouchDB - LA Hacker News
Introduction to CouchDB - LA Hacker NewsIntroduction to CouchDB - LA Hacker News
Introduction to CouchDB - LA Hacker News
 
Dates and Times
Dates and TimesDates and Times
Dates and Times
 
Introduction to Redis - LA Hacker News
Introduction to Redis - LA Hacker NewsIntroduction to Redis - LA Hacker News
Introduction to Redis - LA Hacker News
 
Calendario servicio profesional docente
Calendario servicio profesional docenteCalendario servicio profesional docente
Calendario servicio profesional docente
 
Libro100juegosydinamicas 110323211245-phpapp02
Libro100juegosydinamicas 110323211245-phpapp02Libro100juegosydinamicas 110323211245-phpapp02
Libro100juegosydinamicas 110323211245-phpapp02
 
Descubre el coworking
Descubre el coworkingDescubre el coworking
Descubre el coworking
 
Get the Jump of Spring with Brian O'Neill Sarasota
Get the Jump of Spring with Brian O'Neill SarasotaGet the Jump of Spring with Brian O'Neill Sarasota
Get the Jump of Spring with Brian O'Neill Sarasota
 
A To-do Web App on Google App Engine
A To-do Web App on Google App EngineA To-do Web App on Google App Engine
A To-do Web App on Google App Engine
 
Views from the Wild with Brian O'Neill Sarasota
Views from the Wild with Brian O'Neill Sarasota Views from the Wild with Brian O'Neill Sarasota
Views from the Wild with Brian O'Neill Sarasota
 
Repaso4to grado2015rm
Repaso4to grado2015rmRepaso4to grado2015rm
Repaso4to grado2015rm
 
Sexto 4bim-15-16
Sexto 4bim-15-16Sexto 4bim-15-16
Sexto 4bim-15-16
 
Compren lectora
Compren lectoraCompren lectora
Compren lectora
 
Matemáticas act iniciar bien dia
Matemáticas act iniciar bien diaMatemáticas act iniciar bien dia
Matemáticas act iniciar bien dia
 
Tablas de multiplicar
Tablas de multiplicarTablas de multiplicar
Tablas de multiplicar
 
Acertijos
AcertijosAcertijos
Acertijos
 
fichas de silabas
fichas de silabasfichas de silabas
fichas de silabas
 

Similar a Java on Google App engine

Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...
Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...
Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...Miguel Gallardo
 
TangoWithDjango - ch8
TangoWithDjango - ch8TangoWithDjango - ch8
TangoWithDjango - ch8Asika Kuo
 
Moodle Development Best Pracitces
Moodle Development Best PracitcesMoodle Development Best Pracitces
Moodle Development Best PracitcesJustin Filip
 
Google app-engine-with-python
Google app-engine-with-pythonGoogle app-engine-with-python
Google app-engine-with-pythonDeepak Garg
 
JMP103 : Extending Your App Arsenal With OpenSocial
JMP103 : Extending Your App Arsenal With OpenSocialJMP103 : Extending Your App Arsenal With OpenSocial
JMP103 : Extending Your App Arsenal With OpenSocialRyan Baxter
 
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocial
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocialIBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocial
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocialIBM Connections Developers
 
C# .NET Developer Portfolio
C# .NET Developer PortfolioC# .NET Developer Portfolio
C# .NET Developer Portfoliocummings49
 
Java EE Application Security With PicketLink
Java EE Application Security With PicketLinkJava EE Application Security With PicketLink
Java EE Application Security With PicketLinkpigorcraveiro
 
Get things done with Yii - quickly build webapplications
Get things done with Yii - quickly build webapplicationsGet things done with Yii - quickly build webapplications
Get things done with Yii - quickly build webapplicationsGiuliano Iacobelli
 
Google App Engine Developer - Day2
Google App Engine Developer - Day2Google App Engine Developer - Day2
Google App Engine Developer - Day2Simon Su
 
Mastering Test Automation: How To Use Selenium Successfully
Mastering Test Automation: How To Use Selenium SuccessfullyMastering Test Automation: How To Use Selenium Successfully
Mastering Test Automation: How To Use Selenium SuccessfullySpringPeople
 
AEM Sightly Deep Dive
AEM Sightly Deep DiveAEM Sightly Deep Dive
AEM Sightly Deep DiveGabriel Walt
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular IntermediateLinkMe Srl
 
Frontend APIs powering fast paced product iterations
Frontend APIs powering fast paced product iterationsFrontend APIs powering fast paced product iterations
Frontend APIs powering fast paced product iterationsKarthik Ramgopal
 
Implementing a Symfony Based CMS in a Publishing Company
Implementing a Symfony Based CMS in a Publishing CompanyImplementing a Symfony Based CMS in a Publishing Company
Implementing a Symfony Based CMS in a Publishing CompanyMarcos Labad
 
Android training day 4
Android training day 4Android training day 4
Android training day 4Vivek Bhusal
 
Your First Scala Web Application using Play 2.1
Your First Scala Web Application using Play 2.1Your First Scala Web Application using Play 2.1
Your First Scala Web Application using Play 2.1Matthew Barlocker
 
Introduction to django
Introduction to djangoIntroduction to django
Introduction to djangoIlian Iliev
 

Similar a Java on Google App engine (20)

Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...
Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...
Code decoupling from Symfony (and others frameworks) - PHP Conference Brasil ...
 
TangoWithDjango - ch8
TangoWithDjango - ch8TangoWithDjango - ch8
TangoWithDjango - ch8
 
Moodle Development Best Pracitces
Moodle Development Best PracitcesMoodle Development Best Pracitces
Moodle Development Best Pracitces
 
Google app-engine-with-python
Google app-engine-with-pythonGoogle app-engine-with-python
Google app-engine-with-python
 
JMP103 : Extending Your App Arsenal With OpenSocial
JMP103 : Extending Your App Arsenal With OpenSocialJMP103 : Extending Your App Arsenal With OpenSocial
JMP103 : Extending Your App Arsenal With OpenSocial
 
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocial
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocialIBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocial
IBM Connect 2014 - JMP103: Extending Your Application Arsenal With OpenSocial
 
.Net template solution architecture
.Net template solution architecture.Net template solution architecture
.Net template solution architecture
 
C# .NET Developer Portfolio
C# .NET Developer PortfolioC# .NET Developer Portfolio
C# .NET Developer Portfolio
 
Java EE Application Security With PicketLink
Java EE Application Security With PicketLinkJava EE Application Security With PicketLink
Java EE Application Security With PicketLink
 
Get things done with Yii - quickly build webapplications
Get things done with Yii - quickly build webapplicationsGet things done with Yii - quickly build webapplications
Get things done with Yii - quickly build webapplications
 
Google App Engine Developer - Day2
Google App Engine Developer - Day2Google App Engine Developer - Day2
Google App Engine Developer - Day2
 
Mastering Test Automation: How To Use Selenium Successfully
Mastering Test Automation: How To Use Selenium SuccessfullyMastering Test Automation: How To Use Selenium Successfully
Mastering Test Automation: How To Use Selenium Successfully
 
AEM Sightly Deep Dive
AEM Sightly Deep DiveAEM Sightly Deep Dive
AEM Sightly Deep Dive
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular Intermediate
 
Frontend APIs powering fast paced product iterations
Frontend APIs powering fast paced product iterationsFrontend APIs powering fast paced product iterations
Frontend APIs powering fast paced product iterations
 
Implementing a Symfony Based CMS in a Publishing Company
Implementing a Symfony Based CMS in a Publishing CompanyImplementing a Symfony Based CMS in a Publishing Company
Implementing a Symfony Based CMS in a Publishing Company
 
Android training day 4
Android training day 4Android training day 4
Android training day 4
 
Bn1038 demo pega
Bn1038 demo  pegaBn1038 demo  pega
Bn1038 demo pega
 
Your First Scala Web Application using Play 2.1
Your First Scala Web Application using Play 2.1Your First Scala Web Application using Play 2.1
Your First Scala Web Application using Play 2.1
 
Introduction to django
Introduction to djangoIntroduction to django
Introduction to django
 

Último

Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 

Último (20)

Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 

Java on Google App engine

  • 1. Using the Google App Engine with Java Michael Parker michael.g.parker@gmail.com
  • 2. App Engine Introduction ● Upload your web app to sandbox, and it's ready to go – – ● The good: little maintenance, scalable transactional storage, secure and reliable environment, standard APIs used The bad/unfamiliar: not a relational DB, sandboxed filesystem and sockets, no longrunning responses Free quota: 500MB of storage, and CPU and bandwidth for 5M pageviews per month
  • 3. Services Service Java Standard Google Infrastructure Authentication Servlet API Google Accounts Datastore JPA, JDO Bigtable Caching javax.cache memcacheg E-mail javax.mail Gmail gateway URLFetch URLConnection Caching HTTP Proxy ● Other services: – Java servlet 2.5 implementation, image manipulation, asynchronous task scheduling
  • 4. Development ● ● ● Apache Ant component to simplify common App Engine tasks Google Plugin for Eclipse Local development server simulates the sandbox restrictions, datastore, and services – LRU memcache – Disk-backed datastore – Jakarta Commons HttpClient-backed URL Fetch
  • 5. Sandboxing ● ● ● ● Can read all application files uploaded with the app; for read-write, use the datastore. No “direct” network access; use the URL fetch service for HTTP/HTTPS access No spawning new threads or processes; must use cron service Servlet requests can take up to 30s to respond before a throwing DeadlineExceededException
  • 6. Datastore with JDO ● ● ● ● JDO (JSR 243) defines annotations for Java objects, retrieving objects with queries, and interacting with a database using transactions Post-compilation "enhancement" step on compiled classes associates them with the JDO implementation The PersistenceManager is the interface to the underlying JDO implementation Datastore implementation is scalable with an emphasis on reads and queries
  • 7. Datastore Entities ● ● ● A entity has one or more properties, which are ints, floats, strings, dates, blobs, or references to other entites Each entity has a key; entities are fetched using their corresponding key, or by a query that matches its properties. Entities are schemaless; must enforce at the application level
  • 8. Annotating an Entity with JDO @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Employee { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent private String firstName; @Persistent private String lastName; @Persistent private Date hireDate; Public Employee(String firstName, String lastname, Date hireDate) { … } } /* accessors and other methods here */
  • 9. Entity Keys ● ● Unique and identified by the @PrimaryKey annotation. Keys are a kind (class name) and: – A long automatically generated by the datastore, e.g. the unique message ID for an e-mail – A string specified by the client, e.g. the username belonging to an account
  • 10. Creating Keys ● A Key instance combines the long or string fields with key representing the entity group ancestors, if any Key keyFromString = KeyFactory.createKey( Employee.class.getSimpleName(), "Alfred.Smith@example.com"); Key keyFromLong = KeyFactory.createKey( Employee.class.getSimpleName(), 52234); Key keyWithParent = new KeyFactory .Builder(Employee.class.getSimpleName(), 52234) .addChild(ExpenseReport.class.getSimpleName(), "A23Z79") .getKey();
  • 11. Atomic Storage Operations PersistenceManager pm = pmfInstance.getPersistenceManager(); Employee e = new Employee("Alfred", "Smith", new Date()); try { // Create pm.makePersistent(e); // Update Key key = KeyFactory.createKey( Employee.class.getSimpleName(), "Alfred.Smith@example.com"); Employee copy = pm.getObjectById(Employee.class, key); // Delete pm.deletePersistent(copy); } finally { pm.close(); }
  • 12. Queries ● A query specifies – – ● Zero or more conditions based on their property values – ● An entity kind Zero or more sort orders Once executed, can return all entities meeting these criteria in the given sort order, or just their keys JDO has its own query language, like SQL, with two different calling styles
  • 13. JDOQL Calling Styles ● String style: Query query = pm.newQuery("select from Employee " + "where lastName == lastNameParam " + "order by hireDate desc " + "parameters String lastNameParam") List<Employee> results = (List<Employee>) query.execute("Smith"); ● Method style: Query query = pm.newQuery(Employee.class); // select from query.setFilter("lastName == lastNameParam"); // where query.setOrdering("hireDate desc"); // order by query.declareParameters("String lastNameParam"); // parameters List<Employee> results = (List<Employee>) query.execute("Smith");
  • 14. Query Caveats ● Filters have a field name, an operator, and a value – – The operator must be in < <= == >= > – Only logical and is supported for multiple filters – ● The value must be provided by the app Cannot test inequality on multiple properties A query can specify a range of results to be returned to the application. – Datastore must retrieve and discard all results before to the starting offset
  • 15. Indexes ● ● An application has an index for each combination of kind, filter property and operator, and sort order used in a query. Given a query, the datastore identifies the index to use – ● all results for every possible query that uses an index are in consecutive rows in the table An index will sort entities first by value type, then by an order appropriate to the type. – Watch out! 38 (int) < 37.5 (float)
  • 16. Custom Indexes ● In production, a query with no suitable index will fail, but the development web server can create the configuration for an index and succeed – ● Indexes specified in datastore-indexes.xml Must specify an index to be built for queries like: – queries with multiple sort orders – queries with a sort order on keys in descending order
  • 17. Custom Indexes Code ● The XML configuration: <?xml version="1.0" encoding="utf-8"?> <datastore-indexes xmlns="http://appengine.google.com/ns/datastore-indexes/1.0" autoGenerate="true"> <datastore-index kind="Person" ancestor="false"> <property name="lastName" direction="asc" /> <property name="height" direction="desc" /> </datastore-index> </datastore-indexes> supports: select from Person where lastName = 'Smith' && height < 72 order by height desc
  • 18. Exploding Indexes ● A property value for an entity is stored in every custom index that refers to the property – ● ● The more indexes that refer to a property, the longer it takes to update a property For properties with multiple values, an index has a row for every permutation of values for every property To keep updates quick, datastore limits the number of index entries an entity can have – Insertion or update will fail with an exception
  • 19. Exploding Indexes Example ● Custom index: <?xml version="1.0" encoding="utf-8"?> <datastore-indexes> <datastore-index kind="MyModel"> <property name="x" direction="asc" /> <property name="y" direction="asc" /> </datastore-index> </datastore-indexes> ● Adding an entity: MyModel m = new MyModel(); m.setX(Arrays.asList("one", "two")); m.setY(Arrays.asList("three", "four")); pm.makePersistent(m); Built-in on x: one, two two, one Built-in on y: three, four four, three Custom index: one, two three, four one, two four, three two, one three, four two, one four, three
  • 20. Relationships ● Relationship dimensions: – – One-to-one versus one-to-many – ● Owned versus unowned Unidirectional and bidirectional Implementation of the JDO can model owned one-to-one and owned one-to-many relationships, both unidirectional and bidirectional – Unowned is possible with some manual bookkeeping, allows many-to-many
  • 21. Owned, one-to-one ● Have a parent (the owner) and a child – – ● ● ● Follows from encapsulation in code Child key uses the parent key as its entity group parent When the parent is retrieved, the child is retrieved In unidirectional case, child has Key for parent In bidirectional case, child has reference to parent – When child is retrieved, parent is retrieved
  • 22. One-to-one unidirectional code @PersistenceCapable(identityType = IdentityType.APPLICATION) public class ContactInfo /* the child */ { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; } // ... @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Employee /* the parent */ { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent private ContactInfo contactInfo; // … }
  • 23. One-to-one bidirectional code @PersistenceCapable(identityType = IdentityType.APPLICATION) public class ContactInfo /* the child */ { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; @Persistent(mappedBy = "contactInfo") private Employee employee; } ● ● // ... Note that the Key member is still present The argument to mappedBy must be the name of the child in the parent class
  • 24. One-to-many bidirectional code @PersistenceCapable(identityType = IdentityType.APPLICATION) public class ContactInfo /* the child */ { // ... @Persistent private Employee employee; // ... } @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Employee /* the parent */ { // … @Persistent(mappedBy = “employee”) private List<ContactInfo> contactInfoSets; // … } ● Note that mappedBy is on the parent class, its argument is its name in the child class
  • 25. Owned collections ● ● Can use any Set, List, or built-in collection implementation for owned one-to-many Order is preserved by storing a position property for every element – ● If an element is added or deleted, positions of subsequent elements must be updated If you do not need to preserve arbitrary order, use the @Order annotation: @Persistent @Order(extensions = @Extension(vendorName="datanucleus", key="list-ordering", value="state asc, city asc")) private List<ContactInfo> contactInfoSets = new List<ContactInfo>();
  • 26. Unowned relationships ● ● Use Key instances instead of instances or a collection of instances Easy to model any relationship, but – No referential integrity is enforced – In some cases, entities on different sides of the relationship belong to different entity groups, disallowing atomic updates
  • 27. Unowned many-to-many code @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Person { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; } @Persistent private Set<Key> favoriteFoods; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class Food { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; } @Persistent private Set<Key> foodFans;
  • 28. Relationships and Transactions Employee e = new Employee(); ContactInfo ci = new ContactInfo(); e.setContactInfo(ci); pm.makePersistent(e); ● Without a transaction, entities are created in separate atomic actions, not a single one: Transaction tx = null; try { tx = pm.currentTransaction(); tx.begin(); pm.makePersistent(e); tx.commit(); } finally { if (tx.isActive()) { tx.rollback(); } }
  • 29. Low Level Datastore API ● There is a lower-level API if you don't like the abstraction that JDO provides you – ● This is the API that the App Engine JDO implementation uses Data store operations: – get for set of keys with optional transaction – put for set of values with optional transaction – delete for set of keys with optional transaction – query preparation and execution
  • 30. Entity Groups ● ● The fundamental data unit in a transaction is the entity group; a single transaction can only manipulate data in one entity group. Each entity group is a hierarchy: – – An entity that is a parent for another entity can also have a parent. – ● An entity without a parent is a root entity. Every entity with a given root entity as an ancestor is in the same entity group. All entities in a group are stored in the same datastore node.
  • 31. Creating a Hierarchy ● ● Creating a hierarchical data model is very different from using SQL For example, given a online photo album, can define the user as the root – children can be preferences and photo albums – children of albums can be images, which can be further broken down into EXIF data and comments, etc.
  • 32. Hierarchies with JDO @PersistenceCapable(identityType = IdentityType.APPLICATION) public class AccountInfo { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Key key; } public void setKey(Key key) { this.key = key; } public static void createAccount(String customerId, String accountId) { KeyFactory.Builder keyBuilder = new KeyFactory.Builder( Customer.class.getSimpleName(), customerId); keyBuilder.addChild(AccountInfo.class.getSimpleName(), accountId); Key accountKey = keyBuilder.getKey(); return new AccountInfo(customerId, accountId); }
  • 33. Transactions ● ● When a transaction commits, all writes succeed, or else the transaction fails and must be retried. A transaction uses optimistic concurrency control: – When creating the transaction, get the time the entity group was last updated – On every read within the group, succeed if the entity group time is unchanged – On committing, or writing, succeed if the entity group time is unchanged
  • 34. Transaction Help ● With optimistic concurrency, can need to try a transaction several times; JDO throws a JDODataStore exception and gives up – ● Consider bundling the transaction logic into a Runnable or Callable, and have a helper method Make them happen quickly – Prepare keys and data outside the transaction
  • 35. Transactions with JDO for (int i = 0; i < NUM_RETRIES; i++) { pm.currentTransaction().begin(); ClubMembers members = pm.getObjectById( ClubMembers.class, "k12345"); members.incrementCounterBy(1); try { pm.currentTransaction().commit(); break; } } catch (JDOCanRetryException ex) { if (i == (NUM_RETRIES - 1)) { throw ex; } }
  • 36. Memcache ● ● Implementation of JCache (JSR 107) atop of memcache Use when you would a traditional cache: – – ● Returned data can be potentially stale – ● The data is popular or query is expensive If the cached data is unavailable, the application performs fine Entries evicted in LRU order when low on memory, or an expiration time can be provided Like the datastore, has a low-level API
  • 37. Memcache code ● Behaves like java.util.Map: String key = "key"; byte[] value = "value".getBytes(Charset.forName(“UTF-8”)); // Put the value into the cache. cache.put(key, value); // Get the value from the cache. value = (byte[]) cache.get(key); ● ● Has other familiar methods, like putAll, containsKey, size, isEmpty, remove, and clear Can set the policy when a value exists – Has “only replace” as well as “only add”
  • 38. URL Fetch ● ● Synchronous HTTP or HTTPS retrieval allowing GET, POST, PUT, HEAD, and DELETE through a HTTP/1.1-compliant proxy Can set HTTP headers on outgoing requests – ● Use Google Secure Data Connector to access intranet URLs – ● Some exceptions, e.g. Host and Referer Restricts to users signed in using an Apps account for your domain Like the memcache, has a low-level API
  • 39. URL Fetch Code ● URL.openStream() transparently uses URL fetch: URL url = new URL("http://www.example.com/atom.xml"); BufferedReader reader = new BufferedReader( new InputStreamReader(url.openStream())); ● As will URL.openConnection(): URL url = new URL("http://www.example.com/comment"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); ● URLConnection is not persistent; buffers request until the client accesses the response, and once received, closes the connection.
  • 40. Mail ● ● To not use the administrator's e-mail account as sender, create a new account and add it as the administrator for the application When mail service is called, message is enqueued and call returns immediately – ● ● Application receives no notification of whether delivery succeeded or failed Sender is the application developer or the address of the Google Accounts user Like URL fetch, has a low-level API
  • 41. Google Accounts ● Authentication with Google Accounts is optional – – ● ● Address from Apps domain, or gmail.com Allows development of admin-only site parts Not SSO from other Google applications Datastore supports storing the User object as a special value type, but don't rely on them as stable user identifiers – If a user changes his or her e-mail address, new User is different than what is stored
  • 42. Google Accounts Code UserService userService = UserServiceFactory.getUserService(); String thisURL = request.getRequestURI(); if (request.getUserPrincipal() != null) { response.getWriter().println("<p>Hello, " + request.getUserPrincipal().getName() + "! You can <a href="" + userService.createLogoutURL(thisURL) + "">sign out</a>.</p>"); } else { response.getWriter().println("<p>Please <a href="" + userService.createLoginURL(thisURL) + "">sign in</a>.</p>"); }
  • 43. Deployment Descriptor ● The web.xml in the application's WAR file under the WEB-INF/ directory defines URL to servlet mappings, which URLs require auth, and other properties – ● Part of the servlet standard, many references App Engine supports automatic compilation and URL mapping for JSPs, and the JSP Standard Tag Library
  • 44. Deployment Security ● A user role of * requires a Google Account: <security-constraint> <web-resource-collection> <url-pattern>/profile/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>*</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint>
  • 45. Application Configuration ● An appengine-web.xml file specifies additional application properties – The application identifier – The version identifier of the latest code – Static files (publically served) and resource files (application private) – System properties and environment variables – Toggling of SSL and sessions
  • 46. Scheduled Tasks ● ● No Executor service for task scheduling yet... The cron service invokes a URL at a specified time of day – – ● Scheduled tasks can access admin-only URLs Requests have the HTTP header XAppEngine-Cron: true Time in a simple English-like format: – every 5 minutes – 2nd,third mon,wed,thu of march 17:00 – every day 00:00
  • 47. Scheduled Tasks cron.xml ● Timezone is UTC (i.e. GMT) by default: <?xml version="1.0" encoding="UTF-8"?> <cronentries> <cron> <url>/recache</url> <description>Repopulate the cache every 2 minutes</description> <schedule>every 2 minutes</schedule> </cron> <cron> <url>/weeklyreport</url> <description>Mail out a weekly report</description> <schedule>every monday 08:30</schedule> <timezone>America/New_York</timezone> </cron> </cronentries>
  • 48. Service Implementation Standard Interface JDO, JPA, JCache, JavaMail Proprietary Interface com.google.appengine.api.* Language Neutral Interface ApiProxy, protocol buffers Implementation Google Servers, SDK stubs ● All calls go through ApiProxy, which in turn invokes a registered delegate
  • 49. Profiling with ApiProxy class ProfilingDelegate extends Delegate { Delegate parent; public ProfilingDelegate(Delegate parent) { this.parent = parent; } public byte[] makeSyncCall(Environment env, String pkg, String method, byte[] request) { long start = System.nanoTime(); byte[] result = parent.makeSyncCall(env, pkg, method, request); log.log(INFO, pkg + “.” + method + “: “ + System.nanoTime() - start); return result; } } ApiProxy.setDelegate(new ProfilingDelegate(ApiProxy.getDelegate()));
  • 50. Defining the Test Environment ● Implement ApiProxy.Environment to return information that appengine-web.xml returns: class TestEnvironment implements ApiProxy.Environment { public String getAppId() { return "Unit Tests"; } public String getVersionId() { return "1.0"; } public String getAuthDomain() { return "gmail.com"; } } // ...
  • 51. Creating a Test Harness ● Specify the local implementations of all services, so you do not use method stubs to a remote server: public class LocalServiceTestCase extends TestCase { @Override public void setUp() throws Exception { super.setUp(); ApiProxy.setEnvironmentForCurrentThread( new TestEnvironment()); ApiProxy.setDelegate(new ApiProxyLocalImpl(new File("."))); } } // ...
  • 52. Using the Test Harness ● Cast services to their local implementations: public void testEmailGetsSent() { ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate(); LocalMailService mailService = (LocalMailService) proxy.getService("mail"); mailService.clearSentMessages(); Bug b = new Bug(); b.setSeverity(Severity.LOW); b.setText("NullPointerException when updating phone number."); b.setOwner("max"); new BugDAO().createBug(b); } assertEquals(1, mailService.getSentMessages().size()); // ... test the content and recipient of the email