/******************************************************************************* * Copyright (c) 2001, 2007 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.j2ee.commonarchivecore.internal.strategy; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.impl.AdapterImpl; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.common.util.WrappedException; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.URIConverter; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.jem.internal.java.adapters.jdk.JavaJDKAdapterFactory; import org.eclipse.jst.j2ee.common.internal.impl.J2EEResouceFactorySaxRegistry; import org.eclipse.jst.j2ee.common.internal.impl.J2EEResourceFactoryDomRegistry; import org.eclipse.jst.j2ee.common.internal.impl.J2EEResourceFactoryRegistry; import org.eclipse.jst.j2ee.commonarchivecore.internal.Archive; import org.eclipse.jst.j2ee.commonarchivecore.internal.CommonArchiveFactoryRegistry; import org.eclipse.jst.j2ee.commonarchivecore.internal.CommonArchiveResourceHandler; import org.eclipse.jst.j2ee.commonarchivecore.internal.CommonarchiveFactory; import org.eclipse.jst.j2ee.commonarchivecore.internal.Container; import org.eclipse.jst.j2ee.commonarchivecore.internal.File; import org.eclipse.jst.j2ee.commonarchivecore.internal.exception.ArchiveRuntimeException; import org.eclipse.jst.j2ee.commonarchivecore.internal.exception.OpenFailureException; import org.eclipse.jst.j2ee.commonarchivecore.internal.exception.ResourceLoadException; import org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveOptions; import org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.ArchiveURIConverterImpl; import org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.FileIterator; import org.eclipse.jst.j2ee.commonarchivecore.internal.helpers.FileIteratorImpl; import org.eclipse.jst.j2ee.commonarchivecore.internal.util.ArchiveUtil; import org.eclipse.jst.j2ee.commonarchivecore.looseconfig.internal.LooseArchive; import org.eclipse.jst.j2ee.commonarchivecore.looseconfig.internal.LooseConfigRegister; import org.eclipse.jst.j2ee.core.internal.plugin.J2EECorePlugin; import org.eclipse.wst.common.internal.emf.utilities.ExtendedEcoreUtil; /** * Abstact implementer off which and load strategy may subclass * * @see LoadStrategy */ public abstract class LoadStrategyImpl extends AdapterImpl implements LoadStrategy { /** flag to indicate whether underlying resources have been closed */ protected boolean isOpen = true; /** The archive or directory to which this strategy belongs */ protected Container container; /** ResourceSet used for mof/xmi resources */ protected ResourceSet resourceSet; protected LooseArchive looseArchive; protected Map collectedLooseArchiveFiles = Collections.EMPTY_MAP; protected boolean readOnly = false; private int rendererType; public LoadStrategyImpl() { super(); } /** * @see Archive */ public void addOrReplaceMofResource(Resource aResource) { Resource existingResource = getResourceSet().getResource(aResource.getURI(), false); if (existingResource != null) getResourceSet().getResources().remove(existingResource); getResourceSet().getResources().add(aResource); } protected void updateModificationTracking(Resource res) { boolean trackingMods = res.isTrackingModification(); boolean isReadOnly = (container != null) ? ((Archive) container).getOptions().isReadOnly() : false; boolean shouldTrackMods = !(isReadOnly || ArchiveUtil.isJavaResource(res) || ArchiveUtil.isRegisteredURIMapping(res)); if (shouldTrackMods && !trackingMods) res.setTrackingModification(true); } /** * Release any resources being held by this object and set the state to closed. Subclasses * should override as necessary */ public void close() { setIsOpen(false); if(resourceSet != null && resourceSet.eAdapters().contains(this)) resourceSet.eAdapters().remove(this); } protected abstract boolean primContains(String uri); /** * @see LoadStrategy */ public boolean contains(String uri) { if (containsUsingLooseArchive(uri)) return true; return primContains(uri); } /* * Try the resources path first; if that false, see if we have a child loose archive with the * uri */ protected boolean containsUsingLooseArchive(String uri) { if (getLooseArchive() == null) return false; LooseArchive loose = getLooseArchive(); if (loose.getResourcesPath() == null) return false; java.io.File aFile = new java.io.File(loose.getResourcesPath(), uri); if (aFile.exists()) return true; return LooseConfigRegister.singleton().findFirstLooseChild(uri, loose) != null; } protected File createDirectory(String uri){ File aFile = null; aFile = getArchiveFactory().createFile(); aFile.setDirectoryEntry(true); aFile.setURI(uri); aFile.setOriginalURI(uri); aFile.setLoadingContainer(getContainer()); return aFile; } protected File createFile(String uri) { File aFile = null; if (isArchive(uri)) aFile = openNestedArchive(uri); if (aFile == null) { aFile = getArchiveFactory().createFile(); aFile.setURI(uri); aFile.setOriginalURI(uri); } aFile.setLoadingContainer(getContainer()); return aFile; } @Override protected void finalize() throws Throwable { if(isOpen){ close(); } } /** * @see LoadStrategy */ public java.lang.String getAbsolutePath() throws FileNotFoundException { throw new FileNotFoundException(CommonArchiveResourceHandler.Absolute_path_unknown_EXC_); // = "Absolute path unknown" } public String getResourcesPath() throws FileNotFoundException { return getLooseArchive() == null ? getAbsolutePath() : getLooseArchive().getResourcesPath(); } protected String primGetResourcesPath() { return getLooseArchive() == null ? null : getLooseArchive().getResourcesPath(); } public String getBinariesPath() throws FileNotFoundException { return getLooseArchive() == null ? getAbsolutePath() : getLooseArchive().getBinariesPath(); } public CommonarchiveFactory getArchiveFactory() { return CommonArchiveFactoryRegistry.INSTANCE.getCommonArchiveFactory(); } public Container getContainer() { return container; } public ResourceSet primGetResourceSet() { return resourceSet; } /** * * Should we iterate all the files in the archive as part of saving, or can we treat the archive * as one big file during save? The following rules apply, iterating the files if: 1) If the * archive is a module file and it is NOT read-only 2) If the load strategy is a directory 3) If * the archive is a utility JAR, and the files list has never been initialized, or if the * loading containers for all the files are the same AND not directories, AND the * {@link ArchiveOptions#isSaveLibrariesAsFiles()}of the archive is true. * * @see com.ibm.etools.archive.LoadStrategy#requiresIterationOnSave() */ public boolean requiresIterationOnSave() { if (!getContainer().isArchive() || isDirectory()) return true; Archive anArchive = (Archive) getContainer(); //We should leave utility JARs intact, unless were told not to //The manifest may have been signed if (anArchive.isModuleFile()) return !anArchive.getOptions().isReadOnly(); else if (anArchive.getOptions().isSaveLibrariesAsFiles() && anArchive.getLoadingContainer() != null) { if (anArchive.isIndexed()) { List files = anArchive.getFiles(); File aFile = null; Container firstContainer = null; Container lContainer = null; for (int i = 0; i < files.size(); i++) { aFile = (File) files.get(i); if (i == 0) { firstContainer = aFile.getLoadingContainer(); if (firstContainer.getLoadStrategy().isDirectory()) return true; } lContainer = aFile.getLoadingContainer(); if (lContainer != firstContainer) return true; } } return false; } else return true; } public ResourceSet getResourceSet() { if (resourceSet == null) { initializeResourceSet(); resourceSet.eAdapters().add(this); } return resourceSet; } /** * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(Notification) */ @Override public void notifyChanged(Notification msg) { switch (msg.getEventType()) { case Notification.ADD : updateModificationTracking((Resource) msg.getNewValue()); break; case Notification.ADD_MANY : List list = (List) msg.getNewValue(); for (int i = 0; i < list.size(); i++) { updateModificationTracking((Resource) list.get(i)); } } } /** * Used internally; clients should not need to call */ public FileIterator getFileIterator() throws IOException { return new FileIteratorImpl(getContainer().getFiles()); } /** * @see com.ibm.etools.archive.LoadStrategy */ public abstract List getFiles(); public List collectFiles() { //The loose archives need to be read first collectFilesFromLooseArchives(); List files = getFiles(); files.addAll(collectedLooseArchiveFiles.values()); collectedLooseArchiveFiles = Collections.EMPTY_MAP; return files; } protected void collectFilesFromLooseArchives() { if (!canHaveLooseChildren() || getLooseArchive() == null) { collectedLooseArchiveFiles = Collections.EMPTY_MAP; return; } collectedLooseArchiveFiles = new HashMap(); List children = LooseConfigRegister.singleton().getLooseChildren(getLooseArchive()); for (int i = 0; i < children.size(); i++) { LooseArchive loose = (LooseArchive) children.get(i); String uri = loose.getUri(); if (!collectedLooseArchiveFiles.containsKey(uri)) { Archive archive = openNestedArchive(loose); if (archive != null) { collectedLooseArchiveFiles.put(uri, archive); archive.setLoadingContainer(getContainer()); } } } } /** * @see com.ibm.etools.archive.LoadStrategy */ public abstract InputStream getInputStream(String uri) throws IOException, FileNotFoundException; public InputStream getResourceInputStream(String uri) throws IOException { return getResourceSet().getURIConverter().createInputStream(URI.createURI(uri)); } /** * @see com.ibm.etools.commonarchive.Archive returns an immutable collection of the loaded * resources in the resource set */ public Collection getLoadedMofResources() { Collection resources = getResourceSet().getResources(); if (resources.isEmpty()) return Collections.EMPTY_LIST; List copyResources = new ArrayList(); copyResources.addAll(resources); List result = new ArrayList(copyResources.size()); Iterator iter = copyResources.iterator(); while (iter.hasNext()) { Resource res = (Resource) iter.next(); if (res.isLoaded()) result.add(res); } return result; } /** * @see com.ibm.etools.commonarchive.Archive */ public Resource getMofResource(String uri) throws FileNotFoundException, ResourceLoadException { try { return getResourceSet().getResource(URI.createURI(uri), true); } catch (WrappedException wrapEx) { if ((ExtendedEcoreUtil.getFileNotFoundDetector().isFileNotFound(wrapEx))) { FileNotFoundException fileNotFoundEx = ExtendedEcoreUtil.getInnerFileNotFoundException(wrapEx); throw fileNotFoundEx; } throwResourceLoadException(uri, wrapEx); return null; //never happens - compiler expects it though } } protected void initializeResourceSet() { //Not the best design here, because a load strategy should only know // about //container; however, this method will only get called when the // container //is an archive Archive archive = (Archive) getContainer(); URIConverter converter = new ArchiveURIConverterImpl(archive, primGetResourcesPath()); ResourceSet rs = new ResourceSetImpl(); Resource.Factory.Registry reg = createResourceFactoryRegistry(); rs.setResourceFactoryRegistry(reg); setResourceSet(rs); rs.setURIConverter(converter); if (archive.shouldUseJavaReflection()) { rs.getAdapterFactories().add(new JavaJDKAdapterFactory()); // TFB: Problem here: // 'Archive.initializeClassLoader' calls // 'Archive.getJavaAdapterFactory', which // 'Archive.getResourceSet', which calls // 'LoadStrategy.initializeResourceSet', which calls // 'Archive.initializeClassLoader' all over again. // // This creates a second, redundant classloader, // and places the first classloader in the JavaJDKAdapterFactory. // Hence not only is the classloader created twice, but // both copies are active. When there are large classpaths, // this will large duplicate structures. // // Since the classloader will be initialized by 'getClassLoader', // the initialization, here, seems unnecessary. // archive.initializeClassLoader(); } } protected Resource.Factory.Registry createResourceFactoryRegistry() { if (isReadOnly()) return new J2EEResouceFactorySaxRegistry(); Resource.Factory.Registry registry = null; switch (getRendererType()) { case ArchiveOptions.SAX : registry = new J2EEResouceFactorySaxRegistry(); break; case ArchiveOptions.DOM : registry = new J2EEResourceFactoryDomRegistry(); break; case ArchiveOptions.DEFAULT : default : registry = new J2EEResourceFactoryRegistry(); break; } return registry; } /** * @return */ public int getRendererType() { return rendererType; } protected boolean isArchive(String uri) { return ((Archive) getContainer()).isNestedArchive(uri); } /** * An archive uses a custom class loader for java reflection within a mof resourceSet; * implementers of LoadStrategy may supply a mof resourceSet for which this class loader is not * necessary, or could even cause breakage; this test gives the strategy the chance to "opt out" * of the class loading game */ public boolean isClassLoaderNeeded() { return true; } /** * @see com.ibm.etools.archive.LoadStrategy The default is false */ public boolean isDirectory() { return false; } /** * @see com.ibm.etools.archive.LoadStrategy#getExistingMofResource(String) */ public Resource getExistingMofResource(String uri) { return getResourceSet().getResource(URI.createURI(uri), false); } public boolean isMofResourceLoaded(java.lang.String uri) { Resource res = getExistingMofResource(uri); return res != null && res.isLoaded(); } public boolean isOpen() { return isOpen; } /** * @see com.ibm.etools.archive.LoadStrategy return false by default; subclasses should override * if necessary */ public boolean isUsing(java.io.File aSystemFile) { return false; } public Resource makeMofResource(String uri, EList extent) { Resource existing = getExistingMofResource(uri); if (existing != null) return existing; return getResourceSet().createResource(URI.createURI(uri)); } protected Archive openNestedArchive(String uri) { try { return ((Archive) getContainer()).openNestedArchive(uri); } catch (OpenFailureException e) { //Caught an exception trying to open the nested archive J2EECorePlugin.logError(e); return null; } } protected Archive openNestedArchive(LooseArchive loose) { try { return ((Archive) getContainer()).openNestedArchive(loose); } catch (OpenFailureException e) { //Caught an exception trying to open the nested archive J2EECorePlugin.logError(e); return null; } } public void setContainer(Container newContainer) { container = newContainer; } public void setResourceSet(org.eclipse.emf.ecore.resource.ResourceSet newResourceSet) { // fixes problem in reopen if (resourceSet != newResourceSet) { // remove adapter from old resource set if (resourceSet != null) resourceSet.eAdapters().remove(this); // add as adapter to new resource set if necessary if (newResourceSet != null && !newResourceSet.eAdapters().contains(this)) newResourceSet.eAdapters().add(this); resourceSet = newResourceSet; } // no need to update if old set equals new set (by reference) } protected void setIsOpen(boolean newIsOpen) { isOpen = newIsOpen; } protected void throwResourceLoadException(String resourceUri, Exception ex) throws ResourceLoadException { throw new ResourceLoadException(CommonArchiveResourceHandler.getString(CommonArchiveResourceHandler.load_resource_EXC_, (new Object[]{resourceUri, getContainer().getURI()})), ex); // = "Could not load resource "{0}" in archive "{1}"" } /** * Gets the looseArchive. * * @return Returns a LooseArchive */ public LooseArchive getLooseArchive() { return looseArchive; } /** * Sets the looseArchive. * * @param looseArchive * The looseArchive to set */ public void setLooseArchive(LooseArchive looseArchive) { this.looseArchive = looseArchive; checkLoosePathsValid(); } /* * Added to support WAS runtime; throw an ArchiveRuntimeException if one of the paths in the * loose config does not point to an existing file */ protected void checkLoosePathsValid() { if (looseArchive == null) return; String path = looseArchive.getBinariesPath(); if (path != null) { java.io.File ioFile = new java.io.File(path); if (!ioFile.exists()) throw new ArchiveRuntimeException("Invalid binaries path: " + path); //$NON-NLS-1$ } path = looseArchive.getResourcesPath(); if (path != null) { java.io.File ioFile = new java.io.File(path); if (!ioFile.exists()) throw new ArchiveRuntimeException("Invalid resources path: " + path); //$NON-NLS-1$ } } protected boolean canHaveLooseChildren() { return container.isEARFile() || container.isWARFile(); } public boolean isReadOnly() { return readOnly; } public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } /** * @param rendererType * The rendererType to set. */ public void setRendererType(int rendererType) { this.rendererType = rendererType; } public java.util.List getFiles(String subfolderPath) { List subset = new ArrayList(); List theFiles = getFiles(); for (int i = 0; i < theFiles.size(); i++) { File aFile = (File)theFiles.get(i); if (aFile.getURI().startsWith(subfolderPath)) subset.add(aFile); } return subset; } }