| /* |
| * Copyright (c) 2007-2012, 2015, 2016, 2019, 2020 Eike Stepper (Loehne, Germany) 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: |
| * Eike Stepper - initial API and implementation |
| */ |
| package org.eclipse.net4j.internal.util.om; |
| |
| import org.eclipse.net4j.internal.util.bundle.AbstractBundle; |
| import org.eclipse.net4j.internal.util.bundle.AbstractPlatform; |
| import org.eclipse.net4j.util.ReflectUtil; |
| import org.eclipse.net4j.util.StringUtil; |
| import org.eclipse.net4j.util.WrappedException; |
| import org.eclipse.net4j.util.io.IOUtil; |
| import org.eclipse.net4j.util.om.OMPlatform; |
| import org.eclipse.net4j.util.om.trace.Tracer; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map.Entry; |
| import java.util.MissingResourceException; |
| import java.util.Properties; |
| import java.util.jar.JarEntry; |
| import java.util.jar.JarFile; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class LegacyBundle extends AbstractBundle |
| { |
| private static final boolean IS_OPTIONS_DISABLED = OMPlatform.INSTANCE.isProperty(Tracer.PROP_DISABLE_TRACING_OPTIONS); |
| |
| private URL baseURL; |
| |
| public LegacyBundle(AbstractPlatform platform, String bundleID, Class<?> accessor) |
| { |
| super(platform, bundleID, accessor); |
| |
| try |
| { |
| computeBaseURL(accessor); |
| if (baseURL == null) |
| { |
| throw new IllegalStateException("No base URL"); |
| } |
| |
| if (!IS_OPTIONS_DISABLED) |
| { |
| loadOptions(); |
| } |
| } |
| catch (Exception ex) |
| { |
| IOUtil.print(ex); |
| } |
| } |
| |
| @Override |
| public String getStateLocation() |
| { |
| return new File(getPlatform().getStateFolder(), ".plugins/" + getBundleID()).getAbsolutePath(); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public URL getBaseURL() |
| { |
| return baseURL; |
| } |
| |
| @Override |
| public Iterator<Class<?>> getClasses() |
| { |
| List<Class<?>> result = new ArrayList<>(); |
| |
| if (isArchiveProtocol(baseURL.getProtocol())) |
| { |
| JarFile jarFile = null; |
| |
| try |
| { |
| jarFile = new JarFile(baseURL.getFile()); |
| |
| Enumeration<JarEntry> entries = jarFile.entries(); |
| while (entries.hasMoreElements()) |
| { |
| JarEntry jarEntry = entries.nextElement(); |
| if (!jarEntry.isDirectory()) |
| { |
| Class<?> c = getClassFromBundle(jarEntry.getName()); |
| if (c != null) |
| { |
| result.add(c); |
| } |
| } |
| } |
| } |
| catch (IOException ex) |
| { |
| throw WrappedException.wrap(ex); |
| } |
| finally |
| { |
| if (jarFile != null) |
| { |
| try |
| { |
| jarFile.close(); |
| } |
| catch (IOException ex) |
| { |
| throw WrappedException.wrap(ex); |
| } |
| } |
| } |
| } |
| else |
| { |
| try |
| { |
| URL url = getClassesURL(getAccessor()); |
| File folder = new File(url.getFile()); |
| collectFileClasses(folder, null, result); |
| } |
| catch (MalformedURLException ex) |
| { |
| throw WrappedException.wrap(ex); |
| } |
| } |
| |
| return result.iterator(); |
| } |
| |
| private void collectFileClasses(File folder, String path, List<Class<?>> result) |
| { |
| File file = folder; |
| if (path == null) |
| { |
| path = ""; |
| } |
| else |
| { |
| file = new File(folder, path); |
| } |
| |
| if (file.isDirectory()) |
| { |
| for (String child : file.list()) |
| { |
| collectFileClasses(folder, path + "/" + child, result); |
| } |
| } |
| else |
| { |
| Class<?> c = getClassFromBundle(path); |
| if (c != null) |
| { |
| result.add(c); |
| } |
| } |
| } |
| |
| private void loadOptions() |
| { |
| InputStream inputStream = null; |
| Properties properties = new Properties(); |
| |
| try |
| { |
| final String prefix = getBundleID() + "/"; //$NON-NLS-1$ |
| final int length = prefix.length(); |
| |
| inputStream = new BufferedInputStream(getInputStream(LegacyPlatform.OPTIONS)); |
| properties.load(inputStream); |
| |
| for (Entry<Object, Object> entry : properties.entrySet()) |
| { |
| try |
| { |
| String key = (String)entry.getKey(); |
| if (key.startsWith(prefix)) |
| { |
| String value = (String)entry.getValue(); |
| setDebugOptionIfAbsent(key.substring(length), value.trim()); |
| } |
| } |
| catch (RuntimeException ignore) |
| { |
| } |
| } |
| } |
| catch (IOException ignore) |
| { |
| } |
| finally |
| { |
| IOUtil.closeSilent(inputStream); |
| } |
| } |
| |
| private void computeBaseURL(Class<?> accessor) throws MalformedURLException |
| { |
| // Determine the URL for the class itself. The URL will be of one of the |
| // following forms, so there are a few good places to consider looking for |
| // the plugin.properties. |
| // |
| // For a plugin.xml with runtime="common.jar": |
| // jar:file:/D:/sandbox/unpackage1-3.1M7/eclipse/plugins/org.eclipse.emf.common/common.jar!/org/eclipse/common/ |
| // CommonPlugin.class |
| // |
| // For a plugin.xml with runtime="runtime/common.jar": |
| // jar:file:/D:/sandbox/unpackage1-3.1M7/eclipse/plugins/org.eclipse.emf.common/runtime/common.jar!/org/eclipse/common |
| // /CommonPlugin.class |
| // |
| // For a plugin.xml with runtime="." where the plugin is jarred: |
| // jar:file:/D:/sandbox/unpackage1-3.1M7/eclipse/plugins/org.eclipse.emf.common.jar!/org/eclipse/common/CommonPlugin. |
| // class |
| // |
| // For a plugin.xml with runtime="." where the plugin is not jarred. |
| // file:/D:/sandbox/unpackage1-3.1M7/eclipse/plugins/org.eclipse.emf.common/org/eclipse/emf/common/CommonPlugin.class |
| // |
| // Running in PDE with bin on classpath: |
| // file:/D:/sandbox/unpackage1-3.1M7/eclipse/plugins/org.eclipse.emf.common/bin/org/eclipse/emf/common/CommonPlugin. |
| // class |
| |
| URL url = getClassesURL(accessor); |
| |
| // For an archive URI, check for the plugin.properties in the archive. |
| if (isArchiveProtocol(url.getProtocol())) |
| { |
| try |
| { |
| // If we can open an input stream, then the plugin.properties is there, |
| // and we have a good base URL. |
| URL u = new URL(url.toString() + "plugin.properties"); |
| InputStream inputStream = null; |
| |
| try |
| { |
| inputStream = u.openStream(); |
| } |
| finally |
| { |
| IOUtil.close(inputStream); |
| } |
| |
| baseURL = url; |
| } |
| catch (IOException exception) |
| { |
| // If the plugin.properties isn't within the root of the archive, create |
| // a new URI for the folder location of the archive, so we can look in |
| // the folder that contains it. |
| url = trimSegments(new URL(url.getFile()), 1); |
| } |
| } |
| |
| // If we didn't find the plugin.properties in the usual place nor in the |
| // archive... |
| if (baseURL == null) |
| { |
| // Trim off the "bin" or "runtime" segment. |
| String lastSegment = lastSegment(url); |
| if ("bin".equals(lastSegment) || "runtime".equals(lastSegment)) //$NON-NLS-1$ //$NON-NLS-2$ |
| { |
| url = trimSegments(url, 1); |
| } |
| else if ("classes".equals(lastSegment)) //$NON-NLS-1$ |
| { |
| URL url1 = trimSegments(url, 1); |
| lastSegment = lastSegment(url1); |
| if ("target".equals(lastSegment)) //$NON-NLS-1$ |
| { |
| url = trimSegments(url1, 1); |
| } |
| } |
| |
| try |
| { |
| // If we can open an input stream, then the plugin.properties is in the |
| // folder, and we have a good base URL. |
| InputStream inputStream = new URL(url.toString() + "plugin.properties").openStream(); //$NON-NLS-1$ |
| inputStream.close(); |
| baseURL = url; |
| } |
| catch (IOException exception) |
| { |
| } |
| } |
| |
| // If we still don't have a good base URL, complain about it. |
| if (baseURL == null) |
| { |
| throw new MissingResourceException("Missing properties: " + accessor.getName(), accessor.getName(), //$NON-NLS-1$ |
| "plugin.properties"); //$NON-NLS-1$ |
| } |
| } |
| |
| private static URL getClassesURL(Class<?> accessor) throws MalformedURLException |
| { |
| String className = accessor.getName(); |
| URL url = accessor.getResource(ReflectUtil.getSimpleName(accessor) + ".class"); //$NON-NLS-1$ |
| |
| int segmentsToTrim = 1 + StringUtil.occurrences(className, '.'); |
| return trimSegments(url, segmentsToTrim); |
| } |
| |
| private static String lastSegment(URL url) |
| { |
| String path = url.getPath(); |
| if (path.endsWith("/")) //$NON-NLS-1$ |
| { |
| path = path.substring(0, path.length() - 1); |
| } |
| |
| int pos = path.lastIndexOf('/'); |
| return pos == -1 ? path : path.substring(pos + 1); |
| } |
| |
| private static URL trimSegments(URL url, int count) throws MalformedURLException |
| { |
| String path = url.getPath(); |
| if (path.endsWith("/")) //$NON-NLS-1$ |
| { |
| ++count; |
| } |
| |
| for (int i = 0; i < count; i++) |
| { |
| int pos = path.lastIndexOf('/'); |
| if (pos == -1) |
| { |
| break; |
| } |
| |
| // TODO Optimize |
| path = path.substring(0, pos); |
| } |
| |
| return new URL(url.getProtocol() + ":" + path + "/"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| /** |
| * TODO Make configurable |
| * |
| * @see org.eclipse.emf.common.util.URI#isArchiveScheme(String) |
| */ |
| private static boolean isArchiveProtocol(String scheme) |
| { |
| return "jar".equalsIgnoreCase(scheme); //$NON-NLS-1$ |
| } |
| } |