/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.foundation.rm;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.foundation.FlexoEditor;
import org.openflexo.foundation.FlexoEditor.FlexoEditorFactory;
import org.openflexo.foundation.resource.FlexoResourceCenterService;
import org.openflexo.foundation.rm.FlexoProject.FlexoProjectReferenceLoader;
import org.openflexo.foundation.utils.DefaultProjectLoadingHandler;
import org.openflexo.foundation.utils.FlexoProgress;
import org.openflexo.foundation.utils.ProjectInitializerException;
import org.openflexo.foundation.utils.ProjectLoadingCancelledException;
import org.openflexo.foundation.utils.ProjectLoadingHandler;
/**
* This class is used to perform synchronization of all resources of a Flexo project. Global consistant states of all the resources of a
* Flexo project through all application modules is maintained by this scheme.
*
* Only one instance of the FlexoResourceManager is maintained.
*
* The FlexoResourceManager manages one instance of FlexoProject.
*
* @author sguerin
*/
public class FlexoResourceManager {
protected static final Logger logger = Logger.getLogger(FlexoResourceManager.class.getPackage().getName());
protected FlexoEditor _editor;
protected Thread _clockThread;
protected volatile boolean _stop;
protected ResourceUpdateHandler _resourceUpdateHandler;
private boolean isLoadingAProject = true;
private Object lock;
private FlexoResourceManager(FlexoEditor editor, ResourceUpdateHandler resourceUpdateHandler) {
super();
_editor = editor;
lock = new Object();
if (resourceUpdateHandler == null) {
_resourceUpdateHandler = new DefaultFlexoResourceUpdateHandler();
} else {
_resourceUpdateHandler = resourceUpdateHandler;
}
}
public static final long RESOURCE_CHECKING_DELAY = 5000;
public void startResourcePeriodicChecking() {
if (!_editor.performResourceScanning()) {
return;
}
if (logger.isLoggable(Level.INFO)) {
logger.info("START resource periodic checking for " + "ID=" + _editor.getProject().getID());
}
_stop = false;
_clockThread = new Thread(new Runnable() {
@Override
public void run() {
Thread myThread = Thread.currentThread();
while (_clockThread == myThread && !_stop) {
// if (logger.isLoggable(Level.FINER))
// logger.finer("Checking resources for project " + _editor.getProject());
try {
List<FlexoFileResource<? extends FlexoResourceData>> updatedResources = new ArrayList<FlexoFileResource<? extends FlexoResourceData>>();
for (FlexoFileResource<? extends FlexoResourceData> fileResource : _editor.getProject().getFileResources()) {
if (fileResource.hasMoreRecentThanExpectedDiskUpdate()) {
updatedResources.add(fileResource);
logger.info("File " + fileResource + " update detected on " + _clockThread.getName());
}
}
if (updatedResources.size() > 0 && _resourceUpdateHandler != null) {
if (updatedResources.size() == 1) {
_resourceUpdateHandler.handlesResourceUpdate(updatedResources.get(0));
} else {
_resourceUpdateHandler.handlesResourcesUpdate(updatedResources);
// else if (logger.isLoggable(Level.WARNING))
// logger
// .warning("Resource update for resource " + resource
// + " detected but no resource update handler.");
}
}
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(RESOURCE_CHECKING_DELAY);
} catch (InterruptedException e) {
}
}
if (logger.isLoggable(Level.INFO)) {
logger.info("STOP resource periodic checking for " + getProject().getProjectName() + " ID=" + getProject().getID());
}
return;
}
}, "RM_CHECKING_" + _editor.getProject().getID());
_clockThread.setDaemon(true); // Not really useful, but cleaner
_clockThread.setPriority(Thread.MIN_PRIORITY);
_clockThread.start();
}
public void stopResourcePeriodicChecking() {
if (logger.isLoggable(Level.INFO)) {
logger.info("Will stop resource periodic checking for project " + getProject().getProjectName() + " ID=" + getProject().getID());
}
synchronized (lock) {
_stop = true;
if (_clockThread != null) {
_clockThread.interrupt();// Causes the resource periodic
// checking to
// stop immediately (otherwise, sometimes, 2
// resource periodic checking could run
// simultaneously and might cause a popup
// because of some file modification when
// reloading the same project
_clockThread = null;
}
}
}
private static File findResourceManagerFile(File aProjectDirectory) {
File[] fileArray = aProjectDirectory.listFiles();
for (int i = 0; i < fileArray.length; i++) {
if (fileArray[i].getName().endsWith(".rmxml")) {
return fileArray[i];
}
}
return null;
}
public static boolean needsRestructuring(File aProjectDirectory) {
return findResourceManagerFile(aProjectDirectory) == null;
}
public static File getExpectedResourceManagerFile(File aProjectDirectory) {
File resourceManagerFile = findResourceManagerFile(aProjectDirectory);
if (resourceManagerFile == null) {
String projectName = aProjectDirectory.getName();
if (projectName.endsWith(".prj")) {
projectName = projectName.substring(0, projectName.length() - 4);
} else {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Project directory does not end with '.prj'");
}
}
resourceManagerFile = new File(aProjectDirectory, projectName + ".rmxml");
}
return resourceManagerFile;
}
public static FlexoEditor initializeExistingProject(File aProjectDirectory, FlexoEditorFactory editorFactory,
FlexoResourceCenterService resourceCenterService) throws ProjectInitializerException, ProjectLoadingCancelledException {
// Here implement a default handler that attempts to retrieve properly
return initializeExistingProject(aProjectDirectory, null, editorFactory, new DefaultProjectLoadingHandler(), null,
resourceCenterService);
}
public static FlexoEditor initializeExistingProject(File aProjectDirectory, FlexoProgress progress, FlexoEditorFactory editorFactory,
ProjectLoadingHandler loadingHandler, FlexoProjectReferenceLoader projectReferenceLoader,
FlexoResourceCenterService resourceCenterService) throws ProjectInitializerException, ProjectLoadingCancelledException {
if (loadingHandler == null) {
loadingHandler = new DefaultProjectLoadingHandler();
}
FlexoProject project = null;
if (!aProjectDirectory.exists()) {
if (logger.isLoggable(Level.WARNING)) {
logger.severe("Project directory doesn't exist: " + aProjectDirectory.getAbsolutePath());
}
}
File rmFile = getExpectedResourceManagerFile(aProjectDirectory);
if (!rmFile.exists()) {
throw new ProjectInitializerException(
"There is no rmxml file in project. Cannot load project without one. Use previous versions of Flexo first and then load with this new version.",
aProjectDirectory);
} else {
try {
FlexoProject.restoreJarsIfNeeded(aProjectDirectory);
} catch (IOException e) {
e.printStackTrace();
throw new ProjectInitializerException(e.getMessage(), aProjectDirectory);
}
FlexoRMResource rmRes;
try {
rmRes = new FlexoRMResource(rmFile, aProjectDirectory);
rmRes.setProjectReferenceLoader(projectReferenceLoader);
project = rmRes.loadProject(progress, loadingHandler, resourceCenterService);
} catch (RuntimeException e1) {
e1.printStackTrace();
throw new ProjectInitializerException(e1.getMessage(), aProjectDirectory);
}
}
FlexoEditor returned = editorFactory.makeFlexoEditor(project);
FlexoResourceManager resourceManager = new FlexoResourceManager(returned, returned.getResourceUpdateHandler());
resourceManager.startResourcePeriodicChecking();
resourceManager.isLoadingAProject = false;
project.setResourceManagerInstance(resourceManager);
checkExternalRepositories(project);
return returned;
}
private static void checkExternalRepositories(FlexoProject project) {
for (ProjectExternalRepository repository : project.getExternalRepositories()) {
if (!repository.isConnected()) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Found external repository " + repository + " DISCONNECTED, deactivate resources");
}
for (FlexoResource<?> resource : repository.getRelatedResources()) {
resource.deactivate();
}
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Found external repository " + repository.getDirectory().getAbsolutePath() + " well CONNECTED");
}
}
}
}
public static FlexoEditor initializeNewProject(File aProjectDirectory, FlexoProgress progress, FlexoEditorFactory editorFactory,
FlexoProjectReferenceLoader projectReferenceLoader, FlexoResourceCenterService resourceCenter) {
if (!aProjectDirectory.exists()) {
aProjectDirectory.mkdirs();
}
File rmFile = getExpectedResourceManagerFile(aProjectDirectory);
FlexoEditor returned = FlexoProject.newProject(rmFile, aProjectDirectory, editorFactory, progress, resourceCenter);
FlexoProject project = returned.getProject();
FlexoResourceManager resourceManager = new FlexoResourceManager(returned, returned.getResourceUpdateHandler());
resourceManager.startResourcePeriodicChecking();
resourceManager.isLoadingAProject = false;
project.setResourceManagerInstance(resourceManager);
checkExternalRepositories(project);
return returned;
}
public static FlexoEditor initializeNewProject(File aProjectDirectory, FlexoEditorFactory editorFactory,
FlexoResourceCenterService resourceCenter) {
return initializeNewProject(aProjectDirectory, null, editorFactory, null, resourceCenter);
}
public FlexoEditor getEditor() {
if (_editor == null) {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("Sorry, NO FlexoEditor instance available");
}
}
return _editor;
}
public FlexoProject getProject() {
if (_editor == null) {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("Sorry, NO FlexoEditor instance available");
}
} else if (_editor.getProject() == null) {
if (logger.isLoggable(Level.SEVERE)) {
logger.severe("Sorry, NO FlexoProject instance available");
}
}
return _editor.getProject();
}
private static BackwardSynchronizationHook backwardSynchronizationHook = null;
public static interface BackwardSynchronizationHook {
public void notifyBackwardSynchronization(FlexoResource resource1, FlexoResource resource2);
}
public static BackwardSynchronizationHook getBackwardSynchronizationHook() {
return backwardSynchronizationHook;
}
public static void setBackwardSynchronizationHook(BackwardSynchronizationHook aBackwardSynchronizationHook) {
backwardSynchronizationHook = aBackwardSynchronizationHook;
}
public boolean isLoadingAProject() {
return isLoadingAProject;
}
}