| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.updater; |
| |
| import java.io.*; |
| import java.util.*; |
| import java.util.logging.Level; |
| import javax.swing.SwingUtilities; |
| |
| /** |
| * @author Jiri Rechtacek |
| */ |
| public final class ModuleDeactivator extends Object { |
| |
| public static final String TO_UNINSTALL = "to_uninstall.txt"; // NOI18N |
| public static final String TO_DISABLE = "to_disable.txt"; // NOI18N |
| |
| public static final String CONFIG = "config"; // NOI18N |
| public static final String MODULES = "Modules"; // NOI18N |
| private final UpdatingContext context; |
| |
| ModuleDeactivator (UpdatingContext context) { |
| this.context = context; |
| } |
| |
| public void delete () { |
| assert ! SwingUtilities.isEventDispatchThread () : "Cannot run in EQ"; |
| context.setLabel (Localization.getBrandedString ("CTL_DeletingFiles")); |
| Collection<File> allFiles = new HashSet<File> (); |
| for (File cluster : UpdateTracking.clusters (true)) { |
| boolean modified = allFiles.addAll (readFilesMarkedForDeleteInCluster (cluster)); |
| modified = allFiles.add (getControlFileForMarkedForDelete (cluster)) || modified; |
| modified = allFiles.add (getDeactivateLater (cluster)) || modified; |
| if (modified) { |
| UpdaterDispatcher.touchLastModified (cluster); |
| } |
| } |
| context.setProgressRange (0, allFiles.size ()); |
| int i = 0; |
| for (File f : allFiles) { |
| doDelete (f); |
| context.setProgressValue (i ++); |
| } |
| } |
| |
| public void disable () { |
| assert ! SwingUtilities.isEventDispatchThread () : "Cannot run in EQ"; |
| context.setLabel (Localization.getBrandedString ("CTL_DisablingFiles")); |
| Collection<File> allControlFiles = new HashSet<File> (); |
| for (File cluster : UpdateTracking.clusters (true)) { |
| allControlFiles.addAll (readFilesMarkedForDisableInCluster (cluster)); |
| doDelete (getControlFileForMarkedForDisable (cluster)); |
| doDelete (getDeactivateLater (cluster)); |
| } |
| context.setProgressRange (0, allControlFiles.size ()); |
| int i = 0; |
| for (File f : allControlFiles) { |
| doDisable (f); |
| context.setProgressValue (i ++); |
| } |
| } |
| |
| public static boolean hasModulesForDelete (File updateDir) { |
| File deactivateDir = new File (updateDir, UpdaterDispatcher.DEACTIVATE_DIR); |
| return deactivateDir.exists () && deactivateDir.isDirectory () && Arrays.asList (deactivateDir.list ()).contains (TO_UNINSTALL); |
| } |
| |
| public static boolean hasModulesForDisable (File updateDir) { |
| File deactivateDir = new File (updateDir, UpdaterDispatcher.DEACTIVATE_DIR); |
| return deactivateDir.exists () && deactivateDir.isDirectory () && Arrays.asList (deactivateDir.list ()).contains (TO_DISABLE); |
| } |
| |
| public static File getDeactivateLater (File cluster) { |
| File file = new File (cluster, |
| UpdaterDispatcher.UPDATE_DIR + // update |
| UpdateTracking.FILE_SEPARATOR + UpdaterDispatcher.DEACTIVATE_DIR + // update/deactivate |
| UpdateTracking.FILE_SEPARATOR + UpdaterDispatcher.DEACTIVATE_LATER); // deactivate_later.xml |
| return file; |
| } |
| |
| public static File getControlFileForMarkedForDelete (File cluster) { |
| File file = new File (cluster, |
| UpdaterDispatcher.UPDATE_DIR + // update |
| UpdateTracking.FILE_SEPARATOR + UpdaterDispatcher.DEACTIVATE_DIR + // update/deactivate |
| UpdateTracking.FILE_SEPARATOR + ModuleDeactivator.TO_UNINSTALL); // to_uninstall.txt |
| return file; |
| } |
| |
| public static File getControlFileForMarkedForDisable (File cluster) { |
| File file = new File (cluster, |
| UpdaterDispatcher.UPDATE_DIR + // update |
| UpdateTracking.FILE_SEPARATOR + UpdaterDispatcher.DEACTIVATE_DIR + // update/deactivate |
| UpdateTracking.FILE_SEPARATOR + ModuleDeactivator.TO_DISABLE); // to_disable.txt |
| return file; |
| } |
| |
| // utils |
| public static void writeStringToFile (String content, File file) { |
| BufferedWriter writer = null; |
| try { |
| try { |
| writer = new BufferedWriter (new FileWriter (file)); |
| writer.write (content); |
| XMLUtil.LOG.info("File " + file + " modified." ); |
| } finally { |
| if (writer != null) writer.close (); |
| } |
| } catch (IOException ioe) { |
| XMLUtil.LOG.log(Level.SEVERE, "Cannot write " + file, ioe); |
| } |
| } |
| |
| public static String readStringFromFile (File file) { |
| StringBuffer fileData = null; |
| BufferedReader reader = null; |
| try { |
| try { |
| fileData = new StringBuffer (); |
| reader = new BufferedReader (new FileReader (file)); |
| char[] buf = new char[1024]; |
| int numRead; |
| while ((numRead = reader.read (buf)) != -1) { |
| String readData = String.valueOf (buf, 0, numRead); |
| fileData.append (readData); |
| buf = new char[1024]; |
| } |
| } finally { |
| if (reader != null) reader.close (); |
| } |
| } catch (IOException ioe) { |
| XMLUtil.LOG.log(Level.SEVERE, "Cannot read " + file, ioe); |
| } |
| |
| return fileData == null ? "" : fileData.toString (); |
| } |
| |
| // private methods |
| // delete file and empty dirs too |
| private static void doDelete (File f) { |
| assert f != null : "Invalid file " + f + " for delete."; |
| if (! f.exists ()) { |
| return ; |
| } |
| XMLUtil.LOG.info("Deleting file: " + f); |
| if (! f.delete ()) { |
| // updater_nb.jar is locked on windows, don't throw AE here |
| //assert false : f + " cannot be deleted"; |
| f.deleteOnExit (); |
| XMLUtil.LOG.info("File " + f + " cannot be deleted. Will be delete later on exit."); |
| } else { |
| XMLUtil.LOG.info("File " + f + " deleted."); |
| } |
| f = f.getParentFile (); |
| while (f != null && doDeleteEmptyDirectory (f)) { |
| f = f.getParentFile (); // remove empty dirs too |
| } |
| } |
| |
| private static boolean doDeleteEmptyDirectory (File d) { |
| assert d != null : d + " cannot be null"; |
| |
| boolean res; |
| if (d.isDirectory ()) { // #132673: remove .lastModified as well if the directory is empty |
| List<File> files = Arrays.asList (d.listFiles ()); |
| if (files.size () == 1) { |
| File f = files.get (0); |
| if (UpdaterDispatcher.LAST_MODIFIED.endsWith (f.getName ())) { |
| if (f.delete ()) { |
| d.delete (); |
| } |
| XMLUtil.LOG.info("File " + f + " deleted."); |
| } |
| } |
| res = d.delete (); |
| XMLUtil.LOG.info("Directory " + d + " deleted."); |
| } else { |
| res = d.delete (); |
| XMLUtil.LOG.info("File " + d + " deleted."); |
| } |
| return res; |
| } |
| |
| private static Set<File> readFilesMarkedForDeleteInCluster (File cluster) { |
| |
| File mark4deleteFile = getControlFileForMarkedForDelete (cluster); |
| if (! mark4deleteFile.exists ()) { |
| return Collections.emptySet (); |
| } |
| |
| Set<File> toDelete = new HashSet<File> (); |
| |
| String content = readStringFromFile (mark4deleteFile); |
| StringTokenizer tokenizer = new StringTokenizer (content, UpdateTracking.PATH_SEPARATOR); |
| while (tokenizer.hasMoreElements ()) { |
| String filePath = tokenizer.nextToken (); |
| File f = new File (filePath); |
| if (f.exists ()) { |
| toDelete.add (f); |
| } |
| } |
| |
| return toDelete; |
| } |
| |
| private static Set<File> readFilesMarkedForDisableInCluster (File cluster) { |
| |
| File mark4disableFile = getControlFileForMarkedForDisable (cluster); |
| if (! mark4disableFile.exists ()) { |
| return Collections.emptySet (); |
| } |
| |
| Set<File> toDisable = new HashSet<File> (); |
| |
| String content = readStringFromFile (mark4disableFile); |
| StringTokenizer tokenizer = new StringTokenizer (content, UpdateTracking.PATH_SEPARATOR); |
| while (tokenizer.hasMoreElements ()) { |
| String filePath = tokenizer.nextToken (); |
| File f = new File (filePath); |
| if (f.exists ()) { |
| toDisable.add (f); |
| } |
| } |
| |
| return toDisable; |
| } |
| |
| private static String ENABLE_TAG = "<param name=\"enabled\">true</param>"; |
| private static String DISABLE_TAG = "<param name=\"enabled\">false</param>"; |
| |
| private static void doDisable (File f) { |
| String content = readStringFromFile (f); |
| int pos = content.indexOf (ENABLE_TAG); |
| assert pos != -1 : ENABLE_TAG + " must be contained in " + content; |
| int shift = ENABLE_TAG.length (); |
| String pre = content.substring (0, pos); |
| String post = content.substring (pos + shift); |
| String res = pre + DISABLE_TAG + post; |
| File configDir = new File (new File (UpdateTracking.getUserDir (), CONFIG), MODULES); |
| configDir.mkdirs (); |
| File dest = new File (configDir, f.getName()); |
| writeStringToFile (res, dest); |
| } |
| |
| } |