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; - } -}