1 19 20 package org.netbeans.modules.autoupdate.catalog; 21 22 import java.util.logging.Logger ; 23 import org.netbeans.Module; 24 import org.openide.filesystems.FileUtil; 25 import org.openide.modules.InstalledFileLocator; 26 import org.openide.util.RequestProcessor; 27 import org.openide.xml.XMLUtil; 28 29 import java.io.File ; 30 import java.io.FileInputStream ; 31 import java.io.IOException ; 32 import java.io.InputStream ; 33 import java.util.HashSet ; 34 import java.util.Iterator ; 35 import java.util.Set ; 36 import java.util.logging.Level ; 37 import org.openide.filesystems.FileObject; 38 import org.openide.filesystems.Repository; 39 import org.openide.util.Exceptions; 40 41 import org.w3c.dom.Document ; 42 import org.w3c.dom.Element ; 43 import org.w3c.dom.NamedNodeMap ; 44 import org.w3c.dom.Node ; 45 import org.w3c.dom.NodeList ; 46 import org.xml.sax.InputSource ; 47 import org.xml.sax.SAXException ; 48 49 50 58 public final class ModuleDeleterImpl implements ModuleDeleter { 59 60 private static final String ELEMENT_MODULE = "module"; private static final String ELEMENT_VERSION = "module_version"; private static final String ATTR_ORIGIN = "origin"; private static final String ATTR_LAST = "last"; private static final String ATTR_FILE_NAME = "name"; private static final String UPDATE_TRACKING = "update_tracking"; private static final String INST_ORIGIN = "updater"; private static final boolean ONLY_FROM_AUTOUPDATE = false; 68 private static final int TIME_TO_CHECK = 2000; 69 private static final int MAX_CHECKS_OF_STATE = 50; 70 private static final int HOLD_ON_PROPAGATE_DISABLE = 1000; 71 72 private Logger err = Logger.getLogger("org.netbeans.modules.autoupdate.catalog.ModuleDeleterImpl"); 74 public boolean canDelete (Module module) { 75 if (module.isFixed ()) { 76 err.log(Level.FINE, 77 "Cannot delete module because module " + 78 module.getCodeName() + " isFixed."); 79 } else if (module.isAutoload ()) { 80 err.log(Level.FINE, 81 "Cannot delete module because module " + 82 module.getCodeName() + " isAutoload. See issue #74819."); 83 } else if (module.isEager ()) { 84 err.log(Level.FINE, 85 "Cannot delete module because module " + 86 module.getCodeName() + " isEager. See issue #74819."); 87 } 88 return ModuleNodeUtils.isUninstallAllowed (module) && findUpdateTracking (module, ONLY_FROM_AUTOUPDATE); 89 } 90 91 public void delete (final Module module) throws IOException { 92 if (module == null) { 93 throw new IllegalArgumentException ("Module argument cannot be null."); 94 } 95 96 err.log(Level.FINE, 97 "Locate and remove config file of " + module.getCodeNameBase ()); 98 removeControlModuleFile (module); 99 100 RequestProcessor.Task cleaner = RequestProcessor.getDefault().create(new Runnable () { 101 102 public void run() { 103 try { 104 removeModuleFiles(module); 105 } 106 catch (IOException ioe) { 107 Exceptions.attachLocalizedMessage(ioe, 108 "ERROR: During processing removeModuleFiles (" + 109 module.getDisplayName() + 110 ")"); 111 Exceptions.printStackTrace(ioe); 112 } 113 } 114 }); 115 116 if (! module.isAutoload () && ! module.isEager ()) { 119 RequestProcessor.getDefault ().post (new HackModuleListRefresher ()); 120 } 121 122 if (! module.isAutoload () && ! module.isEager ()) { 123 RequestProcessor.getDefault ().post (new ModuleStateChecker (module, cleaner), TIME_TO_CHECK); 124 } else { 125 cleaner.run (); 126 } 127 } 128 129 private File locateControlFile (Module m) { 130 String configFile = "config" + '/' + "Modules" + '/' + m.getCodeNameBase ().replace ('.', '-') + ".xml"; return InstalledFileLocator.getDefault ().locate (configFile, m.getCodeNameBase (), false); 132 } 133 134 private void removeControlModuleFile (Module m) throws IOException { 135 File configFile = null; 136 while ((configFile = locateControlFile (m)) != null) { 137 if (configFile != null && configFile.exists ()) { 138 err.log(Level.FINE, "Try delete the config File " + configFile); 139 FileUtil.toFileObject (configFile).delete (); 140 } else { 141 err.log(Level.FINE, 142 "Warning: Config File " + configFile + " doesn\'t exist!"); 143 } 144 } 145 } 146 147 private File locateUpdateTracking (Module m) { 148 String fileNameToFind = UPDATE_TRACKING + '/' + m.getCodeNameBase ().replace ('.', '-') + ".xml"; return InstalledFileLocator.getDefault ().locate (fileNameToFind, m.getCodeNameBase (), false); 150 } 151 152 private boolean findUpdateTracking (Module module, boolean checkIfFromAutoupdate) { 153 File updateTracking = locateUpdateTracking (module); 154 if (updateTracking != null && updateTracking.exists ()) { 155 if (! updateTracking.getParentFile ().canWrite ()) { 158 err.log(Level.FINE, 159 "Cannot delete module " + module.getCodeName() + 160 " because no write permission to directory " + 161 updateTracking.getParent()); 162 return false; 163 } 164 if (checkIfFromAutoupdate) { 165 boolean isFromAutoupdate = fromAutoupdate (getModuleConfiguration (updateTracking)); 166 err.log(Level.FINE, 167 "Is Module " + module.getCodeName() + 168 " installed by AutoUpdate? " + isFromAutoupdate); 169 return isFromAutoupdate; 170 } else { 171 return true; 172 } 173 } else { 174 err.log(Level.FINE, 175 "Cannot delete module " + module.getCodeName() + 176 " because no update_tracking file found."); 177 return false; 178 } 179 } 180 181 private boolean fromAutoupdate (Node moduleNode) { 182 Node attrOrigin = moduleNode.getAttributes ().getNamedItem (ATTR_ORIGIN); 183 assert attrOrigin != null : "ELEMENT_VERSION must contain ATTR_ORIGIN attribute."; 184 String origin = attrOrigin.getNodeValue (); 185 return INST_ORIGIN.equals (origin); 186 } 187 188 private void removeModuleFiles (Module m) throws IOException { 189 err.log (Level.FINE, "Entry removing files of module " + m); 190 File updateTracking = null; 191 while ((updateTracking = locateUpdateTracking (m)) != null) { 192 removeModuleFilesInCluster (m, updateTracking); 193 } 194 err.log (Level.FINE, "Exit removing files of module " + m); 195 } 196 197 private void removeModuleFilesInCluster (Module module, File updateTracking) throws IOException { 198 err.log(Level.FINE, "Read update_tracking " + updateTracking + " file."); 199 Set moduleFiles = readModuleFiles (getModuleConfiguration (updateTracking)); 200 String configFile = "config" + '/' + "Modules" + '/' + module.getCodeNameBase ().replace ('.', '-') + ".xml"; if (moduleFiles.contains (configFile)) { 202 File file = InstalledFileLocator.getDefault ().locate (configFile, module.getCodeNameBase (), false); 203 assert file == null || ! file.exists () : "Config file " + configFile + " must be already removed."; 204 } 205 Iterator it = moduleFiles.iterator (); 206 while (it.hasNext ()) { 207 String fileName = (String ) it.next (); 208 if (fileName.equals (configFile)) { 209 continue; 210 } 211 File file = InstalledFileLocator.getDefault ().locate (fileName, module.getCodeNameBase (), false); 212 if (file.equals (updateTracking)) { 213 continue; 214 } 215 assert file.exists () : "File " + file + " exists."; 216 if (file.exists ()) { 217 err.log(Level.FINE, "File " + file + " is deleted."); 218 try { 219 FileUtil.toFileObject (file).delete (); 220 } catch (IOException ioe) { 221 assert false : "Waring: IOException " + ioe.getMessage () + " was caught. Propably file lock on the file."; 222 err.log(Level.FINE, 223 "Waring: IOException " + ioe.getMessage() + 224 " was caught. Propably file lock on the file."); 225 err.log(Level.FINE, 226 "Try call File.deleteOnExit() on " + file); 227 file.deleteOnExit (); 228 } 229 } 230 } 231 FileUtil.toFileObject (updateTracking).delete (); 232 err.log(Level.FINE, "File " + updateTracking + " is deleted."); 233 } 234 235 private Node getModuleConfiguration (File moduleUpdateTracking) { 236 Document document = null; 237 InputStream is; 238 try { 239 is = new FileInputStream (moduleUpdateTracking); 240 InputSource xmlInputSource = new InputSource (is); 241 document = XMLUtil.parse (xmlInputSource, false, false, null, org.openide.xml.EntityCatalog.getDefault ()); 242 if (is != null) { 243 is.close (); 244 } 245 } catch (SAXException saxe) { 246 err.log(Level.WARNING, null, saxe); 247 return null; 248 } catch (IOException ioe) { 249 err.log(Level.WARNING, null, ioe); 250 } 251 252 assert document.getDocumentElement () != null : "File " + moduleUpdateTracking + " must contain <module> element."; 253 return getModuleElement (document.getDocumentElement ()); 254 } 255 256 private Node getModuleElement (Element element) { 257 Node lastElement = null; 258 assert ELEMENT_MODULE.equals (element.getTagName ()) : "The root element is: " + ELEMENT_MODULE + " but was: " + element.getTagName (); 259 NodeList listModuleVersions = element.getElementsByTagName (ELEMENT_VERSION); 260 for (int i = 0; i < listModuleVersions.getLength (); i++) { 261 lastElement = getModuleLastVersion (listModuleVersions.item (i)); 262 if (lastElement != null) { 263 break; 264 } 265 } 266 return lastElement; 267 } 268 269 private Node getModuleLastVersion (Node version) { 270 Node attrLast = version.getAttributes ().getNamedItem (ATTR_LAST); 271 assert attrLast != null : "ELEMENT_VERSION must contain ATTR_LAST attribute."; 272 if (Boolean.valueOf (attrLast.getNodeValue ()).booleanValue ()) { 273 return version; 274 } else { 275 return null; 276 } 277 } 278 279 private Set readModuleFiles (Node version) { 280 Set files = new HashSet (); 281 NodeList fileNodes = version.getChildNodes (); 282 for (int i = 0; i < fileNodes.getLength (); i++) { 283 if (fileNodes.item (i).hasAttributes ()) { 284 NamedNodeMap map = fileNodes.item (i).getAttributes (); 285 files.add (map.getNamedItem (ATTR_FILE_NAME).getNodeValue ()); 286 err.log(Level.FINE, 287 "Mark to delete: " + 288 map.getNamedItem(ATTR_FILE_NAME).getNodeValue()); 289 } 290 } 291 return files; 292 } 293 294 private class ModuleStateChecker implements Runnable { 295 RequestProcessor.Task cleaner; 296 Module m; 297 int checks; 298 public ModuleStateChecker (Module module, RequestProcessor.Task filesCleaner) { 299 cleaner = filesCleaner; 300 m = module; 301 checks = 0; 302 } 303 304 public void run () { 305 checks ++; 306 if (m.isEnabled () && m.isValid ()) { 307 if (checks < MAX_CHECKS_OF_STATE) { 308 err.log(Level.FINE, 309 "Module " + m.getCodeNameBase() + 310 " is still valid, repost later."); 311 RequestProcessor.getDefault ().post (this, TIME_TO_CHECK); 312 } else { 313 err.log(Level.FINE, 314 "Warning: Module " + m.getCodeNameBase() + 315 " is still valid but time-out. Task is terminated."); 316 } 317 return ; 318 } 319 320 cleaner.schedule (HOLD_ON_PROPAGATE_DISABLE); 322 } 323 324 } 325 326 private class HackModuleListRefresher implements Runnable { 327 public void run () { 328 FileObject modulesRoot = Repository.getDefault ().getDefaultFileSystem ().findResource ("Modules"); err.log(Level.FINE, 331 "It\'s a hack: Call refresh on " + modulesRoot + 332 " file object."); 333 if (modulesRoot != null) { 334 modulesRoot.refresh (); 335 } 336 } 338 } 339 } 340 | Popular Tags |