You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2013/07/20 11:49:41 UTC
svn commit: r1505114 - in
/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler:
ProjectController.java ProjectFileChangeTracker.java ProjectWatchdog.java
util/FileWatchdog.java
Author: aadamchik
Date: Sat Jul 20 09:49:41 2013
New Revision: 1505114
URL: http://svn.apache.org/r1505114
Log:
CAY-1852 Straighten thread model and synchronization in the Modeler
refactoring 'ProjectWatchdog'
Added:
cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java
Removed:
cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectWatchdog.java
cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/util/FileWatchdog.java
Modified:
cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java
Modified: cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java?rev=1505114&r1=1505113&r2=1505114&view=diff
==============================================================================
--- cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java (original)
+++ cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectController.java Sat Jul 20 09:49:41 2013
@@ -238,7 +238,7 @@ public class ProjectController extends C
* Project files watcher. When project file is changed, user will be asked
* to confirm loading the changes
*/
- ProjectWatchdog watchdog;
+ private ProjectFileChangeTracker watchdog;
public ProjectController(CayenneModelerController parent) {
super(parent);
@@ -275,7 +275,8 @@ public class ProjectController extends C
}
} else {
if (watchdog == null) {
- watchdog = new ProjectWatchdog(this);
+ watchdog = new ProjectFileChangeTracker(this);
+ watchdog.setDaemon(true);
watchdog.start();
}
@@ -1617,7 +1618,7 @@ public class ProjectController extends C
/**
* @return the project files' watcher
*/
- public ProjectWatchdog getProjectWatcher() {
+ public ProjectFileChangeTracker getProjectWatcher() {
return watchdog;
}
Added: cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java?rev=1505114&view=auto
==============================================================================
--- cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java (added)
+++ cayenne/main/trunk/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ProjectFileChangeTracker.java Sat Jul 20 09:49:41 2013
@@ -0,0 +1,292 @@
+/*****************************************************************
+ * 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.
+ ****************************************************************/
+package org.apache.cayenne.modeler;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import org.apache.cayenne.configuration.DataChannelDescriptor;
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.modeler.action.OpenProjectAction;
+import org.apache.cayenne.modeler.action.SaveAction;
+import org.apache.cayenne.modeler.dialog.FileDeletedDialog;
+import org.apache.cayenne.project.Project;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * ProjectWatchdog class is responsible for tracking changes in cayenne.xml and
+ * other Cayenne project files
+ *
+ */
+public class ProjectFileChangeTracker extends Thread {
+
+ private static final Log log = LogFactory.getLog(ProjectFileChangeTracker.class);
+
+ /**
+ * The default delay between every file modification check
+ */
+ private static final long DEFAULT_DELAY = 4000;
+
+ /**
+ * The names of the files to observe for changes.
+ */
+ protected Map<String, FileInfo> files;
+ protected boolean paused;
+ protected ProjectController mediator;
+
+ /**
+ * Creates new watchdog for a specified project
+ */
+ public ProjectFileChangeTracker(ProjectController mediator) {
+
+ this.files = new ConcurrentHashMap<String, FileInfo>();
+ this.mediator = mediator;
+
+ setName("cayenne-modeler-file-change-tracker");
+ }
+
+ /**
+ * Reloads files to watch from the project. Useful when project's structure
+ * has changed
+ */
+ public void reconfigure() {
+ pauseWatching();
+
+ removeAllFiles();
+
+ Project project = mediator.getProject();
+
+ // check if project exists and has been saved at least once.
+ if (project != null && project.getConfigurationResource() != null) {
+ String projectPath = project.getConfigurationResource().getURL().getPath() + File.separator;
+ addFile(projectPath);
+
+ Iterator<DataMap> it = ((DataChannelDescriptor) project.getRootNode()).getDataMaps().iterator();
+ while (it.hasNext()) {
+ DataMap dm = it.next();
+ addFile(dm.getConfigurationSource().getURL().getPath());
+ }
+
+ }
+
+ resumeWatching();
+ }
+
+ protected void doOnChange() {
+
+ SwingUtilities.invokeLater(new Runnable() {
+
+ public void run() {
+ if (showConfirmation("One or more project files were changed by external program. "
+ + "Do you want to load the changes?")) {
+
+ // Currently we are reloading all project
+ if (mediator.getProject() != null) {
+
+ File fileDirectory = new File(mediator.getProject().getConfigurationResource().getURL()
+ .getPath());
+ Application.getInstance().getActionManager().getAction(OpenProjectAction.class)
+ .openProject(fileDirectory);
+ }
+
+ } else {
+ mediator.setDirty(true);
+ }
+ }
+ });
+ }
+
+ protected void doOnRemove() {
+ if (mediator.getProject() != null) {
+
+ SwingUtilities.invokeLater(new Runnable() {
+
+ public void run() {
+ FileDeletedDialog dialog = new FileDeletedDialog(Application.getFrame());
+ dialog.show();
+
+ if (dialog.shouldSave()) {
+ Application.getInstance().getActionManager().getAction(SaveAction.class).performAction(null);
+ } else if (dialog.shouldClose()) {
+ Application.getInstance().getFrameController().projectClosedAction();
+ } else {
+ mediator.setDirty(true);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Shows confirmation dialog
+ */
+ private boolean showConfirmation(String message) {
+ return JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(Application.getFrame(), message, "File changed",
+ JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
+ }
+
+ /**
+ * Adds a new file to watch
+ *
+ * @param location
+ * path of file
+ */
+ public void addFile(String location) {
+ try {
+ files.put(location, new FileInfo(location));
+ } catch (SecurityException e) {
+ log.error("SecurityException adding file " + location, e);
+ }
+ }
+
+ /**
+ * Turns off watching for a specified file
+ *
+ * @param location
+ * path of file
+ */
+ public void removeFile(String location) {
+ files.remove(location);
+ }
+
+ /**
+ * Turns off watching for all files
+ */
+ public void removeAllFiles() {
+ files.clear();
+ }
+
+ protected void check() {
+ if (paused) {
+ return;
+ }
+
+ boolean hasChanges = false;
+ boolean hasDeletions = false;
+
+ for (Iterator<FileInfo> it = files.values().iterator(); it.hasNext();) {
+ FileInfo fi = it.next();
+
+ boolean fileExists;
+ try {
+ fileExists = fi.getFile().exists();
+ } catch (SecurityException e) {
+ log.error("SecurityException checking file " + fi.getFile().getPath(), e);
+
+ // we still process with other files
+ continue;
+ }
+
+ if (fileExists) {
+ // this can also throw a SecurityException
+ long l = fi.getFile().lastModified();
+ if (l > fi.getLastModified()) {
+ // however, if we reached this point this is very unlikely.
+ fi.setLastModified(l);
+ hasChanges = true;
+ }
+ }
+ // the file has been removed
+ else if (fi.getLastModified() != -1) {
+ hasDeletions = true;
+ it.remove(); // no point to watch the file now
+ }
+ }
+
+ if (hasDeletions) {
+ doOnRemove();
+ } else if (hasChanges) {
+ doOnChange();
+ }
+ }
+
+ public void run() {
+ while (true) {
+ try {
+ Thread.sleep(DEFAULT_DELAY);
+ check();
+ } catch (InterruptedException e) {
+ // someone asked to stop
+ return;
+ }
+ }
+ }
+
+ /**
+ * Tells watcher to pause watching for some time. Useful before changing
+ * files
+ */
+ public void pauseWatching() {
+ paused = true;
+ }
+
+ /**
+ * Resumes watching for files
+ */
+ public void resumeWatching() {
+ paused = false;
+ }
+
+ /**
+ * Class to store information about files (last modification time & File
+ * pointer)
+ */
+ protected class FileInfo {
+
+ /**
+ * Exact java.io.File object, may not be null
+ */
+ File file;
+
+ /**
+ * Time the file was modified
+ */
+ long lastModified;
+
+ /**
+ * Creates new object
+ *
+ * @param location
+ * the file path
+ */
+ public FileInfo(String location) {
+ file = new File(location);
+ lastModified = file.exists() ? file.lastModified() : -1;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ public void setLastModified(long l) {
+ lastModified = l;
+ }
+ }
+
+}