1 19 20 package org.netbeans.modules.web.project; 21 22 import java.io.IOException ; 23 import java.net.URL ; 24 import java.util.ArrayList ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import javax.swing.JButton ; 28 import org.netbeans.api.project.libraries.LibraryManager; 29 import org.netbeans.modules.web.project.api.WebProjectUtilities; 30 import org.netbeans.modules.web.project.classpath.ClassPathSupport; 31 import org.netbeans.modules.web.project.ui.customizer.WebProjectProperties; 32 import org.netbeans.spi.project.support.ant.ReferenceHelper; 33 import org.openide.filesystems.FileObject; 34 import org.openide.filesystems.FileUtil; 35 import org.openide.filesystems.Repository; 36 import org.w3c.dom.Comment ; 37 import org.w3c.dom.Document ; 38 import org.w3c.dom.Element ; 39 import org.w3c.dom.NamedNodeMap ; 40 import org.w3c.dom.Node ; 41 import org.w3c.dom.NodeList ; 42 import org.w3c.dom.Text ; 43 import org.openide.DialogDisplayer; 44 import org.openide.ErrorManager; 45 import org.openide.NotifyDescriptor; 46 import org.openide.util.NbBundle; 47 import org.openide.util.Mutex; 48 import org.netbeans.api.project.Project; 49 import org.netbeans.api.project.ProjectManager; 50 import org.netbeans.spi.project.AuxiliaryConfiguration; 51 import org.netbeans.spi.project.support.ant.AntProjectHelper; 52 import org.netbeans.spi.project.support.ant.EditableProperties; 53 54 55 61 public class UpdateHelper { 62 63 private static final boolean TRANSPARENT_UPDATE = Boolean.getBoolean("webproject.transparentUpdate"); private static final String BUILD_NUMBER = System.getProperty("netbeans.buildnumber"); 66 private final Project project; 67 private final AntProjectHelper helper; 68 private final AuxiliaryConfiguration cfg; 69 private final Notifier notifier; 70 private boolean alreadyAskedInWriteAccess; 71 private Boolean isCurrent; 72 private EditableProperties cachedProperties; 73 private Element cachedElement; 74 private static final String TAG_MINIMUM_ANT_VERSION = "minimum-ant-version"; private static final String TAG_FILE = "file"; private static final String TAG_LIBRARY = "library"; private static final String ATTR_FILES = "files"; private static final String ATTR_DIRS = "dirs"; 80 87 UpdateHelper (Project project, AntProjectHelper helper, AuxiliaryConfiguration cfg, Notifier notifier) { 88 assert project != null && helper != null && cfg != null && notifier != null; 89 this.project = project; 90 this.helper = helper; 91 this.cfg = cfg; 92 this.notifier = notifier; 93 } 94 95 100 public EditableProperties getProperties (final String path) { 101 return (EditableProperties) ProjectManager.mutex().readAccess(new Mutex.Action (){ 103 public Object run() { 104 if (!isCurrent() && AntProjectHelper.PROJECT_PROPERTIES_PATH.equals(path)) { return getUpdatedProjectProperties (); 106 } 107 else { 108 return helper.getProperties(path); 109 } 110 } 111 }); 112 } 113 114 122 public void putProperties (final String path, final EditableProperties props) { 123 ProjectManager.mutex().writeAccess( 124 new Runnable () { 125 public void run() { 126 if (isCurrent() || !AntProjectHelper.PROJECT_PROPERTIES_PATH.equals(path)) { helper.putProperties(path,props); 128 } 129 else if (canUpdate()) { 130 try { 131 saveUpdate (props); 132 helper.putProperties(path,props); 133 } catch (IOException ioe) { 134 ErrorManager.getDefault().notify (ioe); 135 } 136 } 137 } 138 }); 139 } 140 141 149 public Element getPrimaryConfigurationData (final boolean shared) { 150 return (Element ) ProjectManager.mutex().readAccess(new Mutex.Action (){ 151 public Object run() { 152 if (!shared || isCurrent()) { return helper.getPrimaryConfigurationData(shared); 154 } 155 else { 156 return getUpdatedSharedConfigurationData (); 157 } 158 } 159 }); 160 } 161 162 171 public void putPrimaryConfigurationData (final Element element, final boolean shared) { 172 ProjectManager.mutex().writeAccess(new Runnable () { 173 public void run () { 174 if (!shared || isCurrent()) { 175 helper.putPrimaryConfigurationData(element, shared); 176 } else if (canUpdate()) { 177 try { 178 saveUpdate (null); 179 helper.putPrimaryConfigurationData(element, shared); 180 } catch (IOException ioe) { 181 ErrorManager.getDefault().notify(ioe); 182 } 183 } 184 } 185 }); 186 } 187 188 193 public AntProjectHelper getAntProjectHelper () { 194 return this.helper; 195 } 196 197 202 public boolean requestSave () throws IOException { 203 if (isCurrent()) { 204 return true; 205 } 206 if (!canUpdate()) { 207 return false; 208 } 209 saveUpdate (null); 210 return true; 211 } 212 213 217 public synchronized boolean isCurrent () { 218 if (this.isCurrent == null) { 219 this.isCurrent = this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/web-project/1",true) == null && this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/web-project/2",true) == null? Boolean.TRUE : Boolean.FALSE; 222 } 223 return isCurrent.booleanValue(); 224 } 225 226 private boolean canUpdate () { 227 if (TRANSPARENT_UPDATE) { 228 return true; 229 } 230 if (alreadyAskedInWriteAccess) { 232 return false; 233 } 234 else { 235 boolean canUpdate = this.notifier.canUpdate(); 236 if (!canUpdate) { 237 alreadyAskedInWriteAccess = true; 238 ProjectManager.mutex().postReadRequest(new Runnable () { 239 public void run() { 240 alreadyAskedInWriteAccess = false; 241 } 242 }); 243 } 244 return canUpdate; 245 } 246 } 247 248 private void saveUpdate (EditableProperties props) throws IOException { 249 this.helper.putPrimaryConfigurationData(getUpdatedSharedConfigurationData(),true); 250 if (this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/web-project/1",true) != null) { this.cfg.removeConfigurationFragment("data","http://www.netbeans.org/ns/web-project/1",true); } else { 253 this.cfg.removeConfigurationFragment("data","http://www.netbeans.org/ns/web-project/2",true); } 255 256 boolean putProps = false; 257 258 if (props == null) { 261 props = getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); 262 putProps = true; 263 } 264 265 if(props != null) { 267 props.put("test.src.dir", "test"); props.put("build.test.classes.dir", "${build.dir}/test/classes"); props.put("build.test.results.dir", "${build.dir}/test/results"); props.put("conf.dir","${source.root}/conf"); props.put("jspcompilation.classpath", "${jspc.classpath}:${javac.classpath}"); 272 273 props.setProperty(WebProjectProperties.JAVAC_TEST_CLASSPATH, new String [] { 274 "${javac.classpath}:", "${build.classes.dir}:", "${libs.junit.classpath}", }); 278 props.setProperty(WebProjectProperties.RUN_TEST_CLASSPATH, new String [] { 279 "${javac.test.classpath}:", "${build.test.classes.dir}", }); 282 props.setProperty(WebProjectProperties.DEBUG_TEST_CLASSPATH, new String [] { 283 "${run.test.classpath}", }); 285 286 props.put(WebProjectProperties.BUILD_EAR_CLASSES_DIR, "${build.ear.web.dir}/WEB-INF/classes"); 287 props.put(WebProjectProperties.BUILD_EAR_WEB_DIR, "${build.dir}/ear-module"); 288 props.put(WebProjectProperties.WAR_EAR_NAME, props.getProperty(WebProjectProperties.WAR_NAME)); 289 props.put(WebProjectProperties.DIST_WAR_EAR, "${dist.dir}/${war.ear.name}"); 290 291 if (props.getProperty(WebProjectProperties.LIBRARIES_DIR) == null) { 292 props.setProperty(WebProjectProperties.LIBRARIES_DIR, "${" + WebProjectProperties.WEB_DOCBASE_DIR + "}/WEB-INF/lib"); } 294 } 295 296 if(props != null) { 297 ReferenceHelper refHelper = new ReferenceHelper(helper, cfg, helper.getStandardPropertyEvaluator()); 299 ClassPathSupport cs = new ClassPathSupport( helper.getStandardPropertyEvaluator(), refHelper, helper, 300 WebProjectProperties.WELL_KNOWN_PATHS, 301 WebProjectProperties.LIBRARY_PREFIX, 302 WebProjectProperties.LIBRARY_SUFFIX, 303 WebProjectProperties.ANT_ARTIFACT_PREFIX ); 304 Iterator items = cs.itemsIterator((String )props.get( WebProjectProperties.JAVAC_CLASSPATH ), ClassPathSupport.TAG_WEB_MODULE_LIBRARIES); 305 ArrayList cpItems = new ArrayList (); 306 while(items.hasNext()) { 307 ClassPathSupport.Item cpti = (ClassPathSupport.Item)items.next(); 308 String propertyName = cpti.getReference(); 309 if(propertyName != null) { 310 String libname = propertyName.substring("${libs.".length()); 311 if(libname.indexOf(".classpath}") != -1) libname = libname.substring(0, libname.indexOf(".classpath}")); 312 313 if(!("servlet24".equals(libname) || "jsp20".equals(libname))) { cpItems.add(cpti); 315 } 316 } 317 } 318 String [] javac_cp = cs.encodeToStrings(cpItems.iterator(), ClassPathSupport.TAG_WEB_MODULE_LIBRARIES ); 319 props.setProperty( WebProjectProperties.JAVAC_CLASSPATH, javac_cp ); 320 } 321 322 if (putProps) { 323 helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props); 324 } 325 326 ProjectManager.getDefault().saveProject (this.project); 327 synchronized(this) { 328 this.isCurrent = Boolean.TRUE; 329 } 330 331 if(projectUpdateListener != null) projectUpdateListener.projectUpdated(); 333 334 try { 336 FileObject prjFO = project.getProjectDirectory(); 339 FileObject confDirFO = prjFO.createFolder("conf"); FileUtil.copyFile(Repository.getDefault().getDefaultFileSystem().findResource("org-netbeans-modules-web-project/MANIFEST.MF"), confDirFO, "MANIFEST"); }catch(IOException e) { 345 } 347 348 } 349 350 private synchronized Element getUpdatedSharedConfigurationData () { 351 if (cachedElement == null) { 352 int version = 1; 353 Element oldRoot = this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/web-project/1",true); if (oldRoot == null) { 355 version = 2; 356 oldRoot = this.cfg.getConfigurationFragment("data","http://www.netbeans.org/ns/web-project/2",true); } 358 final String ns = version == 1 ? "http://www.netbeans.org/ns/web-project/1" : "http://www.netbeans.org/ns/web-project/2"; if (oldRoot != null) { 360 Document doc = oldRoot.getOwnerDocument(); 361 Element newRoot = doc.createElementNS (WebProjectType.PROJECT_CONFIGURATION_NAMESPACE,"data"); copyDocument (doc, oldRoot, newRoot); 363 if (version == 1) { 364 Element sourceRoots = doc.createElementNS(WebProjectType.PROJECT_CONFIGURATION_NAMESPACE,"source-roots"); Element root = doc.createElementNS (WebProjectType.PROJECT_CONFIGURATION_NAMESPACE,"root"); root.setAttribute ("id","src.dir"); sourceRoots.appendChild(root); 369 newRoot.appendChild (sourceRoots); 370 Element testRoots = doc.createElementNS(WebProjectType.PROJECT_CONFIGURATION_NAMESPACE,"test-roots"); root = doc.createElementNS (WebProjectType.PROJECT_CONFIGURATION_NAMESPACE,"root"); root.setAttribute ("id","test.src.dir"); testRoots.appendChild (root); 374 newRoot.appendChild (testRoots); 375 } 376 if (version == 1 || version == 2) { 377 NodeList libList = newRoot.getElementsByTagNameNS(ns, TAG_LIBRARY); 379 for (int i = 0; i < libList.getLength(); i++) { 380 if (libList.item(i).getNodeType() == Node.ELEMENT_NODE) { 381 Element library = (Element ) libList.item(i); 382 Node webFile = library.getElementsByTagNameNS(ns, TAG_FILE).item(0); 383 String webFileText = findText(webFile); 385 webFileText = webFileText.substring(2, webFileText.length() - 1); 386 if (webFileText.startsWith ("lib.")) { 388 String libName = webFileText.substring(6, webFileText.indexOf(".classpath")); List roots = LibraryManager.getDefault().getLibrary(libName).getContent("classpath"); ArrayList files = new ArrayList (); 391 ArrayList dirs = new ArrayList (); 392 for (Iterator it = roots.iterator(); it.hasNext();) { 393 URL rootUrl = (URL ) it.next(); 394 FileObject root = org.openide.filesystems.URLMapper.findFileObject (rootUrl); 395 if ("jar".equals(rootUrl.getProtocol())) { root = FileUtil.getArchiveFile (root); 397 } 398 if (root != null) { 399 if (root.isData()) { 400 files.add(root); 401 } else { 402 dirs.add(root); 403 } 404 } 405 } 406 if (files.size() > 0) { 407 library.setAttribute(ATTR_FILES, "" + files.size()); 408 } 409 if (dirs.size() > 0) { 410 library.setAttribute(ATTR_DIRS, "" + dirs.size()); 411 } 412 } 413 } 414 } 415 } 416 cachedElement = updateMinAntVersion(newRoot, doc); 417 } 418 } 419 return cachedElement; 420 } 421 422 private synchronized EditableProperties getUpdatedProjectProperties () { 423 if (cachedProperties == null) { 424 cachedProperties = this.helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH); 425 if (cachedProperties.get (WebProjectProperties.JAVADOC_ADDITIONALPARAM)==null) { 427 cachedProperties.put (WebProjectProperties.JAVADOC_ADDITIONALPARAM,""); } 429 } 430 return this.cachedProperties; 431 } 432 433 private static void copyDocument (Document doc, Element from, Element to) { 434 NodeList nl = from.getChildNodes(); 435 int length = nl.getLength(); 436 for (int i=0; i< length; i++) { 437 Node node = nl.item (i); 438 Node newNode = null; 439 switch (node.getNodeType()) { 440 case Node.ELEMENT_NODE: 441 Element oldElement = (Element ) node; 442 newNode = doc.createElementNS(WebProjectType.PROJECT_CONFIGURATION_NAMESPACE,oldElement.getTagName()); 443 NamedNodeMap m = oldElement.getAttributes(); 444 Element newElement = (Element ) newNode; 445 for (int index = 0; index < m.getLength(); index++) { 446 Node attr = m.item(index); 447 newElement.setAttribute(attr.getNodeName(), attr.getNodeValue()); 448 } 449 copyDocument(doc,oldElement,(Element )newNode); 450 break; 451 case Node.TEXT_NODE: 452 Text oldText = (Text ) node; 453 newNode = doc.createTextNode(oldText.getData()); 454 break; 455 case Node.COMMENT_NODE: 456 Comment oldComment = (Comment ) node; 457 newNode = doc.createComment(oldComment.getData()); 458 break; 459 } 460 if (newNode != null) { 461 to.appendChild (newNode); 462 } 463 } 464 } 465 466 private static Element updateMinAntVersion (final Element root, final Document doc) { 467 NodeList list = root.getElementsByTagNameNS (WebProjectType.PROJECT_CONFIGURATION_NAMESPACE,TAG_MINIMUM_ANT_VERSION); 468 if (list.getLength() == 1) { 469 Element me = (Element ) list.item(0); 470 list = me.getChildNodes(); 471 if (list.getLength() == 1) { 472 me.replaceChild (doc.createTextNode(WebProjectUtilities.MINIMUM_ANT_VERSION), list.item(0)); 473 return root; 474 } 475 } 476 assert false : "Invalid project file"; return root; 478 } 479 480 484 public static Notifier createDefaultNotifier () { 485 return new Notifier() { 486 public boolean canUpdate() { 487 JButton updateOption = new JButton (NbBundle.getMessage(UpdateHelper.class, "CTL_UpdateOption")); 488 return DialogDisplayer.getDefault().notify( 489 new NotifyDescriptor (NbBundle.getMessage(UpdateHelper.class,"TXT_ProjectUpdate", BUILD_NUMBER), 490 NbBundle.getMessage(UpdateHelper.class,"TXT_ProjectUpdateTitle"), 491 NotifyDescriptor.DEFAULT_OPTION, 492 NotifyDescriptor.WARNING_MESSAGE, 493 new Object [] { 494 updateOption, 495 NotifyDescriptor.CANCEL_OPTION 496 }, 497 updateOption)) == updateOption; 498 } 499 }; 500 } 501 502 508 private static String findText(Node parent) { 509 NodeList l = parent.getChildNodes(); 510 for (int i = 0; i < l.getLength(); i++) { 511 if (l.item(i).getNodeType() == Node.TEXT_NODE) { 512 Text text = (Text )l.item(i); 513 return text.getNodeValue(); 514 } 515 } 516 return null; 517 } 518 519 523 public static interface Notifier { 524 528 public boolean canUpdate (); 529 } 530 531 private ProjectUpdateListener projectUpdateListener = null; 532 533 public void setProjectUpdateListener(ProjectUpdateListener l) { 534 this.projectUpdateListener = l; 535 } 536 537 539 public static interface ProjectUpdateListener { 540 public void projectUpdated(); 541 } 542 543 } 544 | Popular Tags |