/* license-start * * Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>. * * This program 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 version 3. * * This program 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, at <http://www.gnu.org/licenses/>. * * Contributors: * Crispico - Initial API and implementation * * license-end */ package org.flowerplatform.editor.remote; import java.io.File; import org.flowerplatform.common.CommonPlugin; import org.flowerplatform.common.file_event.FileEvent; import org.flowerplatform.common.file_event.IFileEventListener; import org.flowerplatform.communication.command.DisplaySimpleMessageClientCommand; import org.flowerplatform.communication.stateful_service.StatefulServiceInvocationContext; import org.flowerplatform.editor.EditorPlugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Responsible with listening for resource changed notifications and reloading their corresponding * {@link EditableResource}s. * * @author Mariana * * */ public abstract class FileBasedEditorStatefulService extends EditorStatefulService implements IFileEventListener // implements IResourceChangeListener { /** * */ private static final Logger logger = LoggerFactory.getLogger(FileBasedEditorStatefulService.class); /** * Registers the listener */ public FileBasedEditorStatefulService() { CommonPlugin.getInstance().getFileEventDispatcher().addFileEventListener(this); } @Override protected void loadEditableResource(StatefulServiceInvocationContext context, EditableResource editableResource) { FileBasedEditableResource er = (FileBasedEditableResource) editableResource; Object file = null; try { file = EditorPlugin.getInstance().getFileAccessController().getFile(er.getEditableResourcePath()); } catch (Exception e) { // Mariana: context may be null if the resource was reloaded by the resource changed listener if (context != null) { context.getCommunicationChannel().appendCommandToCurrentHttpResponse(new DisplaySimpleMessageClientCommand( "Resource not Found", String.format("The resource %s was not found", editableResource.getEditableResourcePath()), DisplaySimpleMessageClientCommand.ICON_WARNING)); } logger.error(String.format("Error while loading resource %s", editableResource.getEditableResourcePath()), e); } er.setFile(file); } // TODO CC: add listener mechanism in FileAccessController @Override public void notify(FileEvent event) { File file = (event.getEvent() == FileEvent.FILE_RENAMED) ? event.getOldFile() : event.getFile(); String filePathRelativeToWorkspace = "/" + CommonPlugin.getInstance().getPathRelativeToWorkspaceRoot(file); EditableResource editableResource = getEditableResource(filePathRelativeToWorkspace); if(editableResource != null) { switch (event.getEvent()) { case FileEvent.FILE_REFRESHED : { if(!editableResource.isSynchronized()) { processResourceChanged(editableResource); } break; } case FileEvent.FILE_MODIFIED : { processResourceChanged(editableResource); break; } case FileEvent.FILE_DELETED : { processResourceRemoved(editableResource); break; } case FileEvent.FILE_RENAMED : { // for now just remove the file from editor processResourceRemoved(editableResource); break; } default: break; } } else { switch (event.getEvent()) { case FileEvent.FILE_REFRESHED : { processResourcesRefreshed(editableResource); break; } case FileEvent.FILE_DELETED : { for(EditableResource edRed : editableResources.values()) { if(edRed.getEditableResourcePath().contains(filePathRelativeToWorkspace)) { processResourceRemoved(edRed); } } break; } case FileEvent.FILE_RENAMED : { for(EditableResource edRed : editableResources.values()) { if(edRed.getEditableResourcePath().contains(filePathRelativeToWorkspace)) { processResourceRemoved(edRed); } } break; } default: break; } } } private void processResourcesRefreshed(EditableResource editableResource) { for(EditableResource edRes : editableResources.values()) { if(!edRes.isSynchronized()) { processResourceChanged(edRes); } } } private void processResourceChanged(EditableResource editableResource) { logger.debug("Process resource changed = {} with modification stamp = {}", editableResource.getEditableResourcePath(), EditorPlugin.getInstance().getFileAccessController().getLastModifiedTimestamp(editableResource.getFile())); reloadEditableResource(editableResource, true); // TODO check if refresh handles this } private void processResourceRemoved(EditableResource editableResource) { logger.debug("Process resource removed = {} with modification stamp = {}", editableResource.getEditableResourcePath(), EditorPlugin.getInstance().getFileAccessController().getLastModifiedTimestamp(editableResource.getFile())); unsubscribeAllClientsForcefully(editableResource.getEditableResourcePath(), true); } // /** // * // */ // @Override // public void resourceChanged(IResourceChangeEvent event) { // processDelta(event.getDelta()); // } // // /** // * Recursive method that goes through the children of the {@link IResourceDelta} tree and processes the delta // * accordingly for each resource. // * // * // */ // private void processDelta(IResourceDelta deltaToProcess) { // // recursively call this method for all the deltas // for (IResourceDelta delta : deltaToProcess.getAffectedChildren()) { // processDelta(delta); // } // // IResource resource = deltaToProcess.getResource(); // String editableResourcePath = resource.getFullPath().toString(); // FileBasedEditableResource er = (FileBasedEditableResource) getEditableResource(editableResourcePath); // if (er != null && !er.isSynchronized() && !er.isIgnoreResourceChangedNotification()) { // switch (deltaToProcess.getKind()) { // case IResourceDelta.REMOVED : { // processResourceRemoved(er, resource); // break; // } // case IResourceDelta.CHANGED : { // processResourceChanged(er, resource); // break; // } // default : { // break; // } // } // } // } // // /** // * // */ // private void processResourceChanged(FileBasedEditableResource editableResource, IResource resource) { // logger.debug("Process resource changed = {} with modification stamp = {}", resource, resource.getModificationStamp()); // reloadEditableResource(editableResource, true); // SingletonRefsInEditorPluginFromWebPlugin.INSTANCE_PROJECT_EXPLORER_TREE_STATEFUL_SERVICE.dispatchContentUpdate(resource); // } // // /** // * // */ // private void processResourceRemoved(FileBasedEditableResource editableResource, IResource resource) { // logger.debug("Process resource removed = {} with modification stamp = {}", resource, resource.getModificationStamp()); // unsubscribeAllClientsForcefully(editableResource.getEditableResourcePath(), true); // } // // /** // * Save logic must be executed while ignoring the resource to be saved. This is to ensure that updates are not sent to clients // * during saving. After saving, update the synchronization stamp saved on the file, so any notifications sent for the resource // * after the change (e.g. from the <code>AutoBuildJob</code>) will also be ignored. // * // * <p> // * Note: ignoring notifications during save is important; otherwise, the file would be reloaded while it is already locked by the // * save logic, which could cause deadlocks, because the reload logic also attempts to lock the file. // * // * // */ // @Override // public void save(StatefulServiceInvocationContext context, String editableResourcePath) { // namedLockPool.lock(editableResourcePath); // try { // FileBasedEditableResource er = (FileBasedEditableResource) getEditableResource(editableResourcePath); // er.setIgnoreResourceChangedNotification(true); // try { // super.save(context, editableResourcePath); // er.updateSynchronizationStamp(); // } finally { // er.setIgnoreResourceChangedNotification(false); // } // } finally { // namedLockPool.unlock(editableResourcePath); // } // } // // /** // * Sets the <code>ignoreResourceChangedNotification</code> for the {@link FileBasedEditableResource} corresponding to the given path. // */ // public void setIgnoreResourceChangedNotificationForEditableResourcePath(String editableResourcePath, boolean ignore) { // namedLockPool.lock(editableResourcePath); // try { // FileBasedEditableResource er = (FileBasedEditableResource) getEditableResource(editableResourcePath); // if (er != null) { // er.setIgnoreResourceChangedNotification(ignore); // } // } finally { // namedLockPool.unlock(editableResourcePath); // } // } // // /** // * Iterates through the {@link EditorStatefulService} and delegates to each {@link FileBasedEditorStatefulService}s. // * // * @author Mariana // */ // public static void setIgnoreResourceChangedNotificationForAllServices(String editableResourcePath, boolean ignore) { // for (EditorStatefulService service : EditorSupport.INSTANCE.getEditorStatefulServices()) { // if (service instanceof FileBasedEditorStatefulService) { // ((FileBasedEditorStatefulService) service).setIgnoreResourceChangedNotificationForEditableResourcePath(editableResourcePath, ignore); // } // } // } // // @Override // protected File getEditableResourceFile(EditableResource editableResource) { // return ((FileBasedEditableResource) editableResource).getFile().getLocation().toFile(); // } }