--- a/apisupport.harness/nbproject/project.properties Mon Nov 23 00:52:40 2009 +0100 +++ a/apisupport.harness/nbproject/project.properties Mon Nov 23 06:41:44 2009 +0100 @@ -68,6 +68,7 @@ org/netbeans/nbbuild/Arch-fake-xhtml.dtd,\ org/netbeans/nbbuild/Arch.xsl,\ org/netbeans/nbbuild/Arch-api-questions.xml,\ + org/netbeans/nbbuild/AutoUpdate*,\ org/netbeans/nbbuild/Branding*.class,\ org/netbeans/nbbuild/CreateModuleXML*.class,\ org/netbeans/nbbuild/CustomJavac*.class,\ --- a/apisupport.harness/release/suite.xml Mon Nov 23 00:52:40 2009 +0100 +++ a/apisupport.harness/release/suite.xml Mon Nov 23 06:41:44 2009 +0100 @@ -519,6 +519,14 @@ + + + + + + + + --- a/apisupport.harness/taskdefs.properties Mon Nov 23 00:52:40 2009 +0100 +++ a/apisupport.harness/taskdefs.properties Mon Nov 23 06:41:44 2009 +0100 @@ -55,3 +55,4 @@ shorterpaths=org.netbeans.nbbuild.ShorterPaths custom-javac=org.netbeans.nbbuild.CustomJavac convertclusterpath=org.netbeans.nbbuild.ConvertClusterPath +autoupdate=org.netbeans.nbbuild.AutoUpdate --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 73039bf235f9 Mon Nov 23 06:41:44 2009 +0100 @@ -0,0 +1,267 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.nbbuild; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.taskdefs.Get; +import org.netbeans.nbbuild.AutoUpdateCatalogParser.ModuleItem; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * + * @author Jaroslav Tulach + */ +public class AutoUpdate extends Task { + private List modules = new ArrayList(); + private File dir; + private URL catalog; + + public void setUpdateCenter(URL u) { + catalog = u; + } + + public void setNetBeansDestDir(File dir) { + this.dir = dir; + } + + public Modules createModules() { + final Modules m = new Modules(); + modules.add(m); + return m; + } + + @Override + public void execute() throws BuildException { + File[] arr = dir == null ? null : dir.listFiles(); + if (arr == null) { + throw new BuildException("netbeans.dest.dir must be existing directory: " + dir); + } + + Map> installed = findExistingModules(dir); + + // no userdir + Map units = AutoUpdateCatalogParser.getUpdateItems(catalog, catalog, this); + for (ModuleItem uu : units.values()) { + if (!matches(uu.getCodeName())) { + continue; + } + log("found module: " + uu, Project.MSG_VERBOSE); + List info = installed.get(uu.getCodeName()); + if (info != null && !uu.isNewerThan(info.get(0))) { + log("Version " + info.get(0) + " of " + uu.getCodeName() + " is up to date", Project.MSG_VERBOSE); + continue; + } + if (info == null) { + log(uu.getCodeName() + " is not present, downloading version " + uu.getSpecVersion(), Project.MSG_INFO); + } else { + log("Version " + info.get(0) + " of " + uu.getCodeName() + " needs update to " + uu.getSpecVersion(), Project.MSG_INFO); + } + + byte[] bytes = new byte[4096]; + File tmp = null; + try { + final String dash = uu.getCodeName().replace('.', '-'); + tmp = File.createTempFile(dash, ".nbm"); + tmp.deleteOnExit(); + Get get = new Get(); + get.setProject(getProject()); + get.setTaskName("get:" + uu.getCodeName()); + get.setSrc(uu.getURL()); + get.setDest(tmp); + get.setVerbose(true); + get.execute(); + + File cluster = new File(dir, uu.targetcluster); + + if (info != null) { + for (int i = 1; i < info.size(); i++) { + File oldFile = new File(cluster, info.get(i).replace('/', File.separatorChar)); + oldFile.delete(); + } + } + + File tracking = new File(new File(cluster, "update_tracking"), dash + ".xml"); + tracking.getParentFile().mkdirs(); + OutputStream config = new BufferedOutputStream(new FileOutputStream(tracking)); + config.write(("\n" + + "\n").getBytes("UTF-8")); + config.write((" \n").getBytes("UTF-8")); + + ZipFile zf = new ZipFile(tmp); + Enumeration en = zf.entries(); + while (en.hasMoreElements()) { + ZipEntry zipEntry = en.nextElement(); + if (!zipEntry.getName().startsWith("netbeans/")) { + continue; + } + if (zipEntry.getName().endsWith("/")) { + continue; + } + final String relName = zipEntry.getName().substring(9); + File trgt = new File(cluster, relName.replace('/', File.separatorChar)); + trgt.getParentFile().mkdirs(); + log("Writing " + trgt, Project.MSG_VERBOSE); + + InputStream is = zf.getInputStream(zipEntry); + OutputStream os = new FileOutputStream(trgt); + CRC32 crc = new CRC32(); + for (;;) { + int len = is.read(bytes); + if (len == -1) { + break; + } + crc.update(bytes, 0, len); + os.write(bytes, 0, len); + } + is.close(); + os.close(); + config.write(("\n").getBytes("UTF-8")); + } + config.write(" \n\n".getBytes("UTF-8")); + config.close(); + } catch (IOException ex) { + throw new BuildException(ex); + } finally { + if (tmp != null) { + tmp.delete(); + } + } + } + } + + private boolean matches(String cnb) { + String dash = cnb.replace('.', '-'); + for (Modules ps : modules) { + if (ps.pattern.matcher(dash).matches()) { + return true; + } + } + return false; + } + + private Map> findExistingModules(File dir) { + Map> all = new HashMap>(); + for (File cluster : dir.listFiles()) { + File mc = new File(cluster, "update_tracking"); + final File[] arr = mc.listFiles(); + if (arr == null) { + continue; + } + for (File m : arr) { + try { + parseVersion(m, all); + } catch (Exception ex) { + log("Cannot parse " + m, ex, Project.MSG_WARN); + } + } + } + return all; + } + + private void parseVersion(final File config, final Map> toAdd) throws Exception { + class P extends DefaultHandler { + String name; + List arr; + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if ("module".equals(qName)) { + name = attributes.getValue("codename"); + return; + } + if ("module_version".equals(qName)) { + String version = attributes.getValue("specification_version"); + if (name == null || version == null) { + throw new BuildException("Cannot find version in " + config); + } + arr = new ArrayList(); + arr.add(version); + toAdd.put(name, arr); + return; + } + if ("file".equals(qName)) { + arr.add(attributes.getValue("name")); + } + } + + @Override + public InputSource resolveEntity(String string, String string1) throws IOException, SAXException { + return new InputSource(new ByteArrayInputStream(new byte[0])); + } + } + P p = new P(); + SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); + parser.parse(config, p); + } + + public static final class Modules { + Pattern pattern; + + public void setIncludes(String regExp) { + pattern = Pattern.compile(regExp); + } + } +} --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 73039bf235f9 Mon Nov 23 06:41:44 2009 +0100 @@ -0,0 +1,625 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2008 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.netbeans.nbbuild; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPInputStream; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.xml.sax.Attributes; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * + * @author Jiri Rechtacek + */ +class AutoUpdateCatalogParser extends DefaultHandler { + private final Map items; + private final URL provider; + private final EntityResolver entityResolver; + private final URI baseUri; + + private AutoUpdateCatalogParser (Map items, URL provider, URI base) { + this.items = items; + this.provider = provider; + this.entityResolver = newEntityResolver(); + this.baseUri = base; + } + + private EntityResolver newEntityResolver () { + return new EntityResolver() { + public InputSource resolveEntity(String string, String string1) throws SAXException, IOException { + return new InputSource(new ByteArrayInputStream(new byte[0])); + } + }; + } + + + private static final Logger ERR = Logger.getLogger (AutoUpdateCatalogParser.class.getName ()); + + private static enum ELEMENTS { + module_updates, module_group, notification, module, description, + module_notification, external_package, manifest, l10n, license + } + + private static final String MODULE_UPDATES_ATTR_TIMESTAMP = "timestamp"; // NOI18N + + private static final String MODULE_GROUP_ATTR_NAME = "name"; // NOI18N + + private static final String NOTIFICATION_ATTR_URL = "url"; // NOI18N + + private static final String LICENSE_ATTR_NAME = "name"; // NOI18N + + private static final String MODULE_ATTR_CODE_NAME_BASE = "codenamebase"; // NOI18N + private static final String MODULE_ATTR_HOMEPAGE = "homepage"; // NOI18N + private static final String MODULE_ATTR_DISTRIBUTION = "distribution"; // NOI18N + private static final String MODULE_ATTR_DOWNLOAD_SIZE = "downloadsize"; // NOI18N + private static final String MODULE_ATTR_NEEDS_RESTART = "needsrestart"; // NOI18N + private static final String MODULE_ATTR_MODULE_AUTHOR = "moduleauthor"; // NOI18N + private static final String MODULE_ATTR_RELEASE_DATE = "releasedate"; // NOI18N + private static final String MODULE_ATTR_IS_GLOBAL = "global"; // NOI18N + private static final String MODULE_ATTR_TARGET_CLUSTER = "targetcluster"; // NOI18N + private static final String MODULE_ATTR_EAGER = "eager"; // NOI18N + private static final String MODULE_ATTR_AUTOLOAD = "autoload"; // NOI18N + private static final String MODULE_ATTR_LICENSE = "license"; // NOI18N + private static final String LICENSE_ATTR_URL = "url"; // NOI18N + + private static final String MANIFEST_ATTR_SPECIFICATION_VERSION = "OpenIDE-Module-Specification-Version"; // NOI18N + + private static final String TIME_STAMP_FORMAT = "ss/mm/hh/dd/MM/yyyy"; // NOI18N + + private static final String L10N_ATTR_LOCALE = "langcode"; // NOI18N + private static final String L10N_ATTR_BRANDING = "brandingcode"; // NOI18N + private static final String L10N_ATTR_MODULE_SPECIFICATION = "module_spec_version"; // NOI18N + private static final String L10N_ATTR_MODULE_MAJOR_VERSION = "module_major_version"; // NOI18N + private static final String L10N_ATTR_LOCALIZED_MODULE_NAME = "OpenIDE-Module-Name"; // NOI18N + private static final String L10N_ATTR_LOCALIZED_MODULE_DESCRIPTION = "OpenIDE-Module-Long-Description"; // NOI18N + + private static String GZIP_EXTENSION = ".gz"; // NOI18N + + private static Map cache; + private static URI cacheURI; + synchronized static Map getUpdateItems (URL url, URL provider, Task task) { + + Map items = new HashMap (); + URI base; + try { + if (provider != null) { + base = provider.toURI(); + } else { + base = url.toURI(); + } + if (cache != null && cacheURI.equals(base)) { + task.log("Using existing module item cache " + base, Project.MSG_INFO); + return cache; + } + task.log("Downloading " + base, Project.MSG_INFO); + InputSource is = null; + try { + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setValidating(false); + SAXParser saxParser = factory.newSAXParser(); + is = getInputSource(url, provider, base); + saxParser.parse(is, new AutoUpdateCatalogParser(items, provider, base)); + cacheURI = base; + cache = items; + } catch (Exception ex) { + ERR.log(Level.INFO, "Failed to parse " + base, ex); + } finally { + if (is != null && is.getByteStream() != null) { + try { + is.getByteStream().close(); + } catch (IOException e) { + } + } + } + } catch (URISyntaxException ex) { + ERR.log(Level.INFO, null, ex); + } + return items; + } + + private static boolean isGzip (URL url) { + boolean res = false; + if (url != null) { + res = url.getPath ().toLowerCase ().endsWith (GZIP_EXTENSION); + ERR.log (Level.FINER, "Is GZIP " + url + " ? " + res); + } else { + ERR.log (Level.WARNING, "AutoupdateCatalogProvider has not URL."); + } + return res; + } + + private static InputSource getInputSource(URL toParse, URL p, URI base) { + InputStream is = null; + try { + is = toParse.openStream (); + if (isGzip (p)) { + try { + is = new GZIPInputStream(is); + } catch (IOException e) { + ERR.log (Level.INFO, + "The file at " + toParse + + ", corresponding to the catalog at " + p + + ", does not look like the gzip file, trying to parse it as the pure xml" , e); + //#150034 + // Sometimes the .xml.gz file is downloaded as the pure .xml file due to the strange content-encoding processing + is.close(); + is = null; + is = toParse.openStream(); + } + } + InputSource src = new InputSource(new BufferedInputStream (is)); + src.setSystemId(base.toString()); + return src; + } catch (IOException ex) { + if(is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + ERR.log (Level.SEVERE, "Cannot estabilish input stream for " + toParse , ex); + return new InputSource(); + } + } + + private Stack currentGroup = new Stack (); + private String catalogDate; + private Stack currentModule = new Stack (); + private Stack> currentLicense = new Stack> (); + private Stack currentNotificationUrl = new Stack (); + private List lines = new ArrayList (); + private int bufferInitSize = 0; + + @Override + public void characters (char[] ch, int start, int length) throws SAXException { + lines.add (new String(ch, start, length)); + bufferInitSize += length; + } + + @Override + public void endElement (String uri, String localName, String qName) throws SAXException { + switch (ELEMENTS.valueOf (qName)) { + case module_updates : + break; + case module_group : + assert ! currentGroup.empty () : "Premature end of module_group " + qName; + currentGroup.pop (); + break; + case module : + assert ! currentModule.empty () : "Premature end of module " + qName; + currentModule.pop (); + break; + case l10n : + break; + case manifest : + break; + case description : + ERR.info ("Not supported yet."); + break; + case notification : + // write catalog notification + if (this.provider != null && ! lines.isEmpty ()) { + StringBuffer sb = new StringBuffer (bufferInitSize); + for (String line : lines) { + sb.append (line); + } + String notification = sb.toString (); + String notificationUrl = currentNotificationUrl.peek (); + if (notificationUrl != null && notificationUrl.length () > 0) { + notification += (notification.length () > 0 ? "
" : "") + // NOI18N + "" + notificationUrl + ""; // NOI18N + } else { + notification += (notification.length () > 0 ? "
" : "") + + ""; // NOI18N + } + } + currentNotificationUrl.pop (); + break; + case module_notification : + // write module notification + if (! lines.isEmpty ()) { + ModuleDescriptor md = currentModule.peek (); + assert md != null : "ModuleDescriptor found for " + provider; + StringBuffer buf = new StringBuffer (bufferInitSize); + for (String line : lines) { + buf.append (line); + } + md.appendNotification (buf.toString ()); + } + break; + case external_package : + ERR.info ("Not supported yet."); + break; + case license : + assert ! currentLicense.empty () : "Premature end of license " + qName; + Map curLic = currentLicense.peek (); + String licenseName = curLic.keySet().iterator().next(); + Collection values = curLic.values(); + String licenseUrl = (values.size() > 0) ? values.iterator().next() : null; + + currentLicense.pop (); + break; + default: + ERR.warning ("Unknown element " + qName); + } + } + + @Override + public void endDocument () throws SAXException { + ERR.fine ("End parsing " + (provider == null ? "" : provider) + " at " + System.currentTimeMillis ()); + } + + @Override + public void startDocument () throws SAXException { + ERR.fine ("Start parsing " + (provider == null ? "" : provider) + " at " + System.currentTimeMillis ()); + } + + @Override + public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException { + lines.clear(); + bufferInitSize = 0; + switch (ELEMENTS.valueOf (qName)) { + case module_updates : + try { + catalogDate = ""; + DateFormat format = new SimpleDateFormat (TIME_STAMP_FORMAT); + String timeStamp = attributes.getValue (MODULE_UPDATES_ATTR_TIMESTAMP); + if (timeStamp == null) { + ERR.info ("No timestamp is presented in " + (this.provider == null ? "" : this.provider)); + } else { + //catalogDate = Utilities.formatDate (format.parse (timeStamp)); + catalogDate = format.parse (timeStamp).toString(); + ERR.finer ("Successfully read time " + timeStamp); // NOI18N + } + } catch (ParseException pe) { + ERR.log (Level.INFO, null, pe); + } + break; + case module_group : + currentGroup.push (attributes.getValue (MODULE_GROUP_ATTR_NAME)); + break; + case module : + ModuleDescriptor md = ModuleDescriptor.getModuleDescriptor ( + currentGroup.size () > 0 ? currentGroup.peek () : null, /* group */ + baseUri, /* base URI */ + this.catalogDate); /* catalog date */ + md.appendModuleAttributes (attributes); + currentModule.push (md); + break; + case l10n : + // construct l10n + // XXX + break; + case manifest : + + // construct module + ModuleDescriptor desc = currentModule.peek (); + desc.appendManifest (attributes); + ModuleItem m = desc.createUpdateItem (); + + // put module into UpdateItems + items.put (desc.getId (), m); + + break; + case description : + ERR.info ("Not supported yet."); + break; + case module_notification : + break; + case notification : + currentNotificationUrl.push (attributes.getValue (NOTIFICATION_ATTR_URL)); + break; + case external_package : + ERR.info ("Not supported yet."); + break; + case license : + Map map = new HashMap (); + map.put(attributes.getValue (LICENSE_ATTR_NAME), attributes.getValue (LICENSE_ATTR_URL)); + currentLicense.push (map); + break; + default: + ERR.warning ("Unknown element " + qName); + } + } + + @Override + public void warning(SAXParseException e) throws SAXException { + parseError(e); + } + + @Override + public void error(SAXParseException e) throws SAXException { + parseError(e); + } + + @Override + public void fatalError(SAXParseException e) throws SAXException { + parseError(e); + } + + private void parseError(SAXParseException e) { + ERR.warning(e.getSystemId() + ":" + e.getLineNumber() + ":" + e.getColumnNumber() + ": " + e.getLocalizedMessage()); + } + + @Override + public InputSource resolveEntity (String publicId, String systemId) throws IOException, SAXException { + return entityResolver.resolveEntity (publicId, systemId); + } + + private static class ModuleDescriptor { + private String moduleCodeName; + private URL distributionURL; + private String targetcluster; + private String homepage; + private String downloadSize; + private String author; + private String publishDate; + private String notification; + + private Boolean needsRestart; + private Boolean isGlobal; + private Boolean isEager; + private Boolean isAutoload; + + private String specVersion; + private Manifest mf; + + private String id; + + private String group; + private URI base; + private String catalogDate; + + private static ModuleDescriptor md = null; + + private ModuleDescriptor () {} + + public static ModuleDescriptor getModuleDescriptor (String group, URI base, String catalogDate) { + if (md == null) { + md = new ModuleDescriptor (); + } + + md.group = group; + md.base = base; + md.catalogDate = catalogDate; + + return md; + } + + public void appendModuleAttributes (Attributes module) { + moduleCodeName = module.getValue (MODULE_ATTR_CODE_NAME_BASE); + distributionURL = getDistribution (module.getValue (MODULE_ATTR_DISTRIBUTION), base); + targetcluster = module.getValue (MODULE_ATTR_TARGET_CLUSTER); + homepage = module.getValue (MODULE_ATTR_HOMEPAGE); + downloadSize = module.getValue (MODULE_ATTR_DOWNLOAD_SIZE); + author = module.getValue (MODULE_ATTR_MODULE_AUTHOR); + publishDate = module.getValue (MODULE_ATTR_RELEASE_DATE); + if (publishDate == null || publishDate.length () == 0) { + publishDate = catalogDate; + } + String needsrestart = module.getValue (MODULE_ATTR_NEEDS_RESTART); + String global = module.getValue (MODULE_ATTR_IS_GLOBAL); + String eager = module.getValue (MODULE_ATTR_EAGER); + String autoload = module.getValue (MODULE_ATTR_AUTOLOAD); + + needsRestart = needsrestart == null || needsrestart.trim ().length () == 0 ? null : Boolean.valueOf (needsrestart); + isGlobal = global == null || global.trim ().length () == 0 ? null : Boolean.valueOf (global); + isEager = Boolean.parseBoolean (eager); + isAutoload = Boolean.parseBoolean (autoload); + + String licName = module.getValue (MODULE_ATTR_LICENSE); + } + + public void appendManifest (Attributes manifest) { + specVersion = manifest.getValue (MANIFEST_ATTR_SPECIFICATION_VERSION); + mf = getManifest (manifest); + id = moduleCodeName + '_' + specVersion; // NOI18N + } + + public void appendNotification (String notification) { + this.notification = notification; + } + + public String getId () { + return id; + } + + public ModuleItem createUpdateItem () { + ModuleItem res = ModuleItem.createModule ( + moduleCodeName, + specVersion, + distributionURL, + author, + downloadSize, + homepage, + publishDate, + group, + mf, + isEager, + isAutoload, + needsRestart, + isGlobal, + targetcluster, + null); + + // clean-up ModuleDescriptor + cleanUp (); + + return res; + } + + private void cleanUp (){ + this.specVersion = null; + this.mf = null; + this.notification = null; + } + } + + private static URL getDistribution (String distribution, URI base) { + URL retval = null; + if (distribution != null && distribution.length () > 0) { + try { + URI distributionURI = new URI (distribution); + if (! distributionURI.isAbsolute ()) { + if (base != null) { + distributionURI = base.resolve (distributionURI); + } + } + retval = distributionURI.toURL (); + } catch (MalformedURLException ex) { + ERR.log (Level.INFO, null, ex); + } catch (URISyntaxException ex) { + ERR.log (Level.INFO, null, ex); + } + } + return retval; + } + + private static Manifest getManifest (Attributes attrList) { + Manifest mf = new Manifest (); + java.util.jar.Attributes mfAttrs = mf.getMainAttributes (); + + for (int i = 0; i < attrList.getLength (); i++) { + mfAttrs.put (new java.util.jar.Attributes.Name (attrList.getQName (i)), attrList.getValue (i)); + } + return mf; + } + + public static final class ModuleItem { + private final String moduleCodeName; + private final String specVersion; + private final URL distributionURL; + public final String targetcluster; + + private ModuleItem(String moduleCodeName, String specVersion, URL distributionURL, String targetcluster) { + this.moduleCodeName = moduleCodeName; + this.specVersion = specVersion; + this.distributionURL = distributionURL; + this.targetcluster = targetcluster; + } + + + + static ModuleItem createModule( + String moduleCodeName, + String specVersion, + URL distributionURL, + String author, + String downloadSize, + String homepage, + String publishDate, + String group, + Manifest mf, + Boolean eager, + Boolean autoload, + Boolean needsRestart, + Boolean global, + String targetcluster, + Object object + ) { + return new ModuleItem(moduleCodeName, specVersion, distributionURL, targetcluster); + } + + public String getCodeName() { + return moduleCodeName; + } + + String getSpecVersion() { + return specVersion; + } + + URL getURL() { + return distributionURL; + } + + @Override + public String toString() { + return "[" + moduleCodeName + "@" + specVersion + "(" + targetcluster + ") <- " + distributionURL + "]"; + } + + boolean isNewerThan(String version) { + String[] mine = specVersion.split("\\."); + String[] its = version.split("\\."); + + int min = Math.min(mine.length, its.length); + for (int i = 0; i < min; i++) { + int m = Integer.parseInt(mine[i]); + int it = Integer.parseInt(its[i]); + + if (m > it) { + return true; + } + if (m < it) { + return false; + } + } + return mine.length > its.length; + } + } +} --- a/nbbuild/build.xml Mon Nov 23 00:52:40 2009 +0100 +++ a/nbbuild/build.xml Mon Nov 23 06:41:44 2009 +0100 @@ -160,6 +160,25 @@ + + + + + + + + + + + + + + + + + + + --- a/nbbuild/default-properties.xml Mon Nov 23 00:52:40 2009 +0100 +++ a/nbbuild/default-properties.xml Mon Nov 23 06:41:44 2009 +0100 @@ -58,6 +58,8 @@ + + --- a/nbbuild/default.xml Mon Nov 23 00:52:40 2009 +0100 +++ a/nbbuild/default.xml Mon Nov 23 06:41:44 2009 +0100 @@ -87,6 +87,9 @@ + --- a/nbbuild/templates/common.xml Mon Nov 23 00:52:40 2009 +0100 +++ a/nbbuild/templates/common.xml Mon Nov 23 06:41:44 2009 +0100 @@ -162,6 +162,19 @@ + + + + + + + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 73039bf235f9 Mon Nov 23 06:41:44 2009 +0100 @@ -0,0 +1,255 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2009 Sun Microsystems, Inc. + */ + +package org.netbeans.nbbuild; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.netbeans.junit.NbTestCase; + +/** + * + * @author Jaroslav Tulach + */ +public class AutoUpdateTest extends NbTestCase { + + public AutoUpdateTest(String name) { + super(name); + } + + public void testDownloadAndExtractModule() throws Exception { + clearWorkDir(); + + File f = new File(getWorkDir(), "org-netbeans-api-annotations-common.xml"); + PublicPackagesInProjectizedXMLTest.extractResource(f, "org-netbeans-api-annotations-common.xml"); + + File nbm = generateNBM("org-netbeans-api-annotations-common.nbm", + "netbeans/config/Modules/org-netbeans-api-annotations-common.xml", + "netbeans/modules/org-netbeans-api-annotations-common.jar"); + + File target = new File(getWorkDir(), "target"); + target.mkdirs(); + + PublicPackagesInProjectizedXMLTest.execute( + "autoupdate.xml", "-verbose", "-Durl=" + f.toURI().toURL(), + "-Dincludes=org.netbeans.api.annotations.common", + "-Dtarget=" + target + ); + + File xml = new File( + new File(new File(target, "platform11"), "update_tracking"), + "org-netbeans-api-annotations-common.xml" + ); + assertTrue("xml file created", xml.exists()); + + File jar = new File( + new File(new File(target, "platform11"), "modules"), + "org-netbeans-api-annotations-common.jar" + ); + assertTrue("jar file created", jar.exists()); + } + + + public void testUpdateAlreadyInstalled() throws Exception { + clearWorkDir(); + + File f = new File(getWorkDir(), "org-netbeans-api-annotations-common.xml"); + PublicPackagesInProjectizedXMLTest.extractResource(f, "org-netbeans-api-annotations-common.xml"); + + File nbm = generateNBM("org-netbeans-api-annotations-common.nbm", + "netbeans/config/Modules/org-netbeans-api-annotations-common.xml", + "netbeans/modules/org-netbeans-api-annotations-common.jar"); + + File target = new File(getWorkDir(), "target"); + target.mkdirs(); + File m = new File( + new File(new File(target, "platform11"), "modules"), + "org-netbeans-api-annotations-common.jar" + ); + m.getParentFile().mkdirs(); + m.createNewFile(); + + File y = new File( + new File(new File(new File(target, "platform11"), "config"), "Modules"), + "org-netbeans-api-annotations-common.xml" + ); + y.getParentFile().mkdirs(); + File x = new File( + new File(new File(target, "platform11"), "update_tracking"), + "org-netbeans-api-annotations-common.xml" + ); + y.createNewFile(); + + x.getParentFile().mkdirs(); + String txtx = +"\n" + +"\n" + +" \n" + +" \n" + +" \n" + +" \n" + +"\n"; + + + FileOutputStream osx = new FileOutputStream(x); + osx.write(txtx.getBytes()); + osx.close(); + + + PublicPackagesInProjectizedXMLTest.execute( + "autoupdate.xml", "-verbose", "-Durl=" + f.toURI().toURL(), + "-Dincludes=org.netbeans.api.annotations.common", + "-Dtarget=" + target + ); + + File xml = new File( + new File(new File(new File(target, "platform11"), "config"), "Modules"), + "org-netbeans-api-annotations-common.xml" + ); + assertTrue("xml file created:\n" + PublicPackagesInProjectizedXMLTest.getStdOut(), xml.exists()); + + File jar = new File( + new File(new File(target, "platform11"), "modules"), + "org-netbeans-api-annotations-common.jar" + ); + assertTrue("jar file created", jar.exists()); + + if (PublicPackagesInProjectizedXMLTest.getStdOut().contains("Writing ")) { + fail("No writes, the module is already installed:\n" + PublicPackagesInProjectizedXMLTest.getStdOut()); + } + } + + public void testUpdateAlreadyInstalledAndOld() throws Exception { + clearWorkDir(); + + File f = new File(getWorkDir(), "org-netbeans-api-annotations-common.xml"); + PublicPackagesInProjectizedXMLTest.extractResource(f, "org-netbeans-api-annotations-common.xml"); + + File nbm = generateNBM("org-netbeans-api-annotations-common.nbm", + "netbeans/config/Modules/org-netbeans-api-annotations-common.xml", + "netbeans/modules/org-netbeans-api-annotations-common.jar"); + + File target = new File(getWorkDir(), "target"); + target.mkdirs(); + File m = new File( + new File(new File(target, "platform11"), "modules"), + "org-netbeans-api-annotations-common.jar" + ); + m.getParentFile().mkdirs(); + m.createNewFile(); + File e = new File( + new File(new File(new File(target, "platform11"), "modules"), "ext"), + "extra.jar" + ); + e.getParentFile().mkdirs(); + e.createNewFile(); + + File x = new File( + new File(new File(target, "platform11"), "update_tracking"), + "org-netbeans-api-annotations-common.xml" + ); + x.getParentFile().mkdirs(); + String txtx = +"\n" + +"\n" + +" \n" + +" \n" + +" \n" + +" \n" + +" \n" + +"\n"; + + + FileOutputStream osx = new FileOutputStream(x); + osx.write(txtx.getBytes()); + osx.close(); + + Thread.sleep(1000); + long last = x.lastModified(); + + PublicPackagesInProjectizedXMLTest.execute( + "autoupdate.xml", "-verbose", "-Durl=" + f.toURI().toURL(), + "-Dincludes=org.netbeans.api.annotations.common", + "-Dtarget=" + target + ); + + File xml = new File( + new File(new File(new File(target, "platform11"), "config"), "Modules"), + "org-netbeans-api-annotations-common.xml" + ); + assertTrue("xml file created", xml.exists()); + + File jar = new File( + new File(new File(target, "platform11"), "modules"), + "org-netbeans-api-annotations-common.jar" + ); + assertTrue("jar file created", jar.exists()); + + if (!PublicPackagesInProjectizedXMLTest.getStdOut().contains("Writing ")) { + fail("Writes should be there:\n" + PublicPackagesInProjectizedXMLTest.getStdOut()); + } + + if (last >= jar.lastModified()) { + fail("Newer timestamp for " + jar); + } + + assertFalse("extra file has been deleted", e.exists()); + } + + public File generateNBM (String name, String... files) throws IOException { + File f = new File (getWorkDir (), name); + + ZipOutputStream os = new ZipOutputStream (new FileOutputStream (f)); + os.putNextEntry (new ZipEntry ("Info/info.xml")); + os.write ("nothing".getBytes ()); + os.closeEntry (); + for (String n : files) { + os.putNextEntry(new ZipEntry(n)); + os.write("empty".getBytes()); + os.closeEntry(); + } + os.close(); + + return f; + } + +} --- a/nbbuild/test/unit/src/org/netbeans/nbbuild/PublicPackagesInProjectizedXMLTest.java Mon Nov 23 00:52:40 2009 +0100 +++ a/nbbuild/test/unit/src/org/netbeans/nbbuild/PublicPackagesInProjectizedXMLTest.java Mon Nov 23 06:41:44 2009 +0100 @@ -216,11 +216,15 @@ } final static File extractResource(String res) throws Exception { + File f = File.createTempFile("res", ".xml"); + f.deleteOnExit (); + extractResource(f, res); + return f; + } + static void extractResource(File f, String res) throws Exception { URL u = PublicPackagesInProjectizedXMLTest.class.getResource(res); assertNotNull ("Resource should be found " + res, u); - File f = File.createTempFile("res", ".xml"); - f.deleteOnExit (); FileOutputStream os = new FileOutputStream(f); InputStream is = u.openStream(); @@ -230,11 +234,9 @@ os.write (ch); } os.close (); - - return f; } - final static void execute (String res, String[] args) throws Exception { + final static void execute (String res, String... args) throws Exception { execute (extractResource (res), args); } --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 73039bf235f9 Mon Nov 23 06:41:44 2009 +0100 @@ -0,0 +1,16 @@ + + + + + + + + + + + + + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 73039bf235f9 Mon Nov 23 06:41:44 2009 +0100 @@ -0,0 +1,12 @@ + + + + + + + + + + + +