/* * Copyright (C) 2009-2012 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package lombok.installer.eclipse; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import lombok.installer.CorruptedIdeLocationException; import lombok.installer.IdeFinder; import lombok.installer.IdeLocation; import lombok.installer.InstallException; import lombok.installer.Installer; import lombok.installer.UninstallException; /** * Represents an Eclipse installation. * An instance can figure out if an Eclipse installation has been lombok-ified, and can * install and uninstall lombok from the Eclipse installation. */ public class EclipseLocation extends IdeLocation { private final String name; private final File eclipseIniPath; private final String pathToLombokJarPrefix; private volatile boolean hasLombok; private static final String OS_NEWLINE = IdeFinder.getOS().getLineEnding(); protected String getTypeName() { return "eclipse"; } protected String getIniFileName() { return "eclipse.ini"; } EclipseLocation(String nameOfLocation, File pathToEclipseIni) throws CorruptedIdeLocationException { this.name = nameOfLocation; this.eclipseIniPath = pathToEclipseIni; File p1 = pathToEclipseIni.getParentFile(); File p2 = p1 == null ? null : p1.getParentFile(); File p3 = p2 == null ? null : p2.getParentFile(); if (p1 != null && p1.getName().equals("Eclipse") && p2 != null && p2.getName().equals("Contents") && p3 != null && p3.getName().endsWith(".app")) { this.pathToLombokJarPrefix = "../Eclipse/"; } else { this.pathToLombokJarPrefix = ""; } try { this.hasLombok = checkForLombok(eclipseIniPath); } catch (IOException e) { throw new CorruptedIdeLocationException( "I can't read the configuration file of the " + getTypeName() + " installed at " + name + "\n" + "You may need to run this installer with root privileges if you want to modify that " + getTypeName() + ".", getTypeName(), e); } } @Override public int hashCode() { return eclipseIniPath.hashCode(); } @Override public boolean equals(Object o) { if (!(o instanceof EclipseLocation)) return false; return ((EclipseLocation)o).eclipseIniPath.equals(eclipseIniPath); } /** * Returns the name of this location; generally the path to the eclipse executable. */ @Override public String getName() { return name; } /** * @return true if the Eclipse installation has been instrumented with lombok. */ @Override public boolean hasLombok() { return hasLombok; } private final Pattern JAVA_AGENT_LINE_MATCHER = Pattern.compile( "^\\-javaagent\\:.*lombok.*\\.jar$", Pattern.CASE_INSENSITIVE); private final Pattern BOOTCLASSPATH_LINE_MATCHER = Pattern.compile( "^\\-Xbootclasspath\\/a\\:(.*lombok.*\\.jar.*)$", Pattern.CASE_INSENSITIVE); private boolean checkForLombok(File iniFile) throws IOException { if (!iniFile.exists()) return false; FileInputStream fis = new FileInputStream(iniFile); try { BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String line; while ((line = br.readLine()) != null) { if (JAVA_AGENT_LINE_MATCHER.matcher(line.trim()).matches()) { br.close(); return true; } } br.close(); return false; } finally { fis.close(); } } /** Returns directories that may contain lombok.jar files that need to be deleted. */ private List<File> getUninstallDirs() { List<File> result = new ArrayList<File>(); File x = new File(name); if (!x.isDirectory()) x = x.getParentFile(); if (x.isDirectory()) result.add(x); result.add(eclipseIniPath.getParentFile()); return result; } /** * Uninstalls lombok from this location. * It's a no-op if lombok wasn't there in the first place, * and it will remove a half-succeeded lombok installation as well. * * @throws UninstallException * If there's an obvious I/O problem that is preventing * installation. bugs in the uninstall code will probably throw * other exceptions; this is intentional. */ @Override public void uninstall() throws UninstallException { final List<File> lombokJarsForWhichCantDeleteSelf = new ArrayList<File>(); StringBuilder newContents = new StringBuilder(); if (eclipseIniPath.exists()) { try { FileInputStream fis = new FileInputStream(eclipseIniPath); try { BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String line; while ((line = br.readLine()) != null) { if (JAVA_AGENT_LINE_MATCHER.matcher(line).matches()) continue; Matcher m = BOOTCLASSPATH_LINE_MATCHER.matcher(line); if (m.matches()) { StringBuilder elemBuilder = new StringBuilder(); elemBuilder.append("-Xbootclasspath/a:"); boolean first = true; for (String elem : m.group(1).split(Pattern.quote(File.pathSeparator))) { if (elem.toLowerCase().endsWith("lombok.jar")) continue; /* legacy code -see previous comment that starts with 'legacy' */ { if (elem.toLowerCase().endsWith("lombok.eclipse.agent.jar")) continue; } if (first) first = false; else elemBuilder.append(File.pathSeparator); elemBuilder.append(elem); } if (!first) newContents.append(elemBuilder.toString()).append(OS_NEWLINE); continue; } newContents.append(line).append(OS_NEWLINE); } br.close(); } finally { fis.close(); } FileOutputStream fos = new FileOutputStream(eclipseIniPath); try { fos.write(newContents.toString().getBytes()); } finally { fos.close(); } } catch (IOException e) { throw new UninstallException("Cannot uninstall lombok from " + name + generateWriteErrorMessage(), e); } } for (File dir : getUninstallDirs()) { File lombokJar = new File(dir, "lombok.jar"); if (lombokJar.exists()) { if (!lombokJar.delete()) { if (IdeFinder.getOS() == IdeFinder.OS.WINDOWS && Installer.isSelf(lombokJar.getAbsolutePath())) { lombokJarsForWhichCantDeleteSelf.add(lombokJar); } else { throw new UninstallException( "Can't delete " + lombokJar.getAbsolutePath() + generateWriteErrorMessage(), null); } } } /* legacy code - lombok at one point used to have a separate jar for the eclipse agent. * Leave this code in to delete it for those upgrading from an old version. */ { File agentJar = new File(dir, "lombok.eclipse.agent.jar"); if (agentJar.exists()) { agentJar.delete(); } } } if (!lombokJarsForWhichCantDeleteSelf.isEmpty()) { throw new UninstallException(true, String.format( "lombok.jar cannot delete itself on windows.\nHowever, lombok has been uncoupled from your %s.\n" + "You can safely delete this jar file. You can find it at:\n%s", getTypeName(), lombokJarsForWhichCantDeleteSelf.get(0).getAbsolutePath()), null); } } private static String generateWriteErrorMessage() { String osSpecificError; switch (IdeFinder.getOS()) { default: case MAC_OS_X: case UNIX: osSpecificError = ":\nStart terminal, go to the directory with lombok.jar, and run: sudo java -jar lombok.jar"; break; case WINDOWS: osSpecificError = ":\nStart a new cmd (dos box) with admin privileges, go to the directory with lombok.jar, and run: java -jar lombok.jar"; break; } return ", probably because this installer does not have the access rights.\n" + "Try re-running the installer with administrative privileges" + osSpecificError; } /** * Install lombok into the Eclipse at this location. * If lombok is already there, it is overwritten neatly (upgrade mode). * * @throws InstallException * If there's an obvious I/O problem that is preventing * installation. bugs in the install code will probably throw * other exceptions; this is intentional. */ @Override public String install() throws InstallException { // For whatever reason, relative paths in your eclipse.ini file don't work on linux, but only for -javaagent. // If someone knows how to fix this, please do so, as this current hack solution (putting the absolute path // to the jar files in your eclipse.ini) means you can't move your eclipse around on linux without lombok // breaking it. NB: rerunning lombok.jar installer and hitting 'update' will fix it if you do that. boolean fullPathRequired = IdeFinder.getOS() == EclipseFinder.OS.UNIX || System.getProperty("lombok.installer.fullpath") != null; boolean installSucceeded = false; StringBuilder newContents = new StringBuilder(); File lombokJar = new File(eclipseIniPath.getParentFile(), "lombok.jar"); /* No need to copy lombok.jar to itself, obviously. On windows this would generate an error so we check for this. */ if (!Installer.isSelf(lombokJar.getAbsolutePath())) { File ourJar = findOurJar(); byte[] b = new byte[524288]; boolean readSucceeded = true; try { FileOutputStream out = new FileOutputStream(lombokJar); try { readSucceeded = false; InputStream in = new FileInputStream(ourJar); try { while (true) { int r = in.read(b); if (r == -1) break; if (r > 0) readSucceeded = true; out.write(b, 0, r); } } finally { in.close(); } } finally { out.close(); } } catch (IOException e) { try { lombokJar.delete(); } catch (Throwable ignore) { /* Nothing we can do about that. */ } if (!readSucceeded) throw new InstallException( "I can't read my own jar file. I think you've found a bug in this installer!\nI suggest you restart it " + "and use the 'what do I do' link, to manually install lombok. Also, tell us about this at:\n" + "http://groups.google.com/group/project-lombok - Thanks!", e); throw new InstallException("I can't write to your " + getTypeName() + " directory at " + name + generateWriteErrorMessage(), e); } } /* legacy - delete lombok.eclipse.agent.jar if its there, which lombok no longer uses. */ { new File(lombokJar.getParentFile(), "lombok.eclipse.agent.jar").delete(); } try { FileInputStream fis = new FileInputStream(eclipseIniPath); try { BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String line; while ((line = br.readLine()) != null) { if (JAVA_AGENT_LINE_MATCHER.matcher(line).matches()) continue; Matcher m = BOOTCLASSPATH_LINE_MATCHER.matcher(line); if (m.matches()) { StringBuilder elemBuilder = new StringBuilder(); elemBuilder.append("-Xbootclasspath/a:"); boolean first = true; for (String elem : m.group(1).split(Pattern.quote(File.pathSeparator))) { if (elem.toLowerCase().endsWith("lombok.jar")) continue; /* legacy code -see previous comment that starts with 'legacy' */ { if (elem.toLowerCase().endsWith("lombok.eclipse.agent.jar")) continue; } if (first) first = false; else elemBuilder.append(File.pathSeparator); elemBuilder.append(elem); } if (!first) newContents.append(elemBuilder.toString()).append(OS_NEWLINE); continue; } newContents.append(line).append(OS_NEWLINE); } br.close(); } finally { fis.close(); } String pathPrefix; if (fullPathRequired) { pathPrefix = lombokJar.getParentFile().getCanonicalPath() + File.separator; } else { pathPrefix = pathToLombokJarPrefix; } newContents.append(String.format( "-javaagent:%s", escapePath(pathPrefix + "lombok.jar"))).append(OS_NEWLINE); FileOutputStream fos = new FileOutputStream(eclipseIniPath); try { fos.write(newContents.toString().getBytes()); } finally { fos.close(); } installSucceeded = true; } catch (IOException e) { throw new InstallException("Cannot install lombok at " + name + generateWriteErrorMessage(), e); } finally { if (!installSucceeded) try { lombokJar.delete(); } catch (Throwable ignore) {} } if (!installSucceeded) { throw new InstallException("I can't find the " + getIniFileName() + " file. Is this a real " + getTypeName() + " installation?", null); } return "If you start " + getTypeName() + " with a custom -vm parameter, you'll need to add:<br>" + "<code>-vmargs -javaagent:lombok.jar</code><br>as parameter as well."; } @Override public URL getIdeIcon() { return EclipseLocation.class.getResource("eclipse.png"); } }