/******************************************************************************* * Copyright (c) 2010 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.jsp.core.internal.java; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; import java.util.zip.CRC32; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jst.jsp.core.internal.JSPCorePlugin; import org.eclipse.jst.jsp.core.internal.Logger; import org.eclipse.jst.jsp.core.internal.java.search.JSPIndexManager; import org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForJSP; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.FileBufferModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; /** * <p>This is a static class used to persist JSP translations and retrieve the persisted * translations.</p> * * <p>It is not actually in charge of finding files to persist, rather it provides API * for some other mechanism that tracks JSP files to call into to persist the translations.</p> * * <p>This class can be deactivated through the <code>persistJSPTranslations</code> system property, * a value of <code>true</code> means the persister is activated (which is the default), value of * <code>false</code> means the persister is not activated.</p> * * @see JSPIndexManager */ public class JSPTranslatorPersister{ /** * <code>true</code> if the persister is activated, <code>false</code> * otherwise. This is determined by checking the system property * <code>persistJSPTranslations</code>, if no value supplied then * default is <code>true</code> */ public static final boolean ACTIVATED = Boolean.valueOf(System.getProperty("persistJSPTranslations", "true")).booleanValue(); //$NON-NLS-1$ //$NON-NLS-2$ /** the location where {@link JSPTranslator}s are externalized too for persistence purposes */ private static final IPath PERSIST_LOCATION = JSPCorePlugin.getDefault().getStateLocation().append("translators"); //$NON-NLS-1$ /** used to calculate persisted translator file names */ private static final CRC32 CHECKSUM_CALC = new CRC32(); /** lock to use while using the checksum */ private static final Object CHECKSUM_CALC_LOCK = new Object(); /** * <p>Private constructor to prevent creating an instance of this class</p> */ private JSPTranslatorPersister() { } /** * <p>Given the {@link IStructuredModel} of a JSP file attempts to retrieve the persisted * {@link JSPTranslator} for that model.</p> * <p><b>NOTE: </b><i>It is possible for there not to be a persisted translator</i></p> * * @param model {@link IStructuredModel} to get the persisted {@link JSPTranslator} for * @return the persisted {@link JSPTranslator} for the given <code>model</code>, or * <code>null</code> if none could be found or an existing one could not be read */ public static JSPTranslator getPersistedTranslator(IStructuredModel model) { String persistedTranslatorFilePath = getPersistedTranslatorFilePath(model.getBaseLocation()); File persistedTranslatorFile = new File(persistedTranslatorFilePath); //attempt to read in the externalized translator JSPTranslator translator = null; ObjectInputStream in = null; try { //get the persisted translator file if one exists if(persistedTranslatorFile.exists()) { long persistedTranslatorFileTimestamp = persistedTranslatorFile.lastModified(); long jspFileTimestamp = FileBufferModelManager.getInstance().getBuffer( model.getStructuredDocument()).getModificationStamp(); /* if the persisted translator timestamp is newer then the jsp file timestamp * then the translation has not become stale, otherwise it has so delete * it and don't use it */ if(persistedTranslatorFileTimestamp > jspFileTimestamp) { FileInputStream fis = new FileInputStream(persistedTranslatorFile); in = new ObjectInputStream(fis); translator = (JSPTranslator)in.readObject(); //do post read external setup if(translator != null) { translator.postReadExternalSetup(model); } } else { persistedTranslatorFile.delete(); } } } catch(InvalidClassException e) { /* this means that the externalized translator is for an older version * of the JSPTranslator, so delete it */ persistedTranslatorFile.delete(); }catch (IOException e) { Logger.logException("Could not read externalized JSPTranslator at " + persistedTranslatorFilePath, e); //$NON-NLS-1$ } catch (ClassNotFoundException e) { Logger.logException("Class of a serialized JSPTranslator cannot be found", e); //$NON-NLS-1$ } finally { if(in != null) { try { in.close(); } catch (IOException e) { Logger.logException("Could not close externalized JSPTranslator that was just read", e); //$NON-NLS-1$ } } } return translator; } /** * @param resource JSP resource who's translation should be persisted */ public static void persistTranslation(IResource resource) { if(ACTIVATED) { IPath path = resource.getFullPath(); String filePath = getPersistedTranslatorFilePath(path.toString()); IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); JSPTranslator translator = getJSPTranslator(file); if(translator != null) { persistTranslator(translator, filePath); } } } /** * @param resource JSP resource who's translation should no longer be persisted */ public static void removePersistedTranslation(IResource resource) { if(ACTIVATED) { File file = getPersistedFile(resource.getFullPath()); file.delete(); } } /** * @param resource JSP resource that has moved and thus its persisted translation should be updated * @param fromPath Path the JSP resource moved from */ public static void movePersistedTranslation(IResource resource, IPath fromPath) { if(ACTIVATED) { File from = getPersistedFile(fromPath); File to = getPersistedFile(resource.getFullPath()); from.renameTo(to); } } /** * <p>Given the path to a JSP file determines the path to its persisted {@link JSPTranslator}</p> * * @param jspFilePath {@link IPath} to JSP file for which the path to its persisted {@link JSPTranslator} * should be determined * * @return OS file path to the persisted {@link JSPTranslator} associated with the JSP file at * <code>jspFilePath</code> */ private static String getPersistedTranslatorFilePath(String jspFilePath) { String persistedTranslatorFileName = "error.translator"; //$NON-NLS-1$ synchronized(CHECKSUM_CALC_LOCK){ try { CHECKSUM_CALC.reset(); CHECKSUM_CALC.update(jspFilePath.getBytes("utf16")); //$NON-NLS-1$ persistedTranslatorFileName = Long.toString(CHECKSUM_CALC.getValue()) + ".translator"; //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { Logger.logException("Could not get utf16 encoded bytes to create checksum to store persisted file.", e); //$NON-NLS-1$ } } IPath location = PERSIST_LOCATION; // ensure the folder exists on disk File folder = new File(location.toOSString()); if (!folder.isDirectory()) { try { folder.mkdir(); } catch (SecurityException e) { } } location = location.addTrailingSeparator(); location = location.append(persistedTranslatorFileName); return location.toOSString(); } /** * <p>Gets the associated {@link JSPTranslator} for a specific JSP file.</p> * <p><b>NOTE: </b><i>This does not get the persisted translator but rather the * associated translator in memory</i></p> * * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator} * is needed for * @return {@link JSPTranslator} associated with the given <code>jspFilePath</code>, or * <code>null</code> if none can be found. */ private static JSPTranslator getJSPTranslator(IFile jspFile) { IStructuredModel model = null; JSPTranslator translator = null; try { model = StructuredModelManager.getModelManager().getModelForRead(jspFile); if(model instanceof IDOMModel) { IDOMDocument doc = ((IDOMModel)model).getDocument(); ModelHandlerForJSP.ensureTranslationAdapterFactory(model); JSPTranslationAdapter adapter = (JSPTranslationAdapter)doc.getAdapterFor(IJSPTranslation.class); //don't want to persist a translator that has not already been requested if(adapter != null && adapter.hasTranslation()) { translator = adapter.getJSPTranslation().getTranslator(); } } } catch (IOException e) { Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$ " because could not read model for same.", e); //$NON-NLS-1$ } catch (CoreException e) { Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$ " because could not read model for same.", e); //$NON-NLS-1$ } finally { if(model != null) { model.releaseFromRead(); } } return translator; } /** * <p>Persists a {@link JSPTranslator} to disk for a specific JSP file</p> * * @param translator {@link JSPTranslator} to persist to disk * @param jspFilePath {@link IPath} to the JSP file the given <code>translator</code> is for */ private static void persistTranslator(JSPTranslator translator, String filePath) { try { FileOutputStream fos = new FileOutputStream(filePath); ObjectOutputStream out = new ObjectOutputStream(fos); out.writeObject(translator); out.close(); } catch (IOException e) { Logger.logException("Was unable to externalize JSPTranslator " + translator + //$NON-NLS-1$ " to " + filePath, e); //$NON-NLS-1$ } } /** * @param path {@link IPath} to the JSP file that the persisted translator is needed for * @return The persisted translator {@link File} for the JSP file at the given path * or <code>null</code> if no persisted translator exists for the JSP file at the given path */ private static File getPersistedFile(IPath path) { return new File(getPersistedTranslatorFilePath(path.toString())); } }