You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by da...@apache.org on 2006/10/07 06:59:29 UTC
svn commit: r453845 [1/2] - in /db/derby/code/trunk/java/demo/localcal: ./
lib/ src/ src/images/
Author: davidvc
Date: Fri Oct 6 21:59:28 2006
New Revision: 453845
URL: http://svn.apache.org/viewvc?view=rev&rev=453845
Log:
DERBY-1936 - LocalCalendar Sample Application
Added:
db/derby/code/trunk/java/demo/localcal/
db/derby/code/trunk/java/demo/localcal/README (with props)
db/derby/code/trunk/java/demo/localcal/build.xml (with props)
db/derby/code/trunk/java/demo/localcal/lib/
db/derby/code/trunk/java/demo/localcal/src/
db/derby/code/trunk/java/demo/localcal/src/AddEventRequest.java (with props)
db/derby/code/trunk/java/demo/localcal/src/CalEvent.java (with props)
db/derby/code/trunk/java/demo/localcal/src/CalendarController.java (with props)
db/derby/code/trunk/java/demo/localcal/src/DatabaseManager.java (with props)
db/derby/code/trunk/java/demo/localcal/src/DeleteEventRequest.java (with props)
db/derby/code/trunk/java/demo/localcal/src/EventManager.java (with props)
db/derby/code/trunk/java/demo/localcal/src/GCalendar.java (with props)
db/derby/code/trunk/java/demo/localcal/src/GCalendarRequest.java (with props)
db/derby/code/trunk/java/demo/localcal/src/NetworkDownException.java (with props)
db/derby/code/trunk/java/demo/localcal/src/RequestManager.java (with props)
db/derby/code/trunk/java/demo/localcal/src/UpdateEventRequest.java (with props)
db/derby/code/trunk/java/demo/localcal/src/images/
db/derby/code/trunk/java/demo/localcal/src/images/delete.gif (with props)
db/derby/code/trunk/java/demo/localcal/src/index.html (with props)
db/derby/code/trunk/java/demo/localcal/src/localcal.js (with props)
Added: db/derby/code/trunk/java/demo/localcal/README
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/README?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/README (added)
+++ db/derby/code/trunk/java/demo/localcal/README Fri Oct 6 21:59:28 2006
@@ -0,0 +1,269 @@
+LocalCalendar is an example application which allows you to manage
+Google Calendar entries while disconnected from the network and
+then synchronize those changes back to Google Calendar when you
+get back online.
+
+The intention of this application is to provide code that demonstrates
+some of the key aspects of an offline architecture, including use
+of Apache Derby for local storage, the value of a transactional
+system, synchronization and conflict resolution, and crash recovery.
+
+
+1. WHAT YOU WILL NEED
+
+1.1 MOZILLA FIREFOX 1.5
+ http://www.mozilla.com/firefox/
+ NOTE: I have *not* tested this on IE or other browsers, and I am
+ pretty sure it doesn't work. You need Firefox to run the application
+ in this tutorial.
+
+1.2 JAVA DEVELOPMENT KIT 1.5 (or greater)
+ http://java.sun.com/javase/downloads/index.jsp
+
+1.3 SUN JAVA PLUGIN 1.5 (or greater) FOR FIREFOX
+ The version of the plugin should be no less than the version of
+ your JDK
+
+ If you don't already have this installed, I am pretty sure when you
+ install the JDK this gets set up for you automatically. To test
+ this go to the location "about:plugins" in your Firefox browser
+ window. It should list the Java 1.5 plugin.
+
+ To be doubly sure, go to http://java.com and click on
+ "Verify Installation"
+
+ If it does *not* appear to be installed, then for Windows you can
+ go to
+ http://java.com, and click on Download Now
+
+ For other platforms, go to
+ http://www.java.com/en/download/manual.jsp
+
+ and choose the appropriate platform and follow instructions for
+ install and verification.
+
+
+1.4 APACHE ANT 1.6 or greater
+ http://ant.apache.org
+
+1.5 APACHE DERBY 10.1.3 or greater
+ http://db.apache.org/derby/derby_downloads.html
+
+ All you need is derby.jar. Place a copy of derby.jar in the lib directory.
+
+1.6 A GOOGLE CALENDAR ACCOUNT
+ You need to get an account at http://calendar.google.com. The
+ email address and password you use to sign up are what you use
+ to log on to LocalCalendar.
+
+1.7 GOOGLE CALENDAR DATA API JAVA CLIENT
+ http://code.google.com/apis/gdata/download/gdata.java.zip
+
+ Extract the jar files you find inside this zip file and place them
+ in the lib directory.
+
+ for more info on this API, see
+ http://code.google.com/apis/gdata/calendar.html
+
+1.8 JSON FOR JAVA
+ This is used to map data sets between Java and JavaScript.
+
+ Download http://www.json.org/java/json.zip. This is a source
+ zip file. Extract this into the src directory. It will be built
+ as part of the overall build of LocalCalendar and added to localcal.jar
+
+
+
+2. SIGNING THE JAR FILES
+
+LocalCalendar runs as an applet, but the code will be performing
+operations that normally aren't allowed inside the applet sandbox.
+For this reason you need to create a key and then use this key
+to sign the jar files.
+
+2.1 Creating the key
+Using the keytool utility that is part of the JDK, run the following
+command:
+
+ keytool -genkey -alias <your-alias>
+
+and follow the prompts. <your-alias> can be whatever alias you want
+to use, such as your username.
+
+2.2 Signing the jar files
+
+Using the jarsigner utility that is part of the JDK, run the following
+commands
+
+ jarsigner lib/derby.jar <alias>
+ jarsigner lib/gdata-calendar-1.0.jar <alias>
+ jarsigner lib/gdata-client-1.0.jar <alias>
+
+
+3. BUILDING
+
+3.1 SET KEYSTORE PROPERTIES IN BUILD.XML
+Edit build.xml and set the keystore.alias and keystore.password
+values to match the ones you used above when signing the jar files.
+This is used to automatically sign the LocalCalendar jar file each
+time we build it.
+
+3.2 SET THE CALENDAR ID
+
+The calendar id uniquely identifies the calendar you want
+to work with. Currently it is set to a test calendar that is
+owned by David Van Couvering. You are free to use this, it is
+available to all, but you can also create your own calendar. Here
+are the steps:
+
+3.2.1 Create a calendar
+Make sure it is set to be publicly available to all. If you want
+to make it private, read the instructions for Google Calendar and
+the Google Calendar API for how to do this.
+
+3.2.2 Get the id
+On the left hand pane of your Google Calendar page is the list of
+calendars you have created. Click on the down-arrow for the
+one you want to use in LocalCalendar and choose "Calendar Settings"
+
+On the settings page, click on the orange [XML] box for calendar
+address. You'll get a long URI of the form
+
+http://www.google.com/calendar/feeds/<id>@group.calendar.google.com/public/basic
+
+(unless you're using your default calendar, in which case it will be
+of the form
+
+http://www.google.com/calendar/feeds/<your-email-address>/public/basic)
+
+Your calendar id is either <id>@group.calendar.google.com or
+<your-email-address>.
+
+3.2.3 Set the id in LocalCalendar
+Set the variable derbycal.calid in src/localcal.js to be your
+calendar id.
+
+3.3 SET THE DATE RANGE IN LOCALCALENDAR
+The date range used by LocalCalendar is currently hardcoded. If you're
+interested, please feel free to make this dynamically settable by the
+user. But I didn't get around to doing that.
+
+To change the date range, edit src/localcal.js and modify the
+array derbycal.days to match the dates you're interested in.
+
+3.4 CHANGE THE TITLE
+If you want, you can change the title to match what your calendar
+is about. Edit src/index.html and change the <h1> tag with the
+id "loginHeader" and also the <h1> tag just underneat the <div>
+with id "main-div".
+
+3.4 BUILD
+To build, simply run "ant" in the top-level directory. This will
+build the source, create and sign a jar file, and put this as well
+as all other needed resources into the dist directory.
+
+
+4 RUNNING LOCALCALENDAR
+
+Bring up Firefox and point it to <path-to-localcal>/dist/index.html.
+
+LocalCalendar shows a week's worth of events. Each event is an
+all-day event. I didn't try to mess around with event times, this
+was more HTML and JavaScript than I was willing to write.
+
+To play around with LocalCalendar's capabilities, try the following.
+To find bugs, try almost anything else :)
+
+4.1 BASIC ONLINE OPERATION
+
+- Start LocalCalendar
+
+- Log in to Google Calendar using your id/password
+Notice that the app turns green and shows the events from your
+calendar once you're online. Green means online. Blue means
+offline. The power of DHTML.
+
+- If you view the Java Console, it will tell you where output
+ is being logged. To watch this go by as you're working, do
+ a running tail of the log file (e.g. tail -f <logfile>). The
+ default location is <java user.dir>/localcal.log
+
+- Add an event by choosing a day, entering a title, and clicking
+ the [Add Event] button
+
+- Delete an event by clicking the red X next to it
+
+- Update an event by selecting it and modifying it. When you exit
+ the field it is updated automatically
+
+- In a separate tab or window log on to Google Calendar. Notice your
+ modifications are reflected in Google Calendar.
+
+
+4.2 OFFLINE OPERATION
+
+- Unplug your network or disable your network
+
+- Try to access Google Calendar and notice that it's not available.
+ Bummer.
+
+- Make a change to LocalCalendar. LocalCalendar automatically detects
+ the network is down and goes offline. The change is still stored
+ locally and is reflected in your application.
+
+- Make a couple more changes
+
+- Re-enable your network
+
+- Try Google Calendar, and notice it's available again (whew!)
+
+- In LocalCalendar, click [Go Online]. LocalCalendar synchronizes
+ its changes with Google Calendar, and then turns green.
+
+- Refresh Google Calendar and notice your changes in LocalCalendar
+ are now reflected (yay!)
+
+
+4.2 CRASH RECOVERY
+This demonstrates the power of Derby's automatic crash recovery
+and consistent durability
+
+- Click [Go Offline]
+
+- Make some offline changes
+
+- Kill Firefox (yipes!)
+
+- Restart Firefox and open Local Calendar. Log in to Google
+ Calendar.
+
+- Notice that LocalCalendar automatically detects that you have
+ made changes and synchronizes with Google Calendar after you
+ log in.
+
+4.3 CONFLICT DETECTION
+Google Calendar has conflict detection. When you get an entry
+from Google Calendar, it includes a version id, and you send that
+version id when you update. If the version ids don't match, Google
+Calendar will reject the update.
+
+- Click [Go Offline]
+
+- Make an update to an existing entry
+
+- Go to Google Calendar
+
+- Update the same entry
+
+- Click [Go Online]
+
+- You should get a popup saying there was a conflict during
+ synchronization
+
+- Look at the log file and you'll see the detailed error
+ information
+
+
+ARCHITECTURE
+
+<Coming Soon>
Propchange: db/derby/code/trunk/java/demo/localcal/README
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/build.xml?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/build.xml (added)
+++ db/derby/code/trunk/java/demo/localcal/build.xml Fri Oct 6 21:59:28 2006
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to you 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.
+-->
+
+<project default="all" basedir=".">
+ <property name="classes.dir" value="classes"/>
+ <property name="src.dir" value="src"/>
+ <property name="dist.dir" value="dist"/>
+ <property name="lib.dir" value="lib"/>
+
+ <!-- CHANGE THIS TO YOUR ALIAS NAME -->
+ <property name="keystore.alias" value="davidvc"/>
+ <!-- CHANGE THIS TO YOUR PASSWORD -->
+ <property name="keystore.password" value="secret"/>
+
+ <target name="all" depends="compile, jar, dist"/>
+
+ <target name="clean">
+ <delete dir="${classes.dir}"/>
+ <mkdir dir="${classes.dir}"/>
+ <delete dir="${dist.dir}"/>
+ <mkdir dir="${dist.dir}"/>
+ </target>
+
+ <target name="compile">
+ <mkdir dir="${classes.dir}"/>
+ <javac
+ srcdir="${src.dir}"
+ destdir="${classes.dir}">
+ <classpath>
+ <pathelement path="${classes.dir}"/>
+ <pathelement path="${lib.dir}/derby.jar"/>
+ <pathelement path="${lib.dir}/gdata-calendar-1.0.jar"/>
+ <pathelement path="${lib.dir}/gdata-client-1.0.jar"/>
+ </classpath>
+ </javac>
+ </target>
+
+ <target name="jar">
+ <jar basedir="${classes.dir}" destfile="${lib.dir}/localcal.jar"/>
+ <signjar jar="${lib.dir}/localcal.jar" alias="${keystore.alias}"
+ storepass="${keystore.password}"/>
+ </target>
+
+ <target name="dist">
+ <mkdir dir="${dist.dir}"/>
+ <copy todir="${dist.dir}">
+ <fileset dir="${lib.dir}"/>
+ </copy>
+ <copy file="${src.dir}/index.html" todir="${dist.dir}"/>
+ <copy file="${src.dir}/localcal.js" todir="${dist.dir}"/>
+ <copy todir="${dist.dir}/images">
+ <fileset dir="${src.dir}/images"/>
+ </copy>
+ </target>
+</project>
Propchange: db/derby/code/trunk/java/demo/localcal/build.xml
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/AddEventRequest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/AddEventRequest.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/AddEventRequest.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/AddEventRequest.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,76 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+import java.sql.*;
+
+/**
+ * Encapsulates a request to add an event
+ */
+public class AddEventRequest extends GCalendarRequest {
+
+ private String eventId;
+ private String date;
+ private String title;
+
+ /** Creates a new instance of AddEventRequest */
+ protected AddEventRequest(int requestId, String eventId, String date,
+ String title) {
+ super(requestId);
+ setEventId(eventId);
+ setDate(date);
+ setTitle(title);
+ }
+
+ public String toString() {
+ return "Request # " + getId() + " to add event (" + getDate() + ": " +
+ getTitle() + ")";
+ }
+
+ public void submit(GCalendar calendar) throws Exception {
+ CalEvent event = calendar.addEvent(getDate(), getTitle());
+
+ // Fix the database so that the id returned by Google Calendar
+ // is used.
+ EventManager.updateEventId(eventId, event.getId());
+ }
+
+ public String getDate() {
+ return date;
+ }
+
+ public void setDate(String date) {
+ this.date = date;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getEventId() {
+ return eventId;
+ }
+
+ public void setEventId(String eventId) {
+ this.eventId = eventId;
+ }
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/AddEventRequest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/CalEvent.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/CalEvent.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/CalEvent.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/CalEvent.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,124 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+import java.util.Date;
+import java.text.SimpleDateFormat;
+import org.json.*;
+
+/**
+ * Encapsulates an event
+ */
+public class CalEvent implements java.io.Serializable {
+ private String id;
+ private String date;
+ private String title;
+ private String editURL;
+ private String versionId;
+
+ private SimpleDateFormat dayFormat = new SimpleDateFormat("EEEE");
+
+ private SimpleDateFormat fullFormat = new SimpleDateFormat("yyyy-MM-dd");
+
+ /** Creates a new instance of DerbyCalEvent
+ *
+ * @param id
+ * The id for this event. This may be a temporary id until we're
+ * assigned an "official" one by Google Calendar (whenever this event
+ * is posted to Google Calendar).
+ *
+ * @param date
+ * The day for this event, in the format <yyyy>-<mm>-<dd>
+ *
+ * @param title
+ * The title for the event
+ *
+ * @param editURL
+ * The edit URL provided by Google Calendar (can be null)
+ *
+ * @param versionId
+ * The version identifier provided by Google Calendar (can be null)
+ */
+ public CalEvent(String id, String date, String title,
+ String editURL, String versionId) {
+ this.id = id;
+ this.date = date;
+ this.title = title;
+ this.editURL = editURL;
+ this.versionId = versionId;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getDate() {
+ return date;
+ }
+
+ /** Get the day string for the date */
+ public String getDay() throws Exception {
+ return dayFormat.format(fullFormat.parse(getDate()));
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setDate(String date) {
+ this.date = date;
+ }
+
+ /**
+ * Get the JSONObject for this event
+ */
+ public JSONObject getJSONObject() throws Exception {
+ JSONObject jobj = new JSONObject();
+ jobj.put("eventid", getId());
+ jobj.put("day", getDay());
+ jobj.put("title", getTitle());
+ jobj.put("date", getDate());
+ jobj.put("editURL", getEditURL());
+ jobj.put("versionId", getVersionId());
+
+ return jobj;
+ }
+
+ public String getEditURL() {
+ return editURL;
+ }
+
+ public String getVersionId() {
+ return versionId;
+ }
+
+ public String toString() {
+ return "id: " + getId() +", date: " + getDate() + ", title: " +
+ getTitle();
+ }
+
+
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/CalEvent.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/CalendarController.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/CalendarController.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/CalendarController.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/CalendarController.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,409 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+*/
+import com.sun.media.sound.JavaSoundAudioClip;
+import java.applet.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.awt.*;
+import java.io.*;
+import java.security.*;
+
+
+import org.json.*;
+
+/**
+ * This is the controller for the local calendar application
+ */
+public class CalendarController extends Applet {
+ public static final String DBNAME = "LocalCalDB";
+ private String calid;
+ private String gmtOffset;
+ private String user;
+ private String password;
+ private String startDay;
+ private String endDay;
+
+ private static PrintStream console;
+ private static final String CONSOLE_FILENAME = "localcal.log";
+
+ /** The calendar we're logged in to */
+ GCalendar calendar;
+
+ /** Indicates whether we're online or not */
+ boolean online = true;
+
+ /**
+ * Log in to the Google Calendar service.
+ *
+ * @param calid
+ * The calendar id. If this is your default Google Calendar,
+ * it's the email address you use to login to Google Calendar,
+ * e.g. "david.vancouvering@gmail.com".
+ * <p>
+ * If it's not your default calendar, you can get the calendar id
+ * for the calendar you want by doing the following:
+ * <ul>
+ * <li>Go to your Google Calendar page
+ * <li>On the left pane all your calendars are listed. Click on the
+ * drop-down menu for the calendar you want, and choose
+ * "Calendar settings".
+ * <li>Under "Calendar Address" click on the [XML] button. A window
+ * will pop up that will give you a URL of the form <pre>
+ * "http://www.google.com/calendar/feeds/<token>@group.calendar.google.com/public/basic"
+ * </pre>
+ * <li>Your calendar id is "<token>@group.calendar.google.com"
+ * </ul>
+ *
+ * @param gmtOffset
+ * The offset, positive or negative, from GMT for the time zone for
+ * the calendar
+ *
+ * @param user
+ * your username for your Google Calendar account, e.g.
+ * david.vancouvering@gmail.com
+ *
+ * @param password
+ * your password for your Google Calendar account
+ *
+ * @param startDay
+ * The starting day for this calendar in the format
+ * <yyyy>-<mm>-<dd>
+ *
+ * @param endDay
+ * The ending day inclusive for this calendar, in the format
+ * <yyyy>-<mm>-<dd>
+ *
+ * @return a JSON Array containing the list of events from Google
+ */
+ public void login(String calid, String gmtOffset,
+ String user, String password, String startDay,
+ String endDay, boolean drop) throws Exception {
+ // Create the calendar, and if the login succeeds, start up the thread
+ log("DerbyCalendarApplet.login(" + calid + ", " + gmtOffset + ", " +
+ user + ", " + startDay + ", " + endDay + ")");
+
+ this.calid = calid;
+ this.gmtOffset = gmtOffset;
+ this.user = user;
+ this.password = password;
+ this.startDay = startDay;
+ this.endDay = endDay;
+
+ goOnline();
+ }
+
+ private void startConsole(String dir) throws Exception {
+ final String path = dir + "/" + CONSOLE_FILENAME;
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ log("Writing log to " + path);
+ console = new PrintStream(
+ new FileOutputStream(path));
+ System.setOut(console);
+ System.setErr(console);
+ return null;
+ }
+ }
+ );
+ }
+
+ /**
+ * Go online. This means (a) send up to Google Calendar any stored
+ * requests, logging any errors that occur and (b) getting the latest
+ * list of events from Google Calendar.
+ *
+ * @return a JSON String representing the latest list of events for
+ * this calendar
+ */
+ public void goOnline() throws Exception {
+ log("GOING ONLINE...");
+ this.online = true;
+
+ try {
+ // Log in to Google Calendar
+ calendar = new GCalendar(calid, gmtOffset, user, password,
+ startDay, endDay);
+
+ RequestManager.submitRequests(calendar);
+ } catch ( Exception e ) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ public void goOffline() {
+ log("GOING OFFLINE");
+ this.online = false;
+ }
+
+ public boolean isOnline() {
+ return this.online;
+ }
+
+ /**
+ * Refresh our calendar from Google Calendar and return a JSON string that
+ * represents the array of entries for the given date range
+ *
+ * @return a JSON string that represents an array of all the entries
+ * for the calendar.
+ */
+ public String refresh() throws Exception {
+ log("DerbyCalendarApplet.refresh()");
+
+
+ Collection<CalEvent> events = null;
+ if ( isOnline() ) {
+ try {
+ events = calendar.getEvents();
+
+ // Refresh the database with the events we got from Google
+ // Calendar
+ EventManager.refresh(events);
+ } catch ( NetworkDownException nde ) {
+ log("The network is down, going offline");
+ goOffline();
+ }
+ }
+
+ if ( ! isOnline() ) {
+ events = EventManager.getEvents();
+ }
+
+ JSONArray jarray = new JSONArray();
+ for ( CalEvent event : events ) {
+
+ jarray.put(event.getJSONObject());
+ }
+
+ return jarray.toString();
+ }
+
+ // Return a list of conflicts as a string so it can be reported
+ // as an error
+ public String getConflicts() {
+ java.util.List<String> conflicts = RequestManager.getConflicts();
+ if ( conflicts.size() == 1 ) {
+ return "There was 1 error during synchronization. Please " +
+ "see the error log for details.";
+ } else if ( conflicts.size() > 1 ) {
+ return "There were " + conflicts.size() + " errors during " +
+ "synchronization. Please see the error log for details.";
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Add an entry to the calendar
+ *
+ * @param id
+ * The unique identifier for this new entry
+ *
+ * @param date
+ * The date for this entry, in the form of <yyyy>-<mm>-<dd>
+ *
+ * @param title
+ * The title for the entry
+ *
+ * @return the new id returned by Google Calendar
+ */
+ public String addEvent(String id, String date, String title)
+ throws Exception {
+ log("DerbyCalendarApplet.addEntry(" + id +
+ ", " + date + ", " + title + ")");
+
+ CalEvent event = null;
+
+ if ( isOnline() ) {
+ try {
+ event = calendar.addEvent(date, title);
+ } catch ( NetworkDownException nde ) {
+ log("The network is down, going offline");
+ goOffline();
+ }
+ }
+
+ // Now do the database operations -- store the event
+ // locally, and if we're offline, also store the request
+ // to add the event so we can ship it to Google Calendar
+ // when we come back online
+ try {
+ DatabaseManager.beginTransaction();
+
+ if ( ! isOnline() ) {
+ log("Storing request to add event");
+ RequestManager.storeAddEvent(id, date, title);
+ event = new CalEvent(id, date, title, null, null);
+ }
+
+ log("Storing new event in the local database");
+ EventManager.addEvent(event);
+
+ DatabaseManager.commitTransaction();
+ } catch ( Exception e ) {
+ DatabaseManager.rollbackTransaction();
+ throw e;
+ }
+
+ return event.getJSONObject().toString();
+}
+
+ public void updateEvent(String id, String title) throws Exception {
+ log("DerbyCalendarApplet.updateEntry(" + id + ", " + title + ")");
+ CalEvent event = EventManager.getEvent(id);
+ event.setTitle(title);
+
+ if ( isOnline() ) {
+ try {
+ event = calendar.updateEvent(event);
+ } catch ( NetworkDownException nde ) {
+ log("The network is down, going offline");
+ goOffline();
+ }
+ }
+
+ // Now do the database operations -- store the event
+ // locally, and if we're offline, also store the request
+ // to add the event so we can ship it to Google Calendar
+ // when we come back online
+ try {
+ DatabaseManager.beginTransaction();
+
+ if ( ! isOnline() ) {
+ log("Storing request to update event");
+ RequestManager.storeUpdateEvent(event);
+ }
+
+ log("Updating event in the local database");
+ EventManager.updateEvent(event);
+
+ DatabaseManager.commitTransaction();
+ } catch ( Exception e ) {
+ DatabaseManager.rollbackTransaction();
+ throw e;
+ }
+
+ }
+
+ public void deleteEvent(String id) throws Exception {
+ log("DerbyCalendarApplet.deleteEntry(" + id + ")");
+ CalEvent event = EventManager.getEvent(id);
+
+ if ( isOnline() ) {
+ try {
+ if ( event == null ) {
+ throw new Exception("Can't find even in the database: " +
+ id);
+ }
+
+ calendar.deleteEvent(event.getEditURL());
+ } catch ( NetworkDownException nde ) {
+ log("The network is down, going offline");
+ goOffline();
+ }
+ }
+
+ // Now do the database operations -- store the event
+ // locally, and if we're offline, also store the request
+ // to add the event so we can ship it to Google Calendar
+ // when we come back online
+ try {
+ DatabaseManager.beginTransaction();
+
+ if ( ! isOnline() ) {
+ log("Storing request to delete event");
+ RequestManager.storeDeleteEvent(id, event.getEditURL());
+ }
+
+ log("Deleting event in the local database");
+ EventManager.deleteEvent(id);
+
+ DatabaseManager.commitTransaction();
+ } catch ( Exception e ) {
+ DatabaseManager.rollbackTransaction();
+ throw e;
+ }
+
+ }
+
+ /**
+ * Empty out the calendar. Used mostly for testing
+ */
+ public void clearCalendar() throws Exception {
+ DatabaseManager.clearTables();
+ calendar.clearCalendar();
+ }
+
+ private void log(String str) {
+ System.out.println(str);
+ }
+
+ /**
+ * Call this to turn on logging of SQL to derby.log,
+ * for debuggig
+ */
+ public void logSql() throws Exception {
+ DatabaseManager.logSql();
+ }
+
+ public void init() {
+ log("Applet init, applet is " + this.hashCode());
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ String userdir = System.getProperty("user.home");
+ String dbname = userdir + "/" + DBNAME;
+
+ startConsole(userdir);
+
+ DatabaseManager.initDatabase(dbname,
+ "user", "secret", false);
+ log("Database initialized, " +
+ "database directory is " + dbname);
+
+ return null;
+ }
+ }
+ );
+ } catch ( PrivilegedActionException e ) {
+ e.getException().printStackTrace();
+ }
+ }
+
+ public void start() {
+ log("Applet start, applet is " + this.hashCode());
+ }
+
+ public void stop() {
+ log("Applet stop, applet is " + this.hashCode());
+ }
+
+ public void destroy() {
+ log("Applet destroy, applet is " + this.hashCode());
+ }
+
+ /** Still need to figure this one out...
+ public void paint(Graphics g) {
+ g.drawString("Repainting", 50, 25);
+ g.drawString(consoleStream.toString(), 50, 35);
+ }
+ */
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/CalendarController.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/DatabaseManager.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/DatabaseManager.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/DatabaseManager.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/DatabaseManager.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,221 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+import javax.sql.*;
+import java.sql.*;
+import org.apache.derby.jdbc.EmbeddedDataSource;
+
+/**
+ * A container for the singleton data source, so we don't have to
+ * create a separate one for each class that wants to do JDBC
+ */
+public class DatabaseManager {
+
+ private static EmbeddedDataSource ds;
+
+ public static String REQUESTS_TABLE = "APP.REQUESTS";
+ public static String EVENTS_TABLE = "APP.EVENTS";
+
+ // We want to keep the same connection for a given thread
+ // as long as we're in the same transaction
+ private static ThreadLocal<Connection> tranConnection = new ThreadLocal();
+
+ private static void initDataSource(String dbname, String user,
+ String password) {
+ ds = new EmbeddedDataSource();
+ ds.setDatabaseName(dbname);
+ ds.setUser(user);
+ ds.setPassword(password);
+ ds.setCreateDatabase("create");
+ }
+
+ public static void logSql() throws Exception {
+ executeUpdate("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY(" +
+ "'derby.language.logStatementText', 'true')");
+ }
+
+ public static synchronized void beginTransaction() throws Exception {
+ if ( tranConnection.get() != null ) {
+ throw new Exception("This thread is already in a transaction");
+ }
+ Connection conn = getConnection();
+ conn.setAutoCommit(false);
+ tranConnection.set(conn);
+ }
+
+ public static void commitTransaction() throws Exception {
+ if ( tranConnection.get() == null ) {
+ throw new Exception("Can't commit: this thread isn't currently in a " +
+ "transaction");
+ }
+ tranConnection.get().commit();
+ tranConnection.set(null);
+ }
+
+ public static void rollbackTransaction() throws Exception {
+ if ( tranConnection.get() == null ) {
+ throw new Exception("Can't rollback: this thread isn't currently in a " +
+ "transaction");
+ }
+ tranConnection.get().rollback();
+ tranConnection.set(null);
+ }
+
+ /** get a connection */
+ public static Connection getConnection() throws Exception {
+ if ( tranConnection.get() != null ) {
+ return tranConnection.get();
+ } else {
+ return ds.getConnection();
+ }
+ }
+
+ public static void releaseConnection(Connection conn) throws Exception {
+ // We don't close the connection while we're in a transaction,
+ // as it needs to be used by others in the same transaction context
+ if ( tranConnection.get() == null ) {
+ conn.close();
+ }
+ }
+
+ public static void initDatabase(String dbname, String user, String password,
+ boolean dropTables)
+ throws Exception {
+ initDataSource(dbname, user, password);
+
+ if ( dropTables ) {
+ dropTables();
+ }
+
+ // Assumption: if the requests table doesn't exist, none of the
+ // tables exists. Avoids multiple queries to the database
+ if ( ! tableExists("REQUESTS") ) {
+ createTables();
+ }
+ }
+
+ private static boolean tableExists(String tablename) throws Exception {
+ Connection conn = getConnection();
+ ResultSet rs;
+ boolean exists;
+
+ try {
+ DatabaseMetaData md = conn.getMetaData();
+
+ rs = md.getTables(null, "APP", tablename, null);
+ exists = rs.next();
+ } finally {
+ releaseConnection(conn);
+ }
+
+ return exists;
+ }
+
+ private static void createTables() throws Exception {
+ System.out.println("Creating tables");
+
+ executeUpdate(
+ "CREATE TABLE " + REQUESTS_TABLE + "(" +
+ "sequence_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, " +
+ "request_type INTEGER, " +
+ "event_id VARCHAR(300), " +
+ "date VARCHAR(20), " +
+ "title VARCHAR(300), " +
+ "edit_url VARCHAR(300))");
+
+ executeUpdate(
+ "CREATE TABLE " + EVENTS_TABLE + "(" +
+ "event_id VARCHAR(300) PRIMARY KEY, " +
+ "date VARCHAR(20), " +
+ "title VARCHAR(300), " +
+ "edit_url VARCHAR(300), " +
+ "version_id VARCHAR(300))");
+ }
+
+ /**
+ * Drop the tables. Used mostly for unit testing, to get back
+ * to a clean state
+ */
+ public static void dropTables() throws Exception {
+ try {
+ executeUpdate("DROP TABLE " + REQUESTS_TABLE);
+ } catch ( SQLException sqle ) {
+ if (! tableDoesntExist(sqle.getSQLState())) {
+ throw sqle;
+ }
+ }
+
+ try {
+ executeUpdate("DROP TABLE " + EVENTS_TABLE);
+ } catch ( SQLException sqle ) {
+ if (! tableDoesntExist(sqle.getSQLState())) {
+ throw sqle;
+ }
+ }
+ }
+
+ private static boolean tableDoesntExist(String sqlState) {
+ return sqlState.equals("42X05") ||
+ sqlState.equals("42Y55");
+ }
+
+ /**
+ * Clean out the tables
+ */
+ public static void clearTables() throws Exception {
+ Connection conn = getConnection();
+
+ try {
+ executeUpdate("DELETE FROM " + REQUESTS_TABLE);
+ executeUpdate("DELETE FROM " + EVENTS_TABLE);
+ } finally {
+ releaseConnection(conn);
+ }
+
+ }
+
+ /**
+ * Helper wrapper around boilerplate JDBC code. Execute a statement
+ * that doesn't return results using a PreparedStatment, and returns
+ * the number of rows affected
+ */
+ public static int executeUpdate(String statement)
+ throws Exception {
+ Connection conn = getConnection();
+ try {
+ PreparedStatement ps = conn.prepareStatement(statement);
+ return ps.executeUpdate();
+ } finally {
+ releaseConnection(conn);
+ }
+ }
+
+ /**
+ * Helper wrapper around boilerplat JDBC code. Execute a statement
+ * that returns results using a PreparedStatement that takes no
+ * parameters (you're on your own if you're binding parameters).
+ *
+ * @return the results from the query
+ */
+ public static ResultSet executeQueryNoParams(Connection conn,
+ String statement) throws Exception {
+ PreparedStatement ps = conn.prepareStatement(statement);
+ return ps.executeQuery();
+ }
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/DatabaseManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/DeleteEventRequest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/DeleteEventRequest.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/DeleteEventRequest.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/DeleteEventRequest.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,58 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+/**
+ * encapsulates a request to delete an event
+ */
+public class DeleteEventRequest extends GCalendarRequest {
+ private String eventId;
+ private String editURL;
+
+ /** Creates a new instance of AddEventRequest */
+ protected DeleteEventRequest(int requestId, String eventId, String editURL) {
+ super(requestId);
+ this.setEventId(eventId);
+ this.setEditURL(editURL);
+ }
+
+ public String toString() {
+ return "Request # " + getId() + " to delete event id " + getEventId()
+ + " with edit URL " + editURL;
+ }
+
+ public void submit(GCalendar calendar) throws Exception {
+ calendar.deleteEvent(editURL);
+ }
+
+ public String getEventId() {
+ return eventId;
+ }
+
+ public void setEventId(String eventId) {
+ this.eventId = eventId;
+ }
+
+ public String getEditURL() {
+ return editURL;
+ }
+
+ public void setEditURL(String editURL) {
+ this.editURL = editURL;
+ }
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/DeleteEventRequest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/EventManager.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/EventManager.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/EventManager.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/EventManager.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,207 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+import java.util.*;
+import java.sql.*;
+
+/**
+ * Responsible for managing persistence of events to the database
+ */
+public class EventManager {
+ public static void addEvent(CalEvent event) throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "INSERT INTO " + DatabaseManager.EVENTS_TABLE +
+ "(event_id, date, title, edit_url, version_id) " +
+ "VALUES(?, ?, ?, ?, ?)");
+
+ pstmt.setString(1, event.getId());
+ pstmt.setString(2, event.getDate());
+ pstmt.setString(3, event.getTitle());
+ if ( event.getEditURL() == null ) {
+ pstmt.setNull(4, Types.VARCHAR);
+ } else {
+ pstmt.setString(4, event.getEditURL());
+ }
+
+ if ( event.getVersionId() == null ) {
+ pstmt.setNull(5, Types.VARCHAR);
+ } else {
+ pstmt.setString(5, event.getVersionId());
+ }
+
+ pstmt.executeUpdate();
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ /**
+ * Fix the event id in both the events and requests tables. This
+ * happens when we add an event and Google Calendar sends us back
+ * a new id
+ */
+ public static void updateEventId(String oldId, String newId)
+ throws Exception {
+ System.out.println("Updating event id in the database");
+ Connection conn = DatabaseManager.getConnection();
+
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "UPDATE " + DatabaseManager.EVENTS_TABLE +
+ " SET event_id = ? WHERE event_id = ?");
+ pstmt.setString(1, oldId);
+ pstmt.setString(2, newId);
+ pstmt.execute();
+
+ pstmt = conn.prepareStatement(
+ "UPDATE " + DatabaseManager.REQUESTS_TABLE +
+ " SET event_id = ? WHERE event_id = ?");
+ pstmt.setString(1, oldId);
+ pstmt.setString(2, newId);
+ pstmt.execute();
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+
+ public static void deleteEvent(String eventId) throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "DELETE FROM " + DatabaseManager.EVENTS_TABLE +
+ " WHERE event_id = ?");
+
+ pstmt.setString(1, eventId);
+ pstmt.executeUpdate();
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ public static void updateEvent(CalEvent event) throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "UPDATE " + DatabaseManager.EVENTS_TABLE +
+ " SET date = ?, title = ?, edit_url = ?, version_id = ? " +
+ "WHERE event_id = ?");
+
+ pstmt.setString(1, event.getDate());
+ pstmt.setString(2, event.getTitle());
+ if ( event.getEditURL() == null ) {
+ pstmt.setNull(3, Types.VARCHAR);
+ } else {
+ pstmt.setString(3, event.getEditURL());
+ }
+ if ( event.getVersionId() == null ) {
+ pstmt.setNull(4, Types.VARCHAR);
+ } else {
+ pstmt.setString(4, event.getVersionId());
+ }
+ pstmt.setString(5, event.getId());
+ if ( pstmt.executeUpdate() == 0 ) {
+ throw new Exception("Event not updated - couldn't find event " +
+ "with id " + event.getId());
+ }
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+
+ }
+
+ public static Collection<CalEvent> getEvents() throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+ ArrayList<CalEvent> events = new ArrayList<CalEvent>();
+
+ try {
+ ResultSet rs = DatabaseManager.executeQueryNoParams(conn,
+ "SELECT event_id, date, title, edit_url, version_id FROM " +
+ DatabaseManager.EVENTS_TABLE);
+
+ // System.out.println("");
+ // System.out.println("Getting events from local database");
+ while ( rs.next() ) {
+ CalEvent event = new CalEvent(
+ rs.getString(1), rs.getString(2), rs.getString(3),
+ rs.getString(4), rs.getString(5));
+
+ // System.out.println(event);
+ events.add(event);
+ }
+ // System.out.println("");
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+
+ return events;
+ }
+
+ /**
+ * Get a single event from the database
+ */
+ public static CalEvent getEvent(String eventId) throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+ CalEvent event;
+
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "SELECT date, title, edit_url, version_id FROM " +
+ DatabaseManager.EVENTS_TABLE +
+ " WHERE event_id = ?");
+ pstmt.setString(1, eventId);
+ ResultSet rs = pstmt.executeQuery();
+
+ if ( ! rs.next() ) {
+ return null;
+ }
+
+ event = new CalEvent(
+ eventId, rs.getString(1), rs.getString(2),
+ rs.getString(3), rs.getString(4));
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+
+ return event;
+ }
+
+ /**
+ * Refresh the database with a list of events from the Google
+ * Calendar. Google Calendar is The Truth and the local store
+ * must submit...
+ *
+ * Note we also clear out any pending requests, as they are no
+ * longer valid once we've refreshed.
+ */
+ public static void refresh(Collection<CalEvent> events)
+ throws Exception {
+ System.out.println("Refreshing local store with list of events " +
+ "from Google Calendar...");
+
+ DatabaseManager.clearTables();
+
+ for ( CalEvent event : events ) {
+ addEvent(event);
+ }
+ }
+}
+
Propchange: db/derby/code/trunk/java/demo/localcal/src/EventManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/GCalendar.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/GCalendar.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/GCalendar.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/GCalendar.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,441 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.net.URL;
+import java.security.*;
+import java.text.SimpleDateFormat;
+
+import com.google.gdata.client.calendar.*;
+import com.google.gdata.data.extensions.*;
+import com.google.gdata.data.*;
+import com.google.gdata.data.calendar.*;
+import com.google.gdata.util.*;
+import com.google.gdata.client.*;
+
+/**
+ * This class provides a simple abstraction for interacting with the
+ * Google Calendar Data API. It runs as a separate thread so that requests
+ * can be posted asynchronously.
+ *
+ * All requests are shipped through the requestQueue. The run method
+ * reads from the request queue and ships the requests to Google Calendar.
+ * Results are then fed back on the response queue.
+ */
+public class GCalendar {
+ private static String GOOGLE_CAL_URL_PREFIX =
+ "http://www.google.com/calendar/feeds/";
+
+ private static String AUTHOR_NAME="David Van Couvering";
+ private static String AUTHOR_EMAIL="david.vancouvering@gmail.com";
+
+ private String calendarId;
+ private URL feedUrl;
+ private EventFeed feed;
+ private CalendarService service;
+ private String gmtOffset;
+ private String startDay;
+ private String endDay;
+
+ /**
+ * Creates a new instance of GCalendarService
+ *
+ * @param calendarId
+ * The special token that identifies the calendar.
+ * @see DerbyCalendarApplet.connect() for a full description of this.
+ *
+ * @param user
+ * Your Google Calendar account name, e.g. david.vancouvering@gmail.com
+ *
+ * @param password
+ * Your Google Calendar password
+ *
+ * * @param startDay
+ * the starting day for the calendar, in the format <yyyy>-<mm>-<dd>
+ *
+ * @param endDay
+ * the starting day for the calendar, in the format <yyyy>-<mm>-<dd>
+
+ */
+ public GCalendar(String calendarId, String gmtOffset, final String user,
+ final String password, String startDay, String endDay)
+ throws Exception {
+ this.calendarId = calendarId;
+ this.gmtOffset = gmtOffset;
+ this.startDay = startDay;
+ this.endDay = endDay;
+
+ // We're accessing a read-write feed, and we want full information
+ this.feedUrl = new URL(GOOGLE_CAL_URL_PREFIX + calendarId +
+ "/private/full");
+
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ service = new CalendarService("DerbyCalendar Demo");
+ service.setUserCredentials(user, password);
+
+ // Send the request and receive the response:
+ feed = service.getFeed(feedUrl, EventFeed.class);
+
+ return null;
+ }
+ }
+ );
+ } catch (PrivilegedActionException e) {
+ handleException(e.getException(),
+ "Unable to get Google Calendar feed",
+ null);
+ }
+
+ }
+
+ /**
+ * Add an event to the Google Calendar
+ *
+ * @param event
+ * The event to add
+ *
+ * @return
+ * The event that was added, modified as needed by what was
+ * returned from Google Calendar. In particular, the id has
+ * been updated to be the id provided by Google Calendar.
+ */
+ public CalEvent addEvent(String date, String title)
+ throws Exception {
+ EventEntry newEntry = new EventEntry();
+
+ newEntry.setTitle(new PlainTextConstruct(title));
+
+ Person author = new Person(AUTHOR_NAME, null, AUTHOR_EMAIL);
+ newEntry.getAuthors().add(author);
+
+ DateTime startTime = DateTime.parseDateTime(date + "T00:00:00" +
+ gmtOffset);
+ startTime.setDateOnly(true);
+
+ // An all day event has an end time of the next day. Need to
+ // calculate that using the wonderful Java date APIs...
+ String endTimeString = getNextDay(date);
+ DateTime endTime = DateTime.parseDateTime(endTimeString + "" +
+ "T00:00:00" + gmtOffset);
+ endTime.setDateOnly(true);
+
+ When eventTimes = new When();
+ eventTimes.setStartTime(startTime);
+ eventTimes.setEndTime(endTime);
+ newEntry.addTime(eventTimes);
+
+
+ // Need a 'final' version of the entry so it can be used
+ // inside the privileged block.
+ final EventEntry finalEntry = newEntry;
+
+ // Send the request. The response contains the final version of the
+ // entry. In particular, we need to store the edit URL for future
+ // updates
+ EventEntry addedEntry = null;
+ try {
+ addedEntry = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<EventEntry>() {
+ public EventEntry run() throws Exception {
+ return service.insert(feedUrl, finalEntry);
+ }
+ }
+ );
+ } catch (PrivilegedActionException e) {
+ handleException(e.getException(), "Unable to add event",
+ finalEntry);
+ }
+
+ return new CalEvent(addedEntry.getId(), date, title,
+ addedEntry.getEditLink().getHref(),
+ addedEntry.getVersionId());
+ }
+
+ /**
+ * Get the next day given a day string of the format <yyyy>-<mm>-<dd>
+ */
+ private String getNextDay(String day) throws Exception {
+ // See how simple it is to add a day to a date? :)
+ // Thanks be to Google for finding this code...
+ java.text.SimpleDateFormat sdf =
+ new java.text.SimpleDateFormat("yyyy-MM-dd");
+ Date date = sdf.parse(day);
+
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ cal.add(Calendar.DATE, 1);
+ return sdf.format(cal.getTime());
+ }
+
+ /**
+ * Update an event
+ *
+ * @param id
+ * The id of the event to be updated
+ *
+ * @param title
+ * The new title for the event - that's the only thing we
+ * allow to be changed at this time -- keeping things simple.
+ *
+ * @return
+ * A new instance of an event, with fields updated as necessary
+ * from Google (e.g. version id has changed)
+ */
+ public CalEvent updateEvent(CalEvent event) throws
+ Exception {
+ final EventEntry entry = createEventEntry(event);
+ final URL editURL = new URL(event.getEditURL());
+
+ // Send the request.
+ EventEntry updatedEntry = null;
+ try {
+ updatedEntry = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<EventEntry>() {
+ public EventEntry run() throws Exception {
+ return service.update(editURL, entry);
+ }
+ }
+ );
+ } catch (PrivilegedActionException e) {
+ handleException(e.getException(), "Unable to update event",
+ entry);
+ }
+
+ return createDerbyCalEvent(updatedEntry);
+ }
+
+ /**
+ * Delete an event
+ *
+ * @param editURLString
+ * The string representation of the edit URL
+ */
+ public void deleteEvent(final String editURLString) throws Exception {
+ final URL editURL = new URL(editURLString);
+
+ // Send the request. ry;
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ service.delete(editURL);
+ return null;
+ }
+ }
+ );
+ } catch (PrivilegedActionException e) {
+ handleException(e.getException(),
+ "Unable to delete event with edit URL " + editURLString, null);
+ }
+ }
+
+ /**
+ * Get the list of events. You would want to
+ * use this, for example, if you are trying to get the calendar when
+ * the application first starts up.
+ *
+ * @return a HashMap of events for the calendar, where the hash id
+ * is the id of the event
+ */
+ public Collection<CalEvent> getEvents() throws Exception {
+
+ CalendarEventFeed resultFeed = null;
+
+ // Do this in a privileged block, as it connects to the Internet
+ try {
+ resultFeed = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<CalendarEventFeed>() {
+ public CalendarEventFeed run() throws Exception {
+ CalendarQuery myQuery = new CalendarQuery(feedUrl);
+
+ myQuery.setMinimumStartTime(DateTime.parseDateTime(
+ startDay + "T00:00:00" + gmtOffset));
+ myQuery.setMaximumStartTime(DateTime.parseDateTime(
+ endDay + "T23:59:59" + gmtOffset));
+
+ // Send the request and receive the response:
+ return service.query(myQuery, CalendarEventFeed.class);
+ }
+ }
+ );
+ } catch (PrivilegedActionException e) {
+ handleException(e.getException(), "Unable to get entries from " +
+ "Google Calendar", null);
+ }
+
+ // Build a list of DerbyCal events based on these entries
+ ArrayList<CalEvent> calentries = new ArrayList<CalEvent>();
+
+ // debugging
+ // System.out.println("Entries from Google Calendar:");
+
+ for ( EventEntry entry : resultFeed.getEntries() ) {
+ calentries.add(createDerbyCalEvent(entry));
+ }
+
+ return calentries;
+ }
+
+ public void clearCalendar() throws Exception {
+ // Get the latest list of entries
+ Collection<CalEvent> events = getEvents();
+
+ // Delete the entries one at a time
+ for ( CalEvent event : events ) {
+ deleteEvent(event.getEditURL());
+ }
+ }
+
+ /**
+ * Create a Google EventEntry from an event object
+ */
+ private EventEntry createEventEntry(CalEvent event) throws Exception {
+ EventEntry entry = new EventEntry();
+ entry.setTitle(new PlainTextConstruct(event.getTitle()));
+ entry.setId(event.getId());
+
+ Person author = new Person(AUTHOR_NAME, null, AUTHOR_EMAIL);
+ entry.getAuthors().add(author);
+
+ DateTime startTime = DateTime.parseDateTime(event.getDate() + "T00:00:00" +
+ gmtOffset);
+ startTime.setDateOnly(true);
+
+ // An all day event has an end time of the next day. Need to
+ // calculate that using the wonderful Java date APIs...
+ String endTimeString = getNextDay(event.getDate());
+ DateTime endTime = DateTime.parseDateTime(endTimeString + "" +
+ "T00:00:00" + gmtOffset);
+ endTime.setDateOnly(true);
+
+ When eventTimes = new When();
+ eventTimes.setStartTime(startTime);
+ eventTimes.setEndTime(endTime);
+ entry.addTime(eventTimes);
+
+ return entry;
+ }
+
+ /**
+ * Create a DerbyCalEvent from an EventEntry
+ */
+ private CalEvent createDerbyCalEvent(EventEntry entry) throws Exception {
+ String title = entry.getTitle().getPlainText();
+
+ // Simplifying assumption: all entries are day-long events.
+ // No more, no less
+ List<When> times = entry.getTimes();
+ String start = times.get(0).getStartTime().toString();
+
+ String editURL = entry.getEditLink().getHref();
+
+ CalEvent event = new CalEvent(entry.getId(), start, title,
+ editURL, entry.getVersionId());
+
+ return event;
+ }
+
+ /**
+ * Handle an exception received when invoking a method on Google Calendar
+ *
+ * @param e
+ * The exception that was encountered. Obtain this by calling
+ * getException() on the PrivilegedActionException
+ *
+ * @param message
+ * This is the message specific to the invocation that got the exception,
+ * such as "unable to update event". This is prepended upon the details
+ * provided by this method
+ *
+ * @param entry
+ * The event entry associated with this message, if available. Otherwise
+ * set this to null.
+ *
+ * @throws Exception
+ * When the exception encountered should result in an exception being thrown
+ * to the caller. This is almost always the case.
+ */
+ private void handleException(Exception e, String message, EventEntry entry)
+ throws Exception {
+
+ String entryInfo = "";
+ String id;
+ String title;
+ String date;
+ if ( entry != null ) {
+ id = parseId(entry.getId());
+ title = entry.getTitle().getPlainText();
+ List<When> times = entry.getTimes();
+ date = times.get(0).getStartTime().toString();
+
+ entryInfo = date + ": " + title;
+ }
+ if ( e instanceof java.net.NoRouteToHostException ) {
+ throw new NetworkDownException(e);
+ }
+ if ( e instanceof ResourceNotFoundException ) {
+ message += ": the event " + entryInfo + " does not appear " +
+ "to exist. Perhaps somebody else deleted it?";
+ }
+ else if ( e instanceof ServiceException ) {
+ ServiceException se = (ServiceException)e;
+
+ // Let's see if this is a conflict where we are trying to
+ // update an entry that has been modified. The error code
+ // is 400, which is not helpful, it's supposed to be 403,
+ // "conflict", but oh well...
+ String responseBody = se.getResponseBody();
+
+ if ( responseBody != null &&
+ responseBody.contains("Sequence ids from event") &&
+ responseBody.contains("do not match")) {
+ message += ": unable to update event " +
+ entryInfo + ", it appears someone else has updated it." +
+ " The event will be updated to contain the latest " +
+ " version from Google Calendar.";
+ }
+ else {
+ message += ": exception from Google Calendar. HTTP error code " +
+ "is " + se.getHttpErrorCodeOverride() + "; response body is: " +
+ se.getResponseBody();
+ }
+ } else {
+ message += ": please see next exception for details.";
+ }
+
+ throw new Exception(message, e);
+
+ }
+
+ /**
+ * Strip off the unique id of the entry from the full Google
+ * Calendar id, which includes the Feed URL
+ */
+ private String parseId(String id) {
+ if ( id == null ) {
+ return null;
+ }
+ return id.substring(id.lastIndexOf("/") + 1, id.length());
+ }
+
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/GCalendar.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/GCalendarRequest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/GCalendarRequest.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/GCalendarRequest.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/GCalendarRequest.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,47 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+/**
+ * Encapsulates a request that we're posting to Google Calendar.
+ * Subtypes provide type-safe mechanisms for providing parameters
+ * to the request and for the result data, if any.
+ */
+public abstract class GCalendarRequest implements java.io.Serializable {
+ private int requestId;
+
+ public GCalendarRequest(int id) {
+ setId(id);
+ }
+
+ public void setId(int id) {
+ this.requestId = id;
+ }
+
+ public int getId() {
+ return requestId;
+ }
+
+ /**
+ * Submit the request to the Google Calendar service. What this
+ * means depends on the type of request.
+ */
+ public abstract void submit(GCalendar calendar) throws Exception ;
+
+ public abstract String toString();
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/GCalendarRequest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/NetworkDownException.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/NetworkDownException.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/NetworkDownException.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/NetworkDownException.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,32 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+/**
+ * Indicate that the network is down
+ */
+public class NetworkDownException extends Exception {
+ public NetworkDownException(Throwable cause) {
+ super("Unable to communicate with Google Calendar; going offline",
+ cause);
+ }
+
+ public NetworkDownException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/NetworkDownException.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: db/derby/code/trunk/java/demo/localcal/src/RequestManager.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/demo/localcal/src/RequestManager.java?view=auto&rev=453845
==============================================================================
--- db/derby/code/trunk/java/demo/localcal/src/RequestManager.java (added)
+++ db/derby/code/trunk/java/demo/localcal/src/RequestManager.java Fri Oct 6 21:59:28 2006
@@ -0,0 +1,316 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+ */
+
+import java.util.*;
+import java.security.*;
+import java.sql.*;
+import javax.sql.*;
+
+/**
+ * This class is resonsible for managing requests while offline
+ */
+public class RequestManager {
+
+ private static ArrayList<String> conflicts;
+
+ // Request types
+ public static final int ADD_EVENT = 1;
+ public static final int DELETE_EVENT = 2;
+ public static final int UPDATE_EVENT = 3;
+
+ private static final String INSERT_REQUEST_SQL =
+ "INSERT INTO " + DatabaseManager.REQUESTS_TABLE +
+ "(request_type, event_id, date, title, edit_url) " +
+ "VALUES(?, ?, ?, ?, ?)";
+
+ public static void storeAddEvent(String eventId, String date,
+ String title) throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+
+ try {
+ // Insert the request to add an event
+ PreparedStatement pstmt = conn.prepareStatement(
+ INSERT_REQUEST_SQL);
+
+ pstmt.setInt(1, ADD_EVENT);
+ pstmt.setString(2, eventId);
+ pstmt.setString(3, date);
+ pstmt.setString(4, title);
+ pstmt.setNull(5, Types.VARCHAR);
+ pstmt.executeUpdate();
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ public static void storeUpdateEvent(CalEvent event)
+ throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+
+ try {
+ // First see if this event was added while we were offline.
+ // If so, just update that request. Otherwise add it as a
+ // separate request. Trying to save round trips here, and also
+ // avoids the issue around the fact that Google changes the id
+ int sequenceId = findAddRequest(event.getId());
+ if ( sequenceId > 0 ) {
+ System.out.println("Request to update an event that was added " +
+ "while offline; updating the add event request instead");
+ updateAddRequest(sequenceId, event.getTitle());
+ } else {
+ // insert the request to update an event
+ PreparedStatement pstmt = conn.prepareStatement(
+ INSERT_REQUEST_SQL);
+ pstmt.setInt(1, UPDATE_EVENT);
+ pstmt.setString(2, event.getId());
+ pstmt.setNull(3, Types.VARCHAR);
+ pstmt.setString(4, event.getTitle());
+ pstmt.setString(5, event.getEditURL());
+
+ pstmt.executeUpdate();
+ }
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ /**
+ * This updates an existing request to add an event, rather than adding
+ * a new update request
+ */
+ private static void updateAddRequest(int sequenceId, String title)
+ throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "UPDATE " + DatabaseManager.REQUESTS_TABLE +
+ " SET title = ? WHERE sequence_id = ?");
+ pstmt.setString(1, title);
+ pstmt.setInt(2, sequenceId);
+ pstmt.executeUpdate();
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ public static void storeDeleteEvent(String eventId, String editURL)
+ throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+
+ try {
+ // First, see if this event was added while we were offline.
+ // If so, just delete the add event request and we're done.
+ // Otherwise log the delete event request
+ int sequenceId = findAddRequest(eventId);
+ if ( sequenceId > 0 ) {
+ System.out.println("Request to delete an event that was " +
+ "added while offline - deleting add event " +
+ "request instead...");
+ deleteRequestsForEvent(eventId);
+ } else {
+ // insert the request to delete an event
+ PreparedStatement pstmt = conn.prepareStatement(
+ INSERT_REQUEST_SQL);
+ pstmt.setInt(1, DELETE_EVENT);
+ pstmt.setString(2, eventId);
+ pstmt.setNull(3, Types.VARCHAR);
+ pstmt.setNull(4, Types.VARCHAR);
+ pstmt.setString(5, editURL);
+
+ pstmt.executeUpdate();
+ }
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+
+ }
+
+ private static int findAddRequest(String eventId) throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "SELECT sequence_id FROM " + DatabaseManager.REQUESTS_TABLE +
+ " WHERE request_type = ? AND event_id = ?");
+ pstmt.setInt(1, ADD_EVENT);
+ pstmt.setString(2, eventId);
+ ResultSet rs = pstmt.executeQuery();
+ if ( rs.next() ) {
+ return rs.getInt(1);
+ } else {
+ return 0;
+ }
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ private static void deleteRequestsForEvent(String eventId) throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "DELETE FROM " + DatabaseManager.REQUESTS_TABLE +
+ " WHERE event_id = ?");
+ pstmt.setString(1, eventId);
+ pstmt.executeUpdate();
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ /**
+ * Submit all stored requests to Google Calendar. This process
+ * is logged to System.out. If the network goes down during this
+ * time, we stop where we were and throw a NetworkDownException.
+ *
+ * If there is an error communicating with the local database we throw
+ * a SQLException.
+ *
+ * Any other exceptions are logged and we continue on until all
+ * requests are processed. Each request is deleted from the database
+ * once it is processed, successfully or not.
+ *
+ * @return the number of failures during this process
+ */
+ public static int submitRequests(GCalendar calendar) throws Exception {
+ List<GCalendarRequest> requests = getRequests();
+
+ //
+ // Go through the requests in order
+ int failures = 0;
+ int totalRequests = requests.size();
+
+ System.out.println("");
+ System.out.println("==== SUBMITTING PENDING REQUESTS TO GOOGLE CALENDAR ====");
+
+ conflicts = new ArrayList<String>();
+
+ for ( GCalendarRequest request : requests ) {
+ try {
+ request.submit(calendar);
+ System.out.println(request + " submitted successfully");
+ deleteRequest(request);
+ } catch ( NetworkDownException nde ) {
+ throw nde;
+ } catch ( SQLException sqle ) {
+ // this is pretty severe, we need to bail
+ throw sqle;
+ } catch ( Exception e ) {
+ System.out.println("ERROR submitting " + request + ": " +
+ e.getMessage());
+ conflicts.add(e.getMessage());
+ // e.printStackTrace();
+ failures++;
+ deleteRequest(request);
+ }
+ }
+
+ System.out.println("==== DONE - " + totalRequests + " requests submitted ==== ");
+ System.out.println("");
+
+ // Now clean out the request table
+
+ return failures;
+ }
+
+ public static List<String> getConflicts() {
+ return conflicts;
+ }
+
+ /**
+ * Delete a request from the database
+ */
+ private static void deleteRequest(GCalendarRequest request) throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+ try {
+ PreparedStatement pstmt = conn.prepareStatement(
+ "DELETE FROM " + DatabaseManager.REQUESTS_TABLE +
+ " WHERE sequence_id = ?");
+
+ pstmt.setInt(1, request.getId());
+ pstmt.executeUpdate();
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ private static List<GCalendarRequest> getRequests() throws Exception {
+ Connection conn = DatabaseManager.getConnection();
+
+ try {
+ ArrayList<GCalendarRequest> requests =
+ new ArrayList<GCalendarRequest>();
+ ResultSet rs = DatabaseManager.executeQueryNoParams(conn,
+ "SELECT sequence_id, request_type, event_id, date, title, " +
+ "edit_url FROM " + DatabaseManager.REQUESTS_TABLE +
+ " ORDER BY sequence_id");
+
+ while ( rs.next() ) {
+ GCalendarRequest request = createRequestFromRow(rs);
+ requests.add(request);
+ }
+
+ return requests;
+ } finally {
+ DatabaseManager.releaseConnection(conn);
+ }
+ }
+
+ /**
+ * Get a request from a result set. Assumes the folowing columns in
+ * the result set:
+ * <ul>
+ * <li>sequence_id
+ * <li>request type
+ * <li>event id
+ * <li>date
+ * <li>title
+ * </ul>
+ */
+ private static GCalendarRequest createRequestFromRow(ResultSet rs) throws Exception {
+ GCalendarRequest request;
+
+ // These might be null, depending on the type, but we can get them anyway
+ int sequenceId = rs.getInt(1);
+ int type = rs.getInt(2);
+ String eventId = rs.getString(3);
+ String date = rs.getString(4);
+ String title = rs.getString(5);
+ String editURL = rs.getString(6);
+
+ switch ( type ) {
+ case ADD_EVENT:
+ request = new AddEventRequest(sequenceId, eventId, date, title);
+ break;
+ case UPDATE_EVENT:
+ request = new UpdateEventRequest(sequenceId, eventId, title);
+ break;
+ case DELETE_EVENT:
+ request = new DeleteEventRequest(sequenceId, eventId, editURL);
+ break;
+ default:
+ throw new Exception("Unrecognized event type " + type +
+ "for request with sequence id " + sequenceId);
+ }
+
+ return request;
+ }
+}
Propchange: db/derby/code/trunk/java/demo/localcal/src/RequestManager.java
------------------------------------------------------------------------------
svn:eol-style = native