blob: 84795ec984fd4ec4e574f85ffab7eb3a2662c60a [file] [log] [blame]
/*
* 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 com.google.apphosting.api.DeadlineExceededException;
import java.util.logging.Logger;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
/**
* Stores the current entity counts.
*/
@Entity
public class EntityCounter {
private static final Logger log = Logger.getLogger(EntityCounter.class.getName());
private static final int KIND_EMPLOYEE = 0;
private static final int KIND_EXPENSE = 1;
private static final String[] KIND_NAMES = {"Employee", "Expense", "Report"};
private static final int KIND_REPORT = 2;
private static final Long ONE = Long.valueOf(1L);
private static final Long ZERO = Long.valueOf(0L);
private static final boolean DENSE_IDS = false;
public static final EntityManager entityManager() {
return EMF.get().createEntityManager();
}
public static long getEmployeeCount() {
EntityCounter counter = getCounter();
Long l = counter.getNumEmployees();
return l == null ? 0 : l.longValue();
}
public static long getExpenseCount() {
EntityCounter counter = getCounter();
Long l = counter.getNumExpenses();
return l == null ? 0 : l.longValue();
}
public static long getReportCount() {
EntityCounter counter = getCounter();
Long l = counter.getNumReports();
return l == null ? 0 : l.longValue();
}
public static void reset() {
EntityCounter counter = getCounter();
counter.clear();
EntityManager em = entityManager();
try {
em.merge(counter);
} finally {
em.close();
}
}
public static long updateEmployeeCount() {
return update(KIND_EMPLOYEE);
}
public static long updateExpenseCount() {
return update(KIND_EXPENSE);
}
public static long updateReportCount() {
return update(KIND_REPORT);
}
private static void copy(EntityCounter dest, EntityCounter src) {
dest.setId(src.getId());
dest.setMaxCheckedEmployeeId(src.getMaxCheckedEmployeeId());
dest.setMaxCheckedExpenseId(src.getMaxCheckedExpenseId());
dest.setMaxCheckedReportId(src.getMaxCheckedReportId());
dest.setNumEmployees(src.getNumEmployees());
dest.setNumExpenses(src.getNumExpenses());
dest.setNumReports(src.getNumReports());
}
private static EntityCounter getCounter() {
EntityManager em = entityManager();
try {
EntityCounter counter = em.find(EntityCounter.class, 1);
if (counter == null) {
counter = new EntityCounter();
counter.clear();
em.persist(counter);
}
return counter;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
em.close();
}
}
private static long update(int kind) {
EntityManager em = entityManager();
String kindName = KIND_NAMES[kind];
log.info("Updating count for " + kindName);
EntityCounter oldCounter = getCounter();
EntityCounter counter = new EntityCounter();
copy(counter, oldCounter);
log.info("Starting at getMaxCheckedEmployeeId() = " + counter.getMaxCheckedEmployeeId());
log.info("Starting at getMaxCheckedExpenseId() = " + counter.getMaxCheckedExpenseId());
log.info("Starting at getMaxCheckedReportId() = " + counter.getMaxCheckedReportId());
log.info("Starting at getNumEmployees() = " + counter.getNumEmployees());
log.info("Starting at getNumExpenses() = " + counter.getNumExpenses());
log.info("Starting at getNumReports() = " + counter.getNumReports());
long endTime = System.currentTimeMillis() + 20000;
EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
while (System.currentTimeMillis() < endTime) {
Long min;
switch (kind) {
case KIND_EMPLOYEE:
min = counter.getMaxCheckedEmployeeId();
break;
case KIND_EXPENSE:
min = counter.getMaxCheckedExpenseId();
break;
case KIND_REPORT:
min = counter.getMaxCheckedReportId();
break;
default:
throw new RuntimeException("kind = " + kind);
}
long mmin = min == null ? 0L : min.longValue();
long mmax = mmin + 1000;
mmin = Math.max(1L, mmin);
String query = "select count(o) from " + kindName + " o where id >= "
+ mmin + " and id < " + mmax;
Number count = (Number) em.createQuery(query).getSingleResult();
long value = count.longValue();
if (value == 0 && DENSE_IDS) {
log.info("Got 0 results between " + mmin + " and " + mmax);
break;
}
mmin = mmax;
min = Long.valueOf(mmin);
switch (kind) {
case KIND_EMPLOYEE:
counter.setMaxCheckedEmployeeId(min);
Long emp = counter.getNumEmployees();
long totalEmp = (emp == null) ? value : value + emp.longValue();
counter.setNumEmployees(Long.valueOf(totalEmp));
break;
case KIND_EXPENSE:
counter.setMaxCheckedExpenseId(min);
Long exp = counter.getNumExpenses();
long totalExp = (exp == null) ? value : value + exp.longValue();
counter.setNumExpenses(Long.valueOf(totalExp));
break;
case KIND_REPORT:
counter.setMaxCheckedReportId(min);
Long rep = counter.getNumReports();
long totalRep = (rep == null) ? value : value + rep.longValue();
counter.setNumReports(Long.valueOf(totalRep));
break;
default:
throw new RuntimeException("kind = " + kind);
}
}
em.merge(counter);
transaction.commit();
transaction = null;
log.info("Ending at getMaxCheckedEmployeeId() = " + counter.getMaxCheckedEmployeeId());
log.info("Ending at getMaxCheckedExpenseId() = " + counter.getMaxCheckedExpenseId());
log.info("Ending at getMaxCheckedReportId() = " + counter.getMaxCheckedReportId());
log.info("Ending at getNumEmployees() = " + counter.getNumEmployees());
log.info("Ending at getNumExpenses() = " + counter.getNumExpenses());
log.info("Ending at getNumReports() = " + counter.getNumReports());
} catch (DeadlineExceededException e) {
if (transaction != null) {
transaction.commit();
transaction = null;
log.info("Ending at getMaxCheckedEmployeeId() = " + counter.getMaxCheckedEmployeeId());
log.info("Ending at getMaxCheckedExpenseId() = " + counter.getMaxCheckedExpenseId());
log.info("Ending at getMaxCheckedReportId() = " + counter.getMaxCheckedReportId());
log.info("Ending at getNumEmployees() = " + counter.getNumEmployees());
log.info("Ending at getNumExpenses() = " + counter.getNumExpenses());
log.info("Ending at getNumReports() = " + counter.getNumReports());
}
} catch (RuntimeException e) {
log.warning("Got exception " + e.getMessage());
throw e;
} finally {
if (transaction != null) {
log.warning("Rolling back transaction");
transaction.rollback();
}
transaction = null;
em.close();
}
long total;
switch (kind) {
case KIND_EMPLOYEE:
total = counter.getNumEmployees();
break;
case KIND_EXPENSE:
total = counter.getNumExpenses();
break;
case KIND_REPORT:
total = counter.getNumReports();
break;
default:
throw new RuntimeException("kind = " + kind);
}
log.info("Returning total = " + total);
return total;
}
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long maxCheckedEmployeeId;
private Long maxCheckedExpenseId;
private Long maxCheckedReportId;
private Long numEmployees;
private Long numExpenses;
private Long numReports;
public Long getId() {
return id;
}
public Long getMaxCheckedEmployeeId() {
return maxCheckedEmployeeId;
}
public Long getMaxCheckedExpenseId() {
return maxCheckedExpenseId;
}
public Long getMaxCheckedReportId() {
return maxCheckedReportId;
}
public Long getNumEmployees() {
return numEmployees;
}
public Long getNumExpenses() {
return numExpenses;
}
public Long getNumReports() {
return numReports;
}
public void setId(Long id) {
this.id = id;
}
public void setMaxCheckedEmployeeId(Long maxCheckedEmployeeId) {
this.maxCheckedEmployeeId = maxCheckedEmployeeId;
}
public void setMaxCheckedExpenseId(Long maxCheckedExpenseId) {
this.maxCheckedExpenseId = maxCheckedExpenseId;
}
public void setMaxCheckedReportId(Long maxCheckedReportId) {
this.maxCheckedReportId = maxCheckedReportId;
}
public void setNumEmployees(Long numEmployees) {
this.numEmployees = numEmployees;
}
public void setNumExpenses(Long numExpenses) {
this.numExpenses = numExpenses;
}
public void setNumReports(Long numReports) {
this.numReports = numReports;
}
private void clear() {
setId(ONE);
setNumEmployees(ZERO);
setNumExpenses(ZERO);
setNumReports(ZERO);
setMaxCheckedEmployeeId(ZERO);
setMaxCheckedExpenseId(ZERO);
setMaxCheckedReportId(ZERO);
}
}