Goodbye fake storage, welcome JPA with appEngine. Notes:
1) Currently there are just 2 entities: Employee and Report. Any new entity must be added to the list in persistence.xml
2) Fixed findReportsByEmployee call.
3) A Report entity cannot refer to an Employee entity -- it instead refers to
the Employee key due to current appEngine implementation
http://code.google.com/appengine/docs/java/datastore/relationships.html#Unowned_Relationships
4) Running the project requires GPE because the appEngine jars are not present in our tools.
Review at http://gwt-code-reviews.appspot.com/352801
Review by: rjrjr@google.com
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7929 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/eclipse.README b/bikeshed/eclipse.README
index 952072b..5425a63 100644
--- a/bikeshed/eclipse.README
+++ b/bikeshed/eclipse.README
@@ -22,6 +22,7 @@
bikeshed/war/stocksmobile/
bikeshed/war/tree/
bikeshed/war/validation/
+appengine-generated/
* Install the Google Plugin for Eclipse
* Create a new Java project with trunk/bikeshed/ as its existing source
@@ -31,6 +32,7 @@
* Google > App Engine > ORM
* Remove src and test
* Add server and shared from src/com/google/gwt/sample/bikeshed/stocks
+ * Add com/google/gwt/sample/expenses/server/domain
* Java Build Path > Libraries > Add Variable > GWT_TOOLS, Extend > redist/json/r2_20080312/json.jar
* Copy tools/redist/json/r2_20080312/json.jar to bikeshed/war/WEB_INF/lib
* Right click on the bikeshed project and choose Run as > Web Application. Choose from the various .html files
diff --git a/bikeshed/src/META-INF/persistence.xml b/bikeshed/src/META-INF/persistence.xml
new file mode 100644
index 0000000..f4731b4
--- /dev/null
+++ b/bikeshed/src/META-INF/persistence.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
+ http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
+
+ <persistence-unit name="transactions-optional">
+ <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
+ <class>com.google.gwt.sample.expenses.server.domain.Employee</class>
+ <class>com.google.gwt.sample.expenses.server.domain.Report</class>
+ <exclude-unlisted-classes>true</exclude-unlisted-classes>
+ <properties>
+ <property name="datanucleus.NontransactionalRead" value="true"/>
+ <property name="datanucleus.NontransactionalWrite" value="true"/>
+ <property name="datanucleus.ConnectionURL" value="appengine"/>
+ </properties>
+ </persistence-unit>
+
+</persistence>
diff --git a/bikeshed/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java b/bikeshed/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
index 2d8d929..d65a0e1 100644
--- a/bikeshed/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
+++ b/bikeshed/src/com/google/gwt/requestfactory/server/RequestFactoryServlet.java
@@ -77,6 +77,8 @@
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
+ initDb(); // temporary place-holder
+
RequestDefinition operation = null;
try {
response.setStatus(HttpServletResponse.SC_OK);
@@ -125,6 +127,13 @@
}
/**
+ * Allow subclass to initialize database.
+ */
+ @SuppressWarnings("unused")
+ protected void initDb() {
+ }
+
+ /**
* Allows subclass to provide hack implementation.
* <p>
* TODO real reflection based implementation.
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java
index 955d748..0ea2229 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/gwt/request/ReportRequest.java
@@ -16,7 +16,6 @@
package com.google.gwt.sample.expenses.gwt.request;
import com.google.gwt.requestfactory.shared.EntityListRequest;
-import com.google.gwt.requestfactory.shared.LongString;
import com.google.gwt.requestfactory.shared.RequestFactory;
import com.google.gwt.requestfactory.shared.ServerOperation;
import com.google.gwt.valuestore.shared.ValueRef;
@@ -37,7 +36,7 @@
}
public Class<?>[] getParameterTypes() {
- return new Class[] { java.lang.Long.class };
+ return new Class[] {java.lang.String.class};
}
public Class<? extends ValuesKey<?>> getReturnType() {
@@ -69,7 +68,7 @@
*/
@ServerOperation("FIND_REPORTS_BY_EMPLOYEE")
EntityListRequest<ReportKey> findReportsByEmployee(
- @LongString ValueRef<EmployeeKey, String> id);
+ ValueRef<EmployeeKey, String> id);
/**
* @return a request object
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java b/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java
index cc6aa0b..7002766 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/ExpensesDataServlet.java
@@ -1,12 +1,12 @@
/*
* Copyright 2010 Google Inc.
- *
+ *
* 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
@@ -17,14 +17,15 @@
import com.google.gwt.requestfactory.server.RequestFactoryServlet;
import com.google.gwt.sample.expenses.gwt.request.ReportKey;
+import com.google.gwt.sample.expenses.server.domain.Employee;
import com.google.gwt.sample.expenses.server.domain.Report;
-import com.google.gwt.sample.expenses.server.domain.Storage;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.PrintWriter;
+import java.util.Date;
/**
* Dwindling interim servlet that calls our mock storage backend directly
@@ -33,6 +34,59 @@
public class ExpensesDataServlet extends RequestFactoryServlet {
@Override
+ protected void initDb() {
+ long size = Employee.countEmployees();
+ if (size > 1) {
+ return;
+ }
+ // initialize
+ Employee abc = new Employee();
+ abc.setUserName("abc");
+ abc.setDisplayName("Able B. Charlie");
+ abc.persist();
+
+ Employee def = new Employee();
+ def.setUserName("def");
+ def.setDisplayName("Delta E. Foxtrot");
+ def.setSupervisorKey(abc.getId());
+ def.persist();
+
+ Employee ghi = new Employee();
+ ghi.setUserName("ghi");
+ ghi.setDisplayName("George H. Indigo");
+ ghi.setSupervisorKey(abc.getId());
+ ghi.persist();
+
+ for (String purpose : new String[] {
+ "Spending lots of money", "Team building diamond cutting offsite",
+ "Visit to Istanbul"}) {
+ Report report = new Report();
+ report.setReporterKey(abc.getId());
+ report.setCreated(new Date());
+ report.setPurpose(purpose);
+ report.persist();
+ }
+
+ for (String purpose : new String[] {"Money laundering", "Donut day"}) {
+ Report report = new Report();
+ report.setCreated(new Date());
+ report.setReporterKey(def.getId());
+ report.setPurpose(purpose);
+ report.persist();
+ }
+
+ for (String purpose : new String[] {
+ "ISDN modem for telecommuting", "Sushi offsite",
+ "Baseball card research", "Potato chip cooking offsite"}) {
+ Report report = new Report();
+ report.setCreated(new Date());
+ report.setReporterKey(ghi.getId());
+ report.setPurpose(purpose);
+ report.persist();
+ }
+ }
+
+ @Override
protected void sync(String content, PrintWriter writer) {
try {
@@ -42,7 +96,7 @@
JSONObject report = reportArray.getJSONObject(0);
Report r = Report.findReport(report.getLong(ReportKey.get().getId().getName()));
r.setPurpose(report.getString(ReportKey.get().getPurpose().getName()));
- r = Storage.INSTANCE.persist(r);
+ r.persist();
report.put(ReportKey.get().getVersion().getName(), r.getVersion());
JSONArray returnArray = new JSONArray();
// TODO: don't echo back everything.
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/CreationVisitor.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/CreationVisitor.java
deleted file mode 100644
index 9f15d15..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/CreationVisitor.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-/**
- * Creates a new entity of the type of the receiver.
- *
- * @param <E> The type of entity to create\
- */
-class CreationVisitor<E extends Entity> implements EntityVisitor<E> {
- private final long id;
- private final int version;
-
- /**
- * @param entity whose id and version will be copied. Used to create empty
- * delta.
- */
- public CreationVisitor(E entity) {
- id = entity.getId();
- version = entity.getVersion();
- }
-
- /**
- * @param id of the new Entity
- * @param i
- */
- public CreationVisitor(long id, int version) {
- this.id = id;
- this.version = version;
- }
-
- @SuppressWarnings("unchecked")
- public E visit(Currency currency) {
- return (E) new Currency(id, version);
- }
-
- @SuppressWarnings("unchecked")
- public E visit(Employee employee) {
- return (E) new Employee(id, version);
- }
-
- @SuppressWarnings("unchecked")
- public E visit(Report report) {
- return (E) new Report(id, version);
- }
-
- @SuppressWarnings("unchecked")
- public E visit(ReportItem reportItem) {
- return (E) new ReportItem(id, version);
- }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Currency.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Currency.java
deleted file mode 100644
index 295937c..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Currency.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-/**
- * Models a type of currency.
- */
-// @javax.persistence.Entity
-public class Currency implements Entity {
-// @javax.validation.constraints.Size(min = 3, max = 3)
- private String code;
-
-// @javax.validation.constraints.Size(min = 2, max = 30)
- private String name;
-
- private final Long id;
-
- private final Integer version;
-
- public Currency() {
- id = null;
- version = null;
- }
-
- Currency(Long id, Integer version) {
- this.id = id;
- this.version = version;
- }
-
- public <T> T accept(EntityVisitor<T> visitor) {
- return visitor.visit(this);
- }
-
- /**
- * @return the code
- */
- public String getCode() {
- return code;
- }
-
- /**
- * @return the id
- */
- public Long getId() {
- return id;
- }
-
- /**
- * @return the name
- */
- public String getName() {
- return name;
- }
-
- /**
- * @return the version
- */
- public Integer getVersion() {
- return version;
- }
-
- /**
- * @param code the code to set
- */
- public void setCode(String code) {
- this.code = code;
- }
-
- /**
- * @param name the name to set
- */
- public void setName(String name) {
- this.name = name;
- }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Status.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/EMF.java
similarity index 61%
rename from bikeshed/src/com/google/gwt/sample/expenses/server/domain/Status.java
rename to bikeshed/src/com/google/gwt/sample/expenses/server/domain/EMF.java
index 1d974ce..96f97f3 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Status.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/EMF.java
@@ -15,14 +15,22 @@
*/
package com.google.gwt.sample.expenses.server.domain;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+
/**
- * Expense report life cycle states.
+ * Factory for creating EntityManager.
*/
-public enum Status {
- Draft, // -> submitted
- Submitted, // -> approved_manager -> declined
- Approved_Manager, // -> approved_accounting
- Approved_Accounting, // -> paid -> declined
- Paid, // -> finish
- Declined // -> draft
+public final class EMF {
+ private static final EntityManagerFactory emfInstance =
+ Persistence.createEntityManagerFactory("transactions-optional");
+
+ public static EntityManagerFactory get() {
+ return emfInstance;
+ }
+
+ private EMF() {
+ // nothing
+ }
}
+
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java
index 7d60262..0be04aa 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Employee.java
@@ -1,12 +1,12 @@
/*
* Copyright 2010 Google Inc.
- *
+ *
* 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
@@ -15,100 +15,169 @@
*/
package com.google.gwt.sample.expenses.server.domain;
+import org.datanucleus.jpa.annotations.Extension;
+
import java.util.List;
-// @javax.persistence.Entity
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.Version;
+
/**
* The Employee domain object.
*/
-public class Employee implements Entity {
+@Entity
+public class Employee {
+
+ public static long countEmployees() {
+ EntityManager em = entityManager();
+ try {
+ return ((Integer) em.createQuery("select count(o) from Employee o").getSingleResult()).intValue();
+ } finally {
+ em.close();
+ }
+ }
+
+ public static final EntityManager entityManager() {
+ return EMF.get().createEntityManager();
+ }
+
+ @SuppressWarnings("unchecked")
public static List<Employee> findAllEmployees() {
- return Storage.INSTANCE.findAllEmployees();
+ EntityManager em = entityManager();
+ try {
+ List<Employee> list = em.createQuery("select o from Employee o").getResultList();
+ // force to get all the employees
+ list.size();
+ return list;
+ } finally {
+ em.close();
+ }
}
public static Employee findEmployee(Long id) {
- return Storage.INSTANCE.findEmployee(id);
+ if (id == null) {
+ return null;
+ }
+ EntityManager em = entityManager();
+ try {
+ return em.find(Employee.class, id);
+ } finally {
+ em.close();
+ }
}
- public static Employee findEmployeeByUserName(String userName) {
- return Storage.INSTANCE.findEmployeeByUserName(userName);
+ @SuppressWarnings("unchecked")
+ public static List<Employee> findEmployeeEntries(int firstResult,
+ int maxResults) {
+ EntityManager em = entityManager();
+ try {
+ return em.createQuery("select o from Employee o").setFirstResult(
+ firstResult).setMaxResults(maxResults).getResultList();
+ } finally {
+ em.close();
+ }
}
- private final Long id;
-
- private final Integer version;
-
-// @javax.validation.constraints.Size(min = 2, max = 30)
private String userName;
-// @javax.validation.constraints.Size(min = 2, max = 30)
private String displayName;
- // @javax.persistence.ManyToOne(targetEntity =
- // com.google.io.expenses.server.domain.Employee.class)
- // @javax.persistence.JoinColumn
- private Employee supervisor;
+ private String password;
- public Employee() {
- this(null, null);
- }
+ @JoinColumn
+ private String supervisorKey;
- Employee(Long id, Integer version) {
- this.id = id;
- this.version = version;
- }
+ @Id
+ @Column(name = "id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
+ private String id;
- public <T> T accept(EntityVisitor<T> visitor) {
- return visitor.visit(this);
- }
+ @Version
+ @Column(name = "version")
+ private Long version;
public String getDisplayName() {
- return displayName;
+ return this.displayName;
}
- /**
- * @return the id
- */
- public Long getId() {
- return id;
+ public String getId() {
+ return this.id;
}
- /**
- * @return the supervisor
- */
- public Employee getSupervisor() {
- return supervisor;
+ public String getPassword() {
+ return this.password;
}
- /**
- * @return the userName
- */
+ public String getSupervisor() {
+ return supervisorKey;
+ }
+
public String getUserName() {
- return userName;
+ return this.userName;
}
- /**
- * @return the version
- */
- public Integer getVersion() {
- return version;
+ public Long getVersion() {
+ return this.version;
+ }
+
+ public void persist() {
+ EntityManager em = entityManager();
+ try {
+ em.persist(this);
+ } finally {
+ em.close();
+ }
+ }
+
+ public void remove() {
+ EntityManager em = entityManager();
+ try {
+ Employee attached = em.find(Employee.class, this.id);
+ em.remove(attached);
+ } finally {
+ em.close();
+ }
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
- /**
- * @param supervisor the supervisor to set
- */
- public void setSupervisor(Employee supervisor) {
- this.supervisor = supervisor;
+ public void setId(String id) {
+ this.id = id;
}
- /**
- * @param userName the userName to set
- */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public void setSupervisorKey(String supervisorKey) {
+ this.supervisorKey = supervisorKey;
+ }
+
public void setUserName(String userName) {
this.userName = userName;
}
+
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Id: ").append(getId()).append(", ");
+ sb.append("Version: ").append(getVersion()).append(", ");
+ sb.append("UserName: ").append(getUserName()).append(", ");
+ sb.append("DisplayName: ").append(getDisplayName()).append(", ");
+ sb.append("Password: ").append(getPassword()).append(", ");
+ return sb.toString();
+ }
+
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Entity.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Entity.java
deleted file mode 100644
index 2d460a3..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Entity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-/**
- * Pale imitation on the stuff JPA entities get baked into them.
- */
-interface Entity {
- /**
- * Double dispatch mechanism to ease imitation of framework mechanisms.
- */
- <T> T accept(EntityVisitor<T> visitor);
-
- Long getId();
-
- Integer getVersion();
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/EntityVisitor.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/EntityVisitor.java
deleted file mode 100644
index 63d6e70..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/EntityVisitor.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-/**
- * Double dispatch mechanism to ease imitation of framework mechanisms.
- */
-interface EntityVisitor<T> {
- T visit(Currency currency);
-
- T visit(Employee employee);
-
- T visit(Report report);
-
- T visit(ReportItem reportItem);
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/NullFieldFiller.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/NullFieldFiller.java
deleted file mode 100644
index eca4607..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/NullFieldFiller.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-/**
- * Does the merging that a persistence framework would have done for us. If
- * persistence frameworks did this "null field means no change" kind of thing.
- * Which seems unlikely. But it was fun to write. D'oh.
- * <p>
- * Class cast exceptions thrown on merge type mismatch.
- */
-final class NullFieldFiller implements EntityVisitor<Void> {
- private final Entity sparseEntity;
-
- /**
- * @param sparseEntity any null fields on this object will be filled from the
- * fields of the Entity that accepts this NullFieldFiller
- */
- NullFieldFiller(Entity sparseEntity) {
- this.sparseEntity = sparseEntity;
- }
-
- public Void visit(Currency currency) {
- Currency sparse = ((Currency) sparseEntity);
- if (null == sparse.getCode()) {
- sparse.setCode(currency.getCode());
- }
- if (null == sparse.getName()) {
- sparse.setName(currency.getName());
- }
- return null;
- }
-
- public Void visit(Employee employee) {
- Employee sparse = ((Employee) sparseEntity);
- if (null == sparse.getUserName()) {
- sparse.setUserName(employee.getUserName());
- }
- if (null == sparse.getSupervisor()) {
- sparse.setSupervisor(employee.getSupervisor());
- }
- if (null == sparse.getDisplayName()) {
- sparse.setDisplayName(employee.getDisplayName());
- }
- return null;
- }
-
- public Void visit(Report report) {
- Report sparse = ((Report) sparseEntity);
- if (sparse.getApprovedSupervisor() == null) {
- sparse.setApprovedSupervisor(report.getApprovedSupervisor());
- }
- if (sparse.getCreated() == null) {
- sparse.setCreated(report.getCreated());
- }
- if (sparse.getPurpose() == null) {
- sparse.setPurpose(report.getPurpose());
- }
- if (sparse.getReporter() == null) {
- sparse.setReporter(report.getReporter());
- }
- if (sparse.getStatus() == null) {
- sparse.setStatus(report.getStatus());
- }
- return null;
- }
-
- public Void visit(ReportItem reportItem) {
- ReportItem sparse = ((ReportItem) sparseEntity);
- if (null == sparse.getAmount()) {
- sparse.setAmount(reportItem.getAmount());
- }
- if (null == sparse.getCurrency()) {
- sparse.setCurrency(reportItem.getCurrency());
- }
- if (null == sparse.getIncurred()) {
- sparse.setIncurred(reportItem.getIncurred());
- }
- if (null == sparse.getPurpose()) {
- sparse.setPurpose(reportItem.getPurpose());
- }
- if (null == sparse.getReport()) {
- sparse.setReport(reportItem.getReport());
- }
- return null;
- }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/RelationshipRefreshingVisitor.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/RelationshipRefreshingVisitor.java
deleted file mode 100644
index 2487b0c..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/RelationshipRefreshingVisitor.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-/**
- * Used by {@link Storage#get(Entity)} to refreshes fields that point to other
- * entities.
- */
-class RelationshipRefreshingVisitor implements EntityVisitor<Void> {
- private final Storage s;
-
- public RelationshipRefreshingVisitor(Storage s) {
- this.s = s;
- }
-
- public Void visit(Currency currency) {
- return null;
- }
-
- public Void visit(Employee employee) {
- employee.setSupervisor(s.get(employee.getSupervisor()));
- return null;
- }
-
- public Void visit(Report report) {
- report.setApprovedSupervisor(s.get(report.getApprovedSupervisor()));
- report.setReporter(s.get(report.getReporter()));
- return null;
- }
-
- public Void visit(ReportItem reportItem) {
- reportItem.setReport(s.get(reportItem.getReport()));
- return null;
- }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/RelationshipValidationVisitor.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/RelationshipValidationVisitor.java
deleted file mode 100644
index 7cd8a55..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/RelationshipValidationVisitor.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-/**
- * Used by {@link Storage#persist(Entity)} to ensure relationships are valid
- * (can't point to an Entity with no id).
- */
-public class RelationshipValidationVisitor implements EntityVisitor<Void> {
-
- public Void visit(Currency currency) {
- return null;
- }
-
- public Void visit(Employee employee) {
- validate(employee, employee.getSupervisor());
- return null;
- }
-
- public Void visit(Report report) {
- validate(report, report.getApprovedSupervisor());
- validate(report, report.getReporter());
- return null;
- }
-
- public Void visit(ReportItem reportItem) {
- validate(reportItem, reportItem.getReport());
- return null;
- }
-
- /**
- * @param supervisor
- */
- private void validate(Entity from, Entity to) {
- if ((to != null) && (to.getId() == null)) {
- throw new IllegalArgumentException(String.format(
- "Attempt to point from %s " + "to invalid (null id) entity %s", from,
- to));
- }
- }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java
index c7ceedc..229b3d5 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java
@@ -15,149 +15,194 @@
*/
package com.google.gwt.sample.expenses.server.domain;
+import org.datanucleus.jpa.annotations.Extension;
+
import java.util.Date;
import java.util.List;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EntityManager;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.Query;
+import javax.persistence.Version;
+
/**
* Models an expense report.
*/
-// @javax.persistence.Entity
-public class Report implements Entity {
+@Entity
+public class Report {
+ public static long countReports() {
+ EntityManager em = entityManager();
+ try {
+ return ((Integer) em.createQuery("select count(o) from Report o").getSingleResult()).intValue();
+ } finally {
+ em.close();
+ }
+ }
+
+ public static final EntityManager entityManager() {
+ return EMF.get().createEntityManager();
+ }
+
+ @SuppressWarnings("unchecked")
public static List<Report> findAllReports() {
- return Storage.INSTANCE.findAllReports();
+ EntityManager em = entityManager();
+ try {
+ List<Report> reportList = em.createQuery("select o from Report o").getResultList();
+ // force it to materialize
+ reportList.size();
+ return reportList;
+ } finally {
+ em.close();
+ }
}
public static Report findReport(Long id) {
- return Storage.INSTANCE.findReport(id);
+ if (id == null) {
+ return null;
+ }
+ EntityManager em = entityManager();
+ try {
+ return em.find(Report.class, id);
+ } finally {
+ em.close();
+ }
}
- public static List<Report> findReportsByEmployee(Long id) {
- return Storage.INSTANCE.findReportsByEmployee(id);
+ @SuppressWarnings("unchecked")
+ public static List<Report> findReportEntries(int firstResult, int maxResults) {
+ EntityManager em = entityManager();
+ try {
+ List<Report> reportList = em.createQuery("select o from Report o").setFirstResult(
+ firstResult).setMaxResults(maxResults).getResultList();
+ // force it to materialize
+ reportList.size();
+ return reportList;
+ } finally {
+ em.close();
+ }
}
- private final Long id;
-private final Integer version;
+ public static List<Report> findReportsByEmployee(String employeeId) {
+ EntityManager em = entityManager();
+ try {
+ Query query = em.createQuery("select o from Report o where o.reporterKey =:reporterKey");
+ query.setParameter("reporterKey", employeeId);
+ List<Report> reportList = query.getResultList();
+ // force it to materialize
+ reportList.size();
+ return reportList;
+ } finally {
+ em.close();
+ }
+ }
-// @javax.validation.constraints.NotNull
-// @javax.validation.constraints.Past
- // @javax.persistence.Temporal(javax.persistence.TemporalType.TIMESTAMP)
- private java.util.Date created;
+ @Id
+ @Column(name = "id")
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Extension(vendorName = "datanucleus", key = "gae.encoded-pk", value = "true")
+ private String id;
-// @javax.validation.constraints.NotNull
- // @javax.persistence.Enumerated
- private Status status;
+ @Version
+ @Column(name = "version")
+ private Long version;
-// @javax.validation.constraints.NotNull
- // @javax.persistence.ManyToOne(targetEntity =
- // com.google.io.expenses.server.domain.Employee.class)
- // @javax.persistence.JoinColumn
- private Employee reporter;
+ private Date created;
- // @javax.persistence.OneToMany(cascade = javax.persistence.CascadeType.ALL,
- // mappedBy = "report")
- // private Set<ReportItem> items = new HashSet<ReportItem>();
-
- // @javax.validation.constraints.Size(min = 3, max = 100)
private String purpose;
- // @javax.persistence.ManyToOne(targetEntity =
- // com.google.io.expenses.server.domain.Employee.class)
- // @javax.persistence.JoinColumn
- private Employee approvedSupervisor;
-
- public Report() {
- id = null;
- version = null;
- }
-
- Report(Long id, Integer version) {
- this.id = id;
- this.version = version;
- }
-
- public <T> T accept(EntityVisitor<T> visitor) {
- return visitor.visit(this);
- }
-
/**
- * @return the approved_supervisor
+ * Store reporter's key instead of reporter because
+ * http://code.google.com/appengine
+ * /docs/java/datastore/relationships.html#Unowned_Relationships
*/
- public Employee getApprovedSupervisor() {
- return approvedSupervisor;
+ @JoinColumn
+ @Column(name = "reporter")
+ private String reporterKey;
+
+ @JoinColumn
+ private String approvedSupervisorKey;
+
+ public String getApprovedSupervisorKey() {
+ return approvedSupervisorKey;
}
- /**
- * @return the created
- */
- public java.util.Date getCreated() {
- return created;
+ public Date getCreated() {
+ return this.created;
}
- /**
- * @return the id
- */
- public Long getId() {
- return id;
+ public String getId() {
+ return this.id;
}
- /**
- * @return the purpose
- */
public String getPurpose() {
- return purpose;
+ return this.purpose;
}
- /**
- * @return the reporter
- */
- public Employee getReporter() {
- return reporter;
+ public String getReporterKey() {
+ return this.reporterKey;
}
- /**
- * @return the status
- */
- public Status getStatus() {
- return status;
+ public Long getVersion() {
+ return this.version;
}
- /**
- * @return the version
- */
- public Integer getVersion() {
- return version;
+ public void persist() {
+ EntityManager em = entityManager();
+ try {
+ em.persist(this);
+ } finally {
+ em.close();
+ }
}
- /**
- * @param approvedSupervisor the approved_supervisor to set
- */
- public void setApprovedSupervisor(Employee approvedSupervisor) {
- this.approvedSupervisor = approvedSupervisor;
+ public void remove() {
+ EntityManager em = entityManager();
+ try {
+ Report attached = em.find(Report.class, this.id);
+ em.remove(attached);
+ } finally {
+ em.close();
+ }
}
- public void setCreated(Date date) {
- this.created = date;
+ public void setApprovedSupervisorKey(String approvedSupervisorKey) {
+ this.approvedSupervisorKey = approvedSupervisorKey;
}
- /**
- * @param purpose the purpose to set
- */
+ public void setCreated(Date created) {
+ this.created = created;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
public void setPurpose(String purpose) {
this.purpose = purpose;
}
- /**
- * @param reporter the reporter to set
- */
- public void setReporter(Employee reporter) {
- this.reporter = reporter;
+ public void setReporterKey(String reporter) {
+ this.reporterKey = reporter;
}
- /**
- * @param status the status to set
- */
- public void setStatus(Status status) {
- this.status = status;
+ public void setVersion(Long version) {
+ this.version = version;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Id: ").append(getId()).append(", ");
+ sb.append("Version: ").append(getVersion()).append(", ");
+ sb.append("Created: ").append(getCreated()).append(", ");
+ sb.append("Purpose: ").append(getPurpose()).append(", ");
+ sb.append("Reporter: ").append(getReporterKey());
+ sb.append("ApprovedSupervisor: ").append(getApprovedSupervisorKey());
+ return sb.toString();
}
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/ReportItem.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/ReportItem.java
deleted file mode 100644
index a4df603..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/ReportItem.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-import java.util.Date;
-
-/**
- * Models a line item in an expense report.
- */
-// @javax.persistence.Entity
-public class ReportItem implements Entity {
- private Long id;
-
- private Integer version;
-
-// @javax.validation.constraints.NotNull
- // @javax.persistence.ManyToOne(targetEntity = Report.class)
- // @javax.persistence.JoinColumn
- private Report report;
-
-// @javax.validation.constraints.NotNull
-// @javax.validation.constraints.Past
- // @javax.persistence.Temporal(javax.persistence.TemporalType.TIMESTAMP)
- private Date incurred;
-
-// @javax.validation.constraints.Size(min = 3, max = 100)
- private String purpose;
-
-// @javax.validation.constraints.NotNull
- // @javax.persistence.ManyToOne(targetEntity = Currency.class)
- // @javax.persistence.JoinColumn
- private Currency currency;
-
-// @javax.validation.constraints.NotNull
-// @javax.validation.constraints.Min(0L)
- private Float amount;
-
- public ReportItem() {
- id = null;
- version = null;
- }
-
- ReportItem(Long id, Integer version) {
- this.id = id;
- this.version = version;
- }
-
- public <T> T accept(EntityVisitor<T> visitor) {
- return visitor.visit(this);
- }
-
- /**
- * @return the amount
- */
- public Float getAmount() {
- return amount;
- }
-
- /**
- * @return the currency
- */
- public Currency getCurrency() {
- return currency;
- }
-
- /**
- * @return the id
- */
- public Long getId() {
- return id;
- }
-
- /**
- * @return the incurred
- */
- public Date getIncurred() {
- return incurred;
- }
-
- /**
- * @return the purpose
- */
- public String getPurpose() {
- return purpose;
- }
-
- /**
- * @return the report
- */
- public Report getReport() {
- return report;
- }
-
- /**
- * @return the version
- */
- public Integer getVersion() {
- return version;
- }
-
- /**
- * @param amount the amount to set
- */
- public void setAmount(Float amount) {
- this.amount = amount;
- }
-
- /**
- * @param currency the currency to set
- */
- public void setCurrency(Currency currency) {
- this.currency = currency;
- }
-
- /**
- * @param incurred the incurred to set
- */
- public void setIncurred(java.util.Date incurred) {
- this.incurred = incurred;
- }
-
- /**
- * @param purpose the purpose to set
- */
- public void setPurpose(String purpose) {
- this.purpose = purpose;
- }
-
- /**
- * @param report the report to set
- */
- public void setReport(Report report) {
- this.report = report;
- }
-}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Storage.java b/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Storage.java
deleted file mode 100644
index cd69d13..0000000
--- a/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Storage.java
+++ /dev/null
@@ -1,317 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Pretend pool of domain objects, trying to act more or less like persistence
- * frameworks do. For goodness sake don't imitate this for production code.
- */
-public class Storage {
- public static final Storage INSTANCE;
- static {
- INSTANCE = new Storage();
- fill(INSTANCE);
- }
-
- /**
- * @param storage to fill with demo entities
- */
- static void fill(Storage storage) {
- Employee abc = new Employee();
- abc.setUserName("abc");
- abc.setDisplayName("Able B. Charlie");
- abc = storage.persist(abc);
- abc.setSupervisor(abc);
- abc = storage.persist(abc);
-
- Employee def = new Employee();
- def.setUserName("def");
- def.setDisplayName("Delta E. Foxtrot");
- def.setSupervisor(abc);
- def = storage.persist(def);
-
- Employee ghi = new Employee();
- ghi.setUserName("ghi");
- ghi.setDisplayName("George H. Indigo");
- ghi.setSupervisor(abc);
- ghi = storage.persist(ghi);
-
- Report abc1 = new Report();
- abc1.setReporter(abc);
- abc1.setCreated(new Date());
- abc1.setPurpose("Spending lots of money");
- abc1 = storage.persist(abc1);
-
- Report abc2 = new Report();
- abc2.setReporter(abc);
- abc2.setCreated(new Date());
- abc2.setPurpose("Team building diamond cutting offsite");
- abc2 = storage.persist(abc2);
-
- Report abc3 = new Report();
- abc3.setReporter(abc);
- abc3.setCreated(new Date());
- abc3.setPurpose("Visit to Istanbul");
- storage.persist(abc3);
-
- Report def1 = new Report();
- def1.setReporter(def);
- def1.setCreated(new Date());
- def1.setPurpose("Money laundering");
- def1 = storage.persist(def1);
-
- Report def2 = new Report();
- def2.setReporter(def);
- def2.setCreated(new Date());
- def2.setPurpose("Donut day");
- storage.persist(def2);
-
- Report ghi1 = new Report();
- ghi1.setReporter(ghi);
- ghi1.setCreated(new Date());
- ghi1.setPurpose("ISDN modem for telecommuting");
- storage.persist(ghi1);
-
- Report ghi2 = new Report();
- ghi2.setReporter(ghi);
- ghi2.setCreated(new Date());
- ghi2.setPurpose("Sushi offsite");
- ghi2 = storage.persist(ghi2);
-
- Report ghi3 = new Report();
- ghi3.setReporter(ghi);
- ghi3.setCreated(new Date());
- ghi3.setPurpose("Baseball card research");
- ghi3 = storage.persist(ghi3);
-
- Report ghi4 = new Report();
- ghi4.setReporter(ghi);
- ghi4.setCreated(new Date());
- ghi4.setPurpose("Potato chip cooking offsite");
- ghi4 = storage.persist(ghi4);
- }
-
- /**
- * Useful for making a surgical update to an entity, e.g. in response to a web
- * update.
- * <p>
- * Given an entity, returns an empty copy: all fields are null except id and
- * version. When this copy is later persisted, only non-null fields will be
- * changed.
- */
- static <E extends Entity> E startSparseEdit(E v1) {
- return v1.accept(new CreationVisitor<E>(v1));
- }
-
- private final Map<Long, Entity> soup = new HashMap<Long, Entity>();
- private final Map<String, Long> employeeUserNameIndex = new HashMap<String, Long>();
- private final Map<Long, Set<Long>> reportsByEmployeeIndex = new HashMap<Long, Set<Long>>();
-
- private Map<Long, Entity> freshForCurrentGet;
- private int getDepth = 0;
- private long serial = 0;
-
- public synchronized <E extends Entity> E persist(final E delta) {
- E next = null;
- E previous = null;
-
- if (delta.getId() == null) {
- next = delta.accept(new CreationVisitor<E>(++serial, 0));
- delta.accept(new NullFieldFiller(next));
- } else {
- previous = get(delta);
- if (!previous.getVersion().equals(delta.getVersion())) {
- throw new IllegalArgumentException(String.format(
- "Version mismatch of %s. Cannot update %d from %d", delta,
- previous.getVersion(), delta.getVersion()));
- }
-
- next = previous.accept(new CreationVisitor<E>(previous.getId(),
- previous.getVersion() + 1));
-
- NullFieldFiller filler = new NullFieldFiller(next);
- // Copy the changed fields into the new version
- delta.accept(filler);
- // And copy the old fields into any null fields remaining on the new
- // version
- previous.accept(filler);
- }
-
- next.accept(new RelationshipValidationVisitor());
-
- updateIndices(previous, next);
- soup.put(next.getId(), next);
- return get(next);
- }
-
- synchronized List<Employee> findAllEmployees() {
- List<Employee> rtn = new ArrayList<Employee>();
- for (Map.Entry<String, Long> entry : employeeUserNameIndex.entrySet()) {
- rtn.add(get((Employee) rawGet(entry.getValue())));
- }
- return rtn;
- }
-
- synchronized List<Report> findAllReports() {
- List<Report> rtn = new ArrayList<Report>();
- for (Entity e : soup.values()) {
- if (e instanceof Report) {
- rtn.add(get((Report) e));
- }
- }
- return rtn;
- }
-
- /**
- * Returns Employee by id.
- * @param id
- * @return
- */
- synchronized Employee findEmployee(Long id) {
- return get((Employee) rawGet(id));
- }
-
- synchronized Employee findEmployeeByUserName(String userName) {
- Long id = employeeUserNameIndex.get(userName);
- return findEmployee(id);
- }
-
- /**
- * Returns report by id.
- * @param id
- * @return
- */
- synchronized Report findReport(Long id) {
- return get((Report) rawGet(id));
- }
-
- synchronized List<Report> findReportsByEmployee(long id) {
- Set<Long> reportIds = reportsByEmployeeIndex.get(id);
- if (reportIds == null) {
- return Collections.emptyList();
- }
- List<Report> reports = new ArrayList<Report>(reportIds.size());
- for (Long reportId : reportIds) {
- reports.add(get((Report) rawGet(reportId)));
- }
- return reports;
- }
-
- /**
- * @return An up to date copy of the given entity, safe for editing.
- */
- @SuppressWarnings("unchecked")
- // We make runtime checks that return type matches in type
- synchronized <E extends Entity> E get(final E entity) {
- if (getDepth == 0) {
- freshForCurrentGet = new HashMap<Long, Entity>();
- }
- getDepth++;
- try {
- if (entity == null) {
- return null;
- }
- Entity previous = rawGet(entity.getId());
- if (null == previous) {
- throw new IllegalArgumentException(String.format(
- "In %s, unknown id %d", entity, entity.getId()));
- }
- if (!previous.getClass().equals(entity.getClass())) {
- throw new IllegalArgumentException(String.format(
- "Type mismatch, fetched %s for %s", entity, previous));
- }
-
- Entity rtn = freshForCurrentGet.get(previous.getId());
- if (rtn == null) {
- // Make a defensive copy
- rtn = copy(previous);
- freshForCurrentGet.put(previous.getId(), rtn);
- // Make sure it has fresh copies of related entities
- rtn.accept(new RelationshipRefreshingVisitor(this));
- }
- return (E) rtn;
- } finally {
- getDepth--;
- if (getDepth == 0) {
- freshForCurrentGet = null;
- }
- }
- }
-
- /**
- * @param original Entity to copy
- * @return copy of original
- */
- private Entity copy(Entity original) {
- Entity copy = original.accept(new CreationVisitor<Entity>(original));
- original.accept(new NullFieldFiller(copy));
- return copy;
- }
-
- private synchronized Entity rawGet(Long id) {
- return soup.get(id);
- }
-
- private void updateIndices(final Entity previous, final Entity next) {
- next.accept(new EntityVisitor<Void>() {
- public Void visit(Currency currency) {
- return null;
- }
-
- public Void visit(Employee employee) {
- if (null == employee.getUserName()) {
- return null;
- }
- if (previous != null) {
- Employee prevEmployee = (Employee) previous;
- if (!prevEmployee.getUserName().equals(next)) {
- employeeUserNameIndex.remove(prevEmployee.getUserName());
- }
- }
- employeeUserNameIndex.put(employee.getUserName(), employee.getId());
- return null;
- }
-
- public Void visit(Report report) {
- Employee reporter = report.getReporter();
- if (reporter == null) {
- return null;
- }
- Long employeeId = reporter.getId();
- Set<Long> reportIds = reportsByEmployeeIndex.get(employeeId);
- if (reportIds == null) {
- reportIds = new LinkedHashSet<Long>();
- reportsByEmployeeIndex.put(employeeId, reportIds);
- }
- reportIds.add(report.getId());
- return null;
- }
-
- public Void visit(ReportItem reportItem) {
- return null;
- }
- });
- }
-}
diff --git a/bikeshed/test/com/google/gwt/sample/expenses/server/domain/CreationVisitorTest.java b/bikeshed/test/com/google/gwt/sample/expenses/server/domain/CreationVisitorTest.java
deleted file mode 100644
index 197f202..0000000
--- a/bikeshed/test/com/google/gwt/sample/expenses/server/domain/CreationVisitorTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-import junit.framework.TestCase;
-
-/**
- * Eponymous test class.
- */
-public class CreationVisitorTest extends TestCase {
- EntityTester tester = new EntityTester();
-
- private void doCreationTest(Entity entity) {
- Entity created = entity.accept(new CreationVisitor<Entity>(1, 2));
- assertEquals(entity.getClass(), created.getClass());
- assertEquals(Long.valueOf(1), created.getId());
- assertEquals(Integer.valueOf(2), created.getVersion());
- }
-
- public void testSimple() {
- tester.run(new EntityVisitor<Boolean>() {
-
- public Boolean visit(Currency currency) {
- doCreationTest(currency);
- return null;
- }
-
- public Boolean visit(Employee employee) {
- doCreationTest(employee);
- return null;
- }
-
- public Boolean visit(Report report) {
- doCreationTest(report);
- return null;
- }
-
- public Boolean visit(ReportItem reportItem) {
- doCreationTest(reportItem);
- return null;
- }
- });
- }
-}
diff --git a/bikeshed/test/com/google/gwt/sample/expenses/server/domain/EntityTester.java b/bikeshed/test/com/google/gwt/sample/expenses/server/domain/EntityTester.java
deleted file mode 100644
index b6c03cc..0000000
--- a/bikeshed/test/com/google/gwt/sample/expenses/server/domain/EntityTester.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-class EntityTester {
- Currency currency;
- Employee employee;
- Report report;
- ReportItem reportItem;
-
- final Entity[] all;
-
- EntityTester() {
- currency = new Currency();
- employee = new Employee();
- report = new Report();
- reportItem = new ReportItem();
-
- all = new Entity[] {currency, employee, report, reportItem};
- }
-
- void run(EntityVisitor<?> v) {
- for (Entity e : all) {
- e.accept(v);
- }
- }
-}
\ No newline at end of file
diff --git a/bikeshed/test/com/google/gwt/sample/expenses/server/domain/NullFieldFillerTest.java b/bikeshed/test/com/google/gwt/sample/expenses/server/domain/NullFieldFillerTest.java
deleted file mode 100644
index 0b94645..0000000
--- a/bikeshed/test/com/google/gwt/sample/expenses/server/domain/NullFieldFillerTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-import junit.framework.TestCase;
-
-import java.util.Date;
-
-/**
- * Eponymous test class.
- */
-public class NullFieldFillerTest extends TestCase {
- public void testEmpty() {
- final EntityTester tester = new EntityTester();
- tester.run(new EntityVisitor<Void>() {
-
- public Void visit(Currency currency) {
- Currency full = new Currency(1L, 2);
- full.setCode("USD");
- full.setName("U.S. Dollars");
-
- doFillAndVerify(currency, full);
-
- assertEquals("USD", currency.getCode());
- assertEquals("U.S. Dollars", currency.getName());
- return null;
- }
-
- public Void visit(Employee employee) {
- Employee full = new Employee(1L, 2);
- full.setUserName("ldap");
- full.setDisplayName("Lawrence D. A. Pimrose");
-
- doFillAndVerify(employee, full);
-
- assertEquals("ldap", employee.getUserName());
- assertEquals("Lawrence D. A. Pimrose", employee.getDisplayName());
- return null;
- }
-
- public Void visit(Report report) {
- Report full = new Report(1L, 2);
- full.setApprovedSupervisor(tester.employee);
- full.setPurpose("purpose");
- full.setReporter(tester.employee);
- full.setStatus(Status.Paid);
- full.setCreated(new Date(1234567890));
-
- doFillAndVerify(report, full);
-
- assertSame(tester.employee, report.getApprovedSupervisor());
- assertEquals("purpose", report.getPurpose());
- assertSame(tester.employee, report.getReporter());
- assertEquals(Status.Paid, report.getStatus());
- assertEquals(new Date(1234567890), report.getCreated());
-
- return null;
- }
-
- public Void visit(ReportItem reportItem) {
- ReportItem full = new ReportItem(1L, 2);
- full.setAmount(123.45f);
- full.setCurrency(tester.currency);
- full.setIncurred(new Date(1234567890));
- full.setPurpose("purpose");
- full.setReport(tester.report);
-
- doFillAndVerify(reportItem, full);
-
- assertEquals(123.45f, reportItem.getAmount());
- assertEquals(tester.currency, reportItem.getCurrency());
- assertEquals(new Date(1234567890), reportItem.getIncurred());
- assertEquals("purpose", reportItem.getPurpose());
- assertEquals(tester.report, reportItem.getReport());
-
- return null;
- }
- });
- }
-
- private void assertNullEntityTag(Entity employee) {
- assertNull(employee.getId());
- assertNull(employee.getVersion());
- }
-
- private void doFillAndVerify(Entity sparse, Entity full) {
- full.accept(new NullFieldFiller(sparse));
- assertNullEntityTag(sparse);
- }
-}
diff --git a/bikeshed/test/com/google/gwt/sample/expenses/server/domain/StorageTest.java b/bikeshed/test/com/google/gwt/sample/expenses/server/domain/StorageTest.java
deleted file mode 100644
index 7d97cc4..0000000
--- a/bikeshed/test/com/google/gwt/sample/expenses/server/domain/StorageTest.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.google.gwt.sample.expenses.server.domain;
-
-import junit.framework.TestCase;
-
-import java.util.List;
-
-/**
- * Eponymous unit test.
- */
-public class StorageTest extends TestCase {
- Storage store = new Storage();
-
- public void testFreshRelationships() {
- Storage s = new Storage();
- Storage.fill(s);
-
- Employee abc = s.findEmployeeByUserName("abc");
- List<Report> reports = s.findReportsByEmployee(abc.getId());
- for (Report report : reports) {
- assertEquals(abc.getVersion(), report.getReporter().getVersion());
- }
-
- abc.setDisplayName("Herbert");
- s.persist(abc);
- List<Report> fresherReports = s.findReportsByEmployee(abc.getId());
- assertEquals(reports.size(), fresherReports.size());
- Integer expectedVersion = abc.getVersion() + 1;
- for (Report report : fresherReports) {
- assertEquals(abc.getId(), report.getReporter().getId());
- assertEquals(expectedVersion, report.getReporter().getVersion());
- assertEquals("Herbert", report.getReporter().getDisplayName());
- }
- }
-
- public void testReportsByEmployeeIndex() {
- Storage s = new Storage();
- Storage.fill(s);
-
- Employee abc = s.findEmployeeByUserName("abc");
- List<Report> reports = s.findReportsByEmployee(abc.getId());
- assertEquals(3, reports.size());
-
- Report report = new Report();
- report.setReporter(abc);
- report = s.persist(report);
-
- reports = s.findReportsByEmployee(abc.getId());
- assertEquals(4, reports.size());
- Report latestReport = reports.get(3);
- assertEquals(report.getId(), latestReport.getId());
- assertEquals(report.getVersion(), latestReport.getVersion());
- }
-
- public void testUserNameIndex() {
- Storage s = new Storage();
- Storage.fill(s);
-
- Employee abc = s.findEmployeeByUserName("abc");
- assertEquals("Able B. Charlie", abc.getDisplayName());
- abc = Storage.startSparseEdit(abc);
- abc.setUserName("xyz");
- abc = s.persist(abc);
-
- assertNull(s.findEmployeeByUserName("abc"));
- Employee xyz = s.findEmployeeByUserName("xyz");
- assertEquals("Able B. Charlie", xyz.getDisplayName());
- assertEquals(abc.getVersion(), xyz.getVersion());
- }
-
- public void testVersioning() {
- final EntityTester tester = new EntityTester();
-
- tester.run(new EntityVisitor<Boolean>() {
-
- public Boolean visit(Currency currency) {
- doTestSparseEdit(doTestNew(currency));
- return null;
- }
-
- public Boolean visit(Employee employee) {
- doTestSparseEdit(doTestNew(employee));
- return null;
- }
-
- public Boolean visit(Report report) {
- doTestSparseEdit(doTestNew(report));
- return null;
- }
-
- public Boolean visit(ReportItem reportItem) {
- doTestFullEdit(doTestSparseEdit(doTestNew(reportItem)));
- return null;
- }
- });
- }
-
- private void doTestFullEdit(Entity v1) {
- v1 = store.get(v1);
- Entity v2 = store.persist(v1);
- assertEquals(v1.getId(), v2.getId());
- assertEquals(Integer.valueOf(v1.getVersion() + 1), v2.getVersion());
- Entity anotherV2 = store.get(v2);
- assertNotSame(v2, anotherV2);
- assertEquals(v1.getId(), anotherV2.getId());
- assertEquals(v2.getVersion(), anotherV2.getVersion());
- }
-
- private Entity doTestNew(Entity e) {
- Entity v1 = store.persist(e);
- assertEquals(Integer.valueOf(0), v1.getVersion());
- assertNotNull(v1.getId());
- assertNotSame(v1, store.get(Storage.startSparseEdit(v1)));
- return v1;
- }
-
- private Entity doTestSparseEdit(Entity v1) {
- Entity delta = Storage.startSparseEdit(v1);
- Entity v2 = store.persist(delta);
- assertEquals(v1.getId(), v2.getId());
- assertEquals(Integer.valueOf(v1.getVersion() + 1), v2.getVersion());
- Entity anotherV2 = store.get(v2);
- assertNotSame(v2, anotherV2);
- assertEquals(v1.getId(), anotherV2.getId());
- assertEquals(v2.getVersion(), anotherV2.getVersion());
- return anotherV2;
- }
-}