/*************************************************************************************************** * Copyright (c) 2005, 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation **************************************************************************************************/ package org.eclipse.jst.jsf.facesconfig.util; import java.util.LinkedList; import java.util.ListIterator; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.ISaveContext; import org.eclipse.core.resources.ISaveParticipant; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jst.jsf.facesconfig.FacesConfigPlugin; import org.eclipse.jst.jsf.facesconfig.internal.Logger; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorRegistry; import org.eclipse.ui.PlatformUI; import org.eclipse.wst.common.componentcore.internal.util.ComponentUtilities; import org.eclipse.wst.common.componentcore.resources.IVirtualComponent; /** * This class is responsible for the following: * <ol> * <li>Ensure that the Struts nature is added to any project to which a Struts * config. file is added.</li> * <li>Ensure that the Struts nature is added to any project to which a diagram * file is added.</li> * <li>Restart project(s) if/as apporopriate when a Struts config. file is * modified. * </ol> * It arguably should be multiple classes, but the things it does are closely * related and splitting it up would result in duplicate work as well as * multiple delta traversals. * * This class is not intended for external use. * Should NOT be referenced or extended externally. */ final class FacesResourceChangeListener implements IResourceChangeListener, IResourceDeltaVisitor, ISaveParticipant, IResourceVisitor { /** The singleton instance. */ private static FacesResourceChangeListener listener; private static boolean restartInProgress = false; private LinkedList facesConfigChangeListeners = new LinkedList(); private static IPreferenceStore preferenceStore = null; private static final QualifiedName EDITOR_KEY = new QualifiedName( "org.eclipse.ui.internal.registry.ResourceEditorRegistry", "EditorProperty");//$NON-NLS-2$//$NON-NLS-1$ /** Start up the singleton instance. */ public static void startup() { // Bail if we're already started. if (listener != null) return; // Create the singleton instance. listener = new FacesResourceChangeListener(); // Register as resource change listener. ResourcesPlugin.getWorkspace().addResourceChangeListener( listener, IResourceChangeEvent.PRE_BUILD + IResourceChangeEvent.POST_BUILD); } /** Shutdown the singleton instance. */ public static void shutdown() { // Bail if we're not started. if (listener == null) return; // Deregister as save participant. ResourcesPlugin.getWorkspace().removeSaveParticipant(FacesConfigPlugin.getPlugin().getBundle().getSymbolicName()); // Deregister as resource change listener. ResourcesPlugin.getWorkspace().removeResourceChangeListener(listener); // Dereference the singleton instance. listener = null; } /** * Only this class can create instances. */ private FacesResourceChangeListener() { // no local instantiation } /** * Process a resource change event. This should be invoked only from the * workbench. * * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent) */ public void resourceChanged(IResourceChangeEvent event) { IResourceDelta delta = event.getDelta(); if (delta != null) { FacesConfigChangeEvent facesConfigChangeEvent = new FacesConfigChangeEvent(); fireFacesConfigChangeEvent(facesConfigChangeEvent); try { delta.accept(this); } catch (CoreException ignored) { Logger.log(this, ignored); } } // Restart projects, if necessary. if ((delta != null) && (event.getType() == IResourceChangeEvent.POST_BUILD)) { FacesConfigRestartServerResourceDeltaVisitor visitor = new FacesConfigRestartServerResourceDeltaVisitor(); try { delta.accept(visitor); } catch (CoreException ignored) { Logger.log(this, ignored); } //restartComponents(visitor.getComponents()); } } /** * Visit a resource delta. This should be invoked only from the * IResourceDelta.accept() method invoked above. * * @see IResourceDeltaVisitor#visit(IResourceDelta) */ public boolean visit(IResourceDelta delta) throws CoreException { // Check for and handle it if it's a Struts config. file. checkForFacesConfigFile(delta); // Done. return true; } private void checkForFacesConfigFile(IResourceDelta delta) { boolean isAdded = delta.getKind() == IResourceDelta.ADDED; if (isAdded || ((delta.getKind() == IResourceDelta.CHANGED) && ((delta.getFlags() & (IResourceDelta.CONTENT | IResourceDelta.TYPE | IResourceDelta.SYNC | IResourceDelta.REPLACED)) != 0))) { checkForFacesConfigFile(delta.getResource(), !isAdded); } } private void checkForFacesConfigFile(IResource resource, boolean ignoreNonFacesProjects) { if (resource.getType() == IResource.FILE) { // See if the file is a Struts config. file. // If the file was just added, we check the file regardless of // whether or not it is in a Struts project. // Otherwise, a file in a non-Struts project is considered to not be // a Struts config. file. IFile file = (IFile) resource; if (FacesConfigUtil.isFacesConfigFile(file, ignoreNonFacesProjects)) { // Ensure that the project has the Struts nature. // TODO: // StrutsNatureRuntime.ensureProjectHasStrutsNature(file.getProject()); IVirtualComponent component = ComponentUtilities.findComponent(file); if (component != null) { restartServerIfNecessary(component); // Try to register the SCFE as the default editor. setRegistration(file); } } else { // Try to unregister the SCFE as the default editor. unsetRegistration(file); } } } /** * Look to see if the persisted resource level property keyed by EDITOR_KEY * has ben set yet. If not then set it to the SCFE. * * @param file * The FCF */ private void setRegistration(IFile file) { String editorID = null; try { editorID = file.getPersistentProperty(EDITOR_KEY); } catch (CoreException e) { // suppress core exception } if (editorID == null) { try { file.setPersistentProperty(EDITOR_KEY, FacesConfigPlugin.FACES_CONFIG_EDITOR_ID); } catch (CoreException e) { Logger.log(file, "Failed to set the vcurrent editor to SCFE", e); //$NON-NLS-1$ } } } private void unsetRegistration(IFile file) { // If the default editor for this file is not the Struts config. editor, // then we're done. IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry(); IEditorDescriptor userEditor = registry.getDefaultEditor(file.getFullPath().toString()); if ((userEditor == null) || !FacesConfigPlugin.FACES_CONFIG_EDITOR_ID.equals(userEditor.getId())) { traceFiner(file, "Not unsetting: Default already not Faces config. editor"); //$NON-NLS-1$ return; } // Make the Struts config. editor the default. traceFiner(file, "Unsetting."); //$NON-NLS-1$ IEditorDescriptor[] editors = registry.getEditors(file.getFullPath().toString()); if (editors.length > 1) { registry.setDefaultEditor(file.getFullPath().toString(), editors[1].getId()); } } //private boolean isRestarting = false; //private Collection restartableComponents = new HashSet(); /* private void restartComponents(Collection components) { restartableComponents.addAll(components); if (!isRestarting) { isRestarting = true; try { while (!restartableComponents.isEmpty()) { IVirtualComponent component = (IVirtualComponent) restartableComponents.iterator().next(); try { ServerRestartUtil.restartComponent(component, true); } finally { restartableComponents.remove(component); } } } finally { isRestarting = false; } } } */ private void traceFiner(IFile file, String message) { String fileName = file.getProjectRelativePath().toString(); Logger.trace("FacesconfigPlugin", this, fileName + ": " + message); //$NON-NLS-1$ //$NON-NLS-2$ } /** @see ISaveParticipant#doneSaving(ISaveContext) */ public void doneSaving(ISaveContext context) { // nothing to do } /** @see ISaveParticipant#prepareToSave(ISaveContext) */ public void prepareToSave(ISaveContext context) throws CoreException { // nothing to do } /** @see ISaveParticipant#rollback(ISaveContext) */ public void rollback(ISaveContext context) { // nothing to do } /** @see ISaveParticipant#saving(ISaveContext) */ public void saving(ISaveContext context) throws CoreException { context.needDelta(); } /** * Visit a resource. This should be invoked only from the * IResource.accept(IResourceVisitor) invocation, above. * * @see IResourceVisitor#visit(IResource) */ public boolean visit(IResource resource) { // Check for and handle a Struts config. file. checkForFacesConfigFile(resource, true); // Continue. return true; } private void restartServerIfNecessary(IVirtualComponent component) { if(!restartInProgress) { // check against preference about whether to automatically restart boolean restart = false; if (FacesResourceChangeListener.preferenceStore != null) { restart = FacesResourceChangeListener.preferenceStore.getBoolean(IFacesconfigPreferences.PREFSKEY_SERVER_RESTART); } if(restart) { restartInProgress = true; // we'll ask that just the containing EAR is restarted, but it may cycle the whole server if running on Portal //ServerRestartUtil.restartComponent(component, true); restartInProgress = false; } } } /** * @return Returns the listener. */ public static FacesResourceChangeListener getFacesResourceChangeListener() { if (listener == null) { listener = new FacesResourceChangeListener(); // Register as resource change listener. ResourcesPlugin.getWorkspace().addResourceChangeListener(listener, IResourceChangeEvent.PRE_BUILD); } return listener; } /** * Adds a change listener to the list of listeners that will be notified * when a change is fired. * * @param facesConfigChangeListener */ public void addFacesConfigChangeListener(IFacesConfigChangeListener facesConfigChangeListener) { facesConfigChangeListeners.add(facesConfigChangeListener); } /** * Removes the listener from the list. * * @param facesConfigChangeListener */ public void removeFacesConfigChangeListener(IFacesConfigChangeListener facesConfigChangeListener) { facesConfigChangeListeners.remove(facesConfigChangeListener); } private void fireFacesConfigChangeEvent(IFacesConfigChangeEvent event) { LinkedList localCopy; synchronized( this ) { localCopy = (LinkedList)facesConfigChangeListeners.clone(); } for ( ListIterator iter = localCopy.listIterator(); iter.hasNext(); ) { IFacesConfigChangeListener facesConfigChangeListener = (IFacesConfigChangeListener)iter.next(); facesConfigChangeListener.resourceChanged(event); } } /** * Set the internally used preference store to preferenceStore * * @param preferenceStore */ public static void setPreferenceStore(IPreferenceStore preferenceStore) { FacesResourceChangeListener.preferenceStore = preferenceStore; } }