blob: 839bdcae08de4c063ad38766027cabf6d13e9026 [file] [log] [blame]
/*
* 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.nio.charset.StandardCharsets;
import java.util.*;
import java.util.logging.Level;
import java.util.zip.CRC32;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/** This class represents module updates tracking
*
* @author Ales Kemr
*/
public final class UpdateTracking {
/** Platform dependent file name separator */
public static final String FILE_SEPARATOR = System.getProperty ("file.separator");
public static final String PATH_SEPARATOR = System.getProperty ("path.separator");
public static final String ELEMENT_MODULES = "installed_modules"; // NOI18N
public static final String ELEMENT_MODULE = "module"; // NOI18N
public static final String ATTR_CODENAMEBASE = "codename"; // NOI18N
public static final String ELEMENT_VERSION = "module_version"; // NOI18N
public static final String ATTR_VERSION = "specification_version"; // NOI18N
public static final String ATTR_LAST = "last"; // NOI18N
public static final String ATTR_INSTALL = "install_time"; // NOI18N
public static final String ELEMENT_FILE = "file"; // NOI18N
public static final String ATTR_FILE_NAME = "name"; // NOI18N
public static final String ATTR_ORIGIN = "origin"; // NOI18N
public static final String UPDATER_ORIGIN = "updater"; // NOI18N
public static final String INSTALLER_ORIGIN = "installer"; // NOI18N
private static final String ATTR_CRC = "crc"; // NOI18N
private static final String NBM_ORIGIN = "nbm"; // NOI18N
public static final String ELEMENT_ADDITIONAL = "module_additional"; // NOI18N
public static final String ELEMENT_ADDITIONAL_MODULE = "module"; // NOI18N
public static final String ATTR_ADDITIONAL_NBM_NAME = "nbm_name"; // NOI18N
public static final String ATTR_ADDITIONAL_SOURCE = "source-display-name"; // NOI18N
public static final String EXTRA_CLUSTER_NAME = "extra";
private static final String LOCALE_DIR = FILE_SEPARATOR + "locale" + FILE_SEPARATOR; // NOI18N
public static final String TRACKING_FILE_NAME = "update_tracking"; // NOI18N
public static final String ADDITIONAL_INFO_FILE_NAME = "additional_information.xml"; // NOI18N
private static final String XML_EXT = ".xml"; // NOI18N
private static final String FORBID_AUTOUPDATE = ".noautoupdate"; // NOI18N
/** maps root of clusters to tracking files. (File -> UpdateTracking) */
private static final Map<File, UpdateTracking> trackings = new HashMap<File, UpdateTracking> ();
private static final Map<File, UpdateTracking.AdditionalInfo> infos = new HashMap<File, UpdateTracking.AdditionalInfo> ();
/** Mapping from files defining modules to appropriate modules objects.
*/
private LinkedHashMap<File, Module> installedModules = new LinkedHashMap<File, Module> ();
private final File directory;
private final File trackingFile;
private String origin = NBM_ORIGIN;
private final UpdatingContext context;
/** Private constructor.
*/
private UpdateTracking( File nbPath, UpdatingContext context ) {
assert nbPath != null : "Path cannot be null";
trackingFile = new File( nbPath + FILE_SEPARATOR + TRACKING_FILE_NAME);
directory = nbPath;
origin = UPDATER_ORIGIN;
this.context = context;
}
//
// Various factory and utility methods
//
/** Finds update tracking for given cluster root.
* @path root of a cluster
* @param createIfDoesNotExists should new tracking be created if it does not exists
* @return the tracking for that cluster
*/
static UpdateTracking getTracking (File path, boolean createIfDoesNotExists, UpdatingContext context) {
synchronized (trackings) {
UpdateTracking track = trackings.get (path);
if (track == null) {
File utFile = new File (path, TRACKING_FILE_NAME);
if (!createIfDoesNotExists && !utFile.isDirectory ()) {
// if the update_tracking directory is missing
// do not allow creation at all (only in userdir)
return null;
}
File noAU = new File(path, FORBID_AUTOUPDATE); // NOI18N
if (noAU.exists()) {
// ok, this prevents autoupdate from accessing this
// directory completely
return null;
}
track = new UpdateTracking (path, context);
trackings.put (path, track);
track.read ();
track.scanDir ();
}
return track;
}
}
/** Finds update tracking for given cluster root.
* @path root of a cluster
* @return the additional information for that cluster
*/
static UpdateTracking.AdditionalInfo getAdditionalInformation (File path, UpdatingContext context) {
synchronized (infos) {
UpdateTracking.AdditionalInfo additionalInfo = infos.get (path);
if (additionalInfo == null) {
getTracking (path, false, context);
File downloadDir = new File (path, ModuleUpdater.DOWNLOAD_DIR);
if (downloadDir.exists () && downloadDir.isDirectory ()) {
File addInfo = new File (downloadDir, ADDITIONAL_INFO_FILE_NAME);
if (addInfo.exists ()) {
additionalInfo = new UpdateTracking.AdditionalInfo (addInfo);
}
}
}
return additionalInfo;
}
}
/** Returns the platform installatiion directory.
* @return the File directory.
*/
public static File getPlatformDir () {
String platform = System.getProperty ("netbeans.home");
return platform == null ? null : new File (platform); // NOI18N
}
public static File getUserDir () {
// bugfix #50242: the property "netbeans.user" can return dir with non-normalized file e.g. duplicate //
// and path and value of this property wrongly differs
String user = System.getProperty ("netbeans.user");
File userDir = null;
if (user != null) {
// XXX cannot use FileUtil.normalizeFile from here
userDir = new File (user);
if (userDir.getPath ().startsWith ("\\\\")) {
// Could use URI.normalize but only on userDir.getPath().toUri() in JDK 7 (#4723726 breaks UNC for userDir.toURI())
try {
userDir = userDir.getCanonicalFile ();
} catch (IOException ex) {
// fallback when getCanonicalFile fails
userDir = userDir.getAbsoluteFile ();
}
} else {
userDir = new File (userDir.toURI ().normalize ()).getAbsoluteFile ();
}
}
return userDir;
}
/** Returns enumeration of Files that represent each possible install
* directory.
* @param includeUserDir whether to include also user dir
* @return List<File>
*/
public static List<File> clusters (boolean includeUserDir) {
List<File> files = new ArrayList<File> ();
if (includeUserDir) {
File ud = getUserDir ();
if (ud != null) {
// this prevents autoupdate from accessing this
// directory completely
File noAU = new File (ud, FORBID_AUTOUPDATE); // NOI18N
if (! noAU.exists ()) {
files.add (ud);
}
}
}
String dirs = System.getProperty("netbeans.dirs"); // NOI18N
if (dirs != null) {
Enumeration<Object> en = new StringTokenizer (dirs, File.pathSeparator);
while (en.hasMoreElements ()) {
File f = new File ((String)en.nextElement ());
// this prevents autoupdate from accessing this
// directory completely
File noAU = new File (f, FORBID_AUTOUPDATE); // NOI18N
if (! noAU.exists ()) {
files.add (f);
}
}
}
File id = getPlatformDir ();
if (id != null) {
// this prevents autoupdate from accessing this
// directory completely
File noAU = new File (id, FORBID_AUTOUPDATE); // NOI18N
if (! noAU.exists ()) {
files.add (id);
}
}
return java.util.Collections.unmodifiableList (files);
}
//
// Useful search methods
//
/** Returns true if module with given code base is installed here
* @param codeBase name of the module
* @return true or false
*/
public boolean isModuleInstalled (String codeBase) {
for (Module m: installedModules.values ()) {
String mm = m.codenamebase;
int indx = mm.indexOf ('/');
if (indx >= 0) {
mm = mm.substring (0, indx);
}
if (codeBase.equals (mm)) {
return true;
}
}
return false;
}
//
// Private impls
//
private static ErrorHandler DUMMY_ERROR_HANDLER = new ErrorHandler() {
@Override
public void warning(SAXParseException exception) throws SAXException {
}
@Override
public void error(SAXParseException exception) throws SAXException {
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
}
};
/** Scan through org.w3c.dom.Document document. */
private void read() {
/** org.w3c.dom.Document document */
org.w3c.dom.Document document;
File file;
InputStream is;
int avail = 0;
try {
file = trackingFile;
if ( ! file.isFile () ) {
return;
}
is = new FileInputStream( file );
avail = is.available();
InputSource xmlInputSource = new InputSource( is );
document = XMLUtil.parse(xmlInputSource, false, false, DUMMY_ERROR_HANDLER, XMLUtil.createAUResolver());
if (is != null) {
is.close();
}
}
catch ( org.xml.sax.SAXException e ) {
XMLUtil.LOG.log(Level.SEVERE, "Bad update_tracking: " + trackingFile + ", available bytes: " + avail, e); // NOI18N
return;
}
catch ( java.io.IOException e ) {
XMLUtil.LOG.log(Level.SEVERE, "Missing update_tracking: " + trackingFile + ", available bytes: " + avail, e); // NOI18N
return;
}
org.w3c.dom.Element element = document.getDocumentElement();
if ((element != null) && element.getTagName().equals(ELEMENT_MODULES)) {
scanElement_installed_modules(element);
}
}
/** Scan through org.w3c.dom.Element named installed_modules. */
void scanElement_installed_modules(org.w3c.dom.Element element) { // <installed_modules>
// element.getValue();
org.w3c.dom.NodeList nodes = element.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
org.w3c.dom.Node node = nodes.item(i);
if ( node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE ) {
org.w3c.dom.Element nodeElement = (org.w3c.dom.Element)node;
if (nodeElement.getTagName().equals(ELEMENT_MODULE)) {
if (true) {
throw new IllegalStateException ("What now!?");
}
// XXX - should put the module into installedModules but do not know the key
// modules.add( scanElement_module(nodeElement, fromuser) );
}
}
}
}
/** Scan through org.w3c.dom.Element named module. */
Module scanElement_module(org.w3c.dom.Element element) { // <module>
Module module = new Module ();
org.w3c.dom.NamedNodeMap attrs = element.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
org.w3c.dom.Attr attr = (org.w3c.dom.Attr)attrs.item(i);
if (attr.getName().startsWith(ATTR_CODENAMEBASE)) {
// <module codename="???"> or old version <module codenamebase="???">
module.setCodenamebase( attr.getValue() );
}
}
org.w3c.dom.NodeList nodes = element.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
org.w3c.dom.Node node = nodes.item(i);
if ( node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE ) {
org.w3c.dom.Element nodeElement = (org.w3c.dom.Element)node;
if (nodeElement.getTagName().equals(ELEMENT_VERSION)) {
scanElement_module_version(nodeElement, module);
}
}
}
return module;
}
/** Scan through org.w3c.dom.Element named module_version. */
private void scanElement_module_version(org.w3c.dom.Element element, Module module) { // <module_version>
Version version = new Version(module);
org.w3c.dom.NamedNodeMap attrs = element.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
org.w3c.dom.Attr attr = (org.w3c.dom.Attr)attrs.item(i);
if (attr.getName().equals(ATTR_VERSION)) { // <module_version specification_version="???">
version.setVersion( attr.getValue() );
}
if (attr.getName().equals(ATTR_ORIGIN)) { // <module_version origin="???">
version.setOrigin( attr.getValue() );
}
if (attr.getName().equals(ATTR_LAST)) { // <module_version last="???">
version.setLast( Boolean.valueOf(attr.getValue() ).booleanValue());
}
if (attr.getName().equals(ATTR_INSTALL)) { // <module_version install_time="???">
long li = 0;
try {
li = Long.parseLong( attr.getValue() );
} catch ( NumberFormatException nfe ) {
}
version.setInstall_time( li );
}
}
org.w3c.dom.NodeList nodes = element.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
org.w3c.dom.Node node = nodes.item(i);
if ( node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE ) {
org.w3c.dom.Element nodeElement = (org.w3c.dom.Element)node;
if (nodeElement.getTagName().equals(ELEMENT_FILE)) {
scanElement_file(nodeElement, version);
}
}
}
module.addOldVersion( version );
}
/** Scan through org.w3c.dom.Element named file. */
void scanElement_file(org.w3c.dom.Element element, Version version) { // <file>
ModuleFile file = new ModuleFile();
org.w3c.dom.NamedNodeMap attrs = element.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
org.w3c.dom.Attr attr = (org.w3c.dom.Attr)attrs.item(i);
if (attr.getName().equals(ATTR_FILE_NAME)) { // <file name="???">
file.setName( attr.getValue() );
}
if (attr.getName().equals(ATTR_CRC)) { // <file crc="???">
file.setCrc( attr.getValue() );
}
if (attr.getName().equals(ATTR_VERSION)) {
file.setLocaleversion( attr.getValue() );
}
}
version.addFile (file );
}
Module readModuleTracking (String codename, boolean create ) {
new File(directory, TRACKING_FILE_NAME).mkdirs();
File file = new File (
new File(directory, TRACKING_FILE_NAME),
getTrackingName( codename ) + XML_EXT
);
// fix for #34355
try {
if ( file.exists() && file.length()==0 ) {
file.delete();
}
} catch (Exception e) {
// ignore
}
if ( ! file.exists() ) {
if ( create ) {
return new Module( codename, file);
} else {
return null;
}
}
return readModuleFromFile( file, codename, create );
}
Version createVersion(String specversion) {
Version ver = new Version(null);
ver.setVersion( specversion );
return ver;
}
private Module readModuleFromFile( File file, String codename, boolean create ) {
/** org.w3c.dom.Document document */
org.w3c.dom.Document document;
InputStream is;
try {
is = new FileInputStream( file );
InputSource xmlInputSource = new InputSource( is );
document = XMLUtil.parse(xmlInputSource, false, false, DUMMY_ERROR_HANDLER, XMLUtil.createAUResolver());
if (is != null) {
is.close();
}
} catch ( org.xml.sax.SAXException e ) {
XMLUtil.LOG.log(Level.SEVERE, "Bad update_tracking", e); // NOI18N
return null;
}
catch ( java.io.IOException e ) {
if ( create ) {
return new Module (codename, file);
} else {
return null;
}
}
org.w3c.dom.Element element = document.getDocumentElement();
if ((element != null) && element.getTagName().equals(ELEMENT_MODULE)) {
Module m = scanElement_module (element);
m.setFile( file );
installedModules.put (file, m);
return m;
}
if ( create ) {
return new Module (codename, file);
} else {
return null;
}
}
private static String getTrackingName(String codename) {
String trackingName = codename;
int pos = trackingName.indexOf('/'); // NOI18N
if ( pos > -1 ) {
trackingName = trackingName.substring( 0, pos );
}
return trackingName.replace( '.', '-' ); // NOI18N
}
void deleteUnusedFiles() {
List<Module> newModules = new ArrayList<Module> (installedModules.values ());
for (Module mod: newModules) {
mod.deleteUnusedFiles();
}
scanDir ();
}
public static long getFileCRC(File file) throws IOException {
BufferedInputStream bsrc = null;
CRC32 crc = new CRC32();
try {
bsrc = new BufferedInputStream( new FileInputStream( file ) );
byte[] bytes = new byte[1024];
int i;
while( (i = bsrc.read(bytes)) != -1 ) {
crc.update(bytes, 0, i );
}
}
finally {
if ( bsrc != null ) {
bsrc.close();
}
}
return crc.getValue();
}
private void scanDir () {
File dir = new File (directory, TRACKING_FILE_NAME);
File[] files = dir.listFiles( new FileFilter() {
@Override
public boolean accept( File file ) {
if ( !file.isDirectory() && file.getName().toUpperCase().endsWith(".XML") ) {
return true;
} else {
return false;
}
}
} );
if (files == null) {
return;
}
for ( int i = 0; i < files.length; i++ ) {
if (!installedModules.containsKey (files[i])) {
readModuleFromFile( files[i], null, true );
}
}
}
@Override
public String toString() {
return "UpdateTracing[" + this.directory + ", origin: " + this.origin + "]";
}
class Module extends Object {
/** Holds value of property codenamebase. */
private String codenamebase;
/** Holds value of property versions. */
private List<Version> versions = new ArrayList<Version>();
private File file = null;
public Module() {
}
public Module(String codenamebase, File file) {
this.codenamebase = codenamebase;
this.file = file;
}
private Version lastVersion = null;
private Version newVersion = null;
private boolean osgi = false;
/** Getter for property codenamebase.
* @return Value of property codenamebase.
*/
String getCodenamebase() {
return codenamebase;
}
/** Setter for property codenamebase.
* @param codenamebase New value of property codenamebase.
*/
void setCodenamebase(String codenamebase) {
this.codenamebase = codenamebase;
}
void setOSGi(boolean isOSGi) {
this.osgi = isOSGi;
}
boolean isOSGi() {
return osgi;
}
/** Getter for property versions.
* @return Value of property versions.
*/
List<Version> getVersions() {
return versions;
}
/** Setter for property versions.
* @param versions New value of property versions.
*/
void setVersions(List<Version> versions) {
this.versions = versions;
}
private Version getNewOrLastVersion() {
if ( newVersion != null ) {
return newVersion;
} else {
return lastVersion;
}
}
boolean hasNewVersion() {
return newVersion != null;
}
void setFile(File file) {
this.file = file;
}
public Version addNewVersion( String spec_version, String origin ) {
if ( lastVersion != null ) {
lastVersion.setLast ( false );
}
Version version = new Version(this);
newVersion = version;
version.setVersion( spec_version );
version.setOrigin( origin );
version.setLast( true );
version.setInstall_time( System.currentTimeMillis() );
versions.add( version );
return version;
}
void addOldVersion( Version version ) {
if ( version.isLast() ) {
lastVersion = version;
}
versions.add( version );
}
void addL10NVersion( Version l_version ) {
if ( lastVersion != null ) {
lastVersion.addL10NFiles( l_version.getFiles() );
} else {
l_version.setOrigin( origin );
l_version.setLast( true );
l_version.setInstall_time( System.currentTimeMillis() );
versions.add( l_version );
}
}
void writeConfigModuleXMLIfMissing () {
File configDir = new File (new File (directory, ModuleDeactivator.CONFIG), ModuleDeactivator.MODULES); // NOI18N
String candidate = null;
String oldCandidate = null;
String newCandidate = null;
String name = codenamebase;
int indx = name.indexOf ('/');
if (indx > 0) {
name = name.substring (0, indx);
}
// check module name from config file
String replaced = name.replace ('.', '-'); // NOI18N
String searchFor;
if (replaced.indexOf (ModuleDeactivator.MODULES) > 0) { // NOI18N
// standard module
searchFor = replaced + ".jar"; // NOI18N
} else if(osgi) {
searchFor = replaced + ".jar"; // NOI18N
} else {
// core module
searchFor = replaced.substring (replaced.lastIndexOf ('-') > 0 ? replaced.lastIndexOf ('-') + 1 : 0) + ".jar"; // NOI18N
}
String dash = name.replace ('.', '-');
{
boolean needInfoInUserDir = false;
boolean afterNBMsCluster = false;
for (File c : clusters(true)) {
File hidden = new File(new File(new File(c, "config"), "Modules"), dash + ".xml_hidden");
if (hidden.exists()) {
hidden.delete();
XMLUtil.LOG.info("File " + hidden + " deleted.");
}
if (directory.equals(c)) {
afterNBMsCluster = true;
continue;
}
if (afterNBMsCluster) {
continue;
}
File customConfigs = new File(new File(new File(c, "config"), "Modules"), dash + ".xml");
if (customConfigs.exists()) {
needInfoInUserDir = lastVersion == null;
}
}
if (needInfoInUserDir) {
// there is a definition for the same XML file in some cluster
// already and
File userConfig = new File(new File(new File(getUserDir(), "config"), "Modules"), dash + ".xml");
writeModulesConfig(userConfig, searchFor, candidate, newCandidate, oldCandidate, name);
return;
}
}
File config = new File (configDir, dash + ".xml"); // NOI18N
if (config.isFile ()) {
// already written
return;
}
writeModulesConfig(config, searchFor, candidate, newCandidate, oldCandidate, name);
}
void write( ) {
Document document = XMLUtil.createDocument(ELEMENT_MODULE);
Element e_module = document.getDocumentElement();
Element e_version;
Element e_file;
e_module.setAttribute(ATTR_CODENAMEBASE, getCodenamebase());
for (Version ver : getVersions()) {
e_version = document.createElement(ELEMENT_VERSION);
if (ver.getVersion() != null) {
e_version.setAttribute(ATTR_VERSION, ver.getVersion());
}
e_version.setAttribute(ATTR_ORIGIN, ver.getOrigin());
e_version.setAttribute(ATTR_LAST, Boolean.valueOf(ver.isLast()).toString());
e_version.setAttribute(ATTR_INSTALL, Long.toString(ver.getInstall_time()));
e_module.appendChild(e_version);
for (ModuleFile moduleFile : ver.getFiles()) {
e_file = document.createElement(ELEMENT_FILE);
e_file.setAttribute(ATTR_FILE_NAME, moduleFile.getName());
e_file.setAttribute(ATTR_CRC, moduleFile.getCrc());
if (moduleFile.getLocaleversion() != null) {
e_file.setAttribute(ATTR_VERSION, moduleFile.getLocaleversion());
}
e_version.appendChild(e_file);
}
}
document.getDocumentElement().normalize();
OutputStream os = null;
try {
os = context.createOS(file);
} catch (Exception e) {
XMLUtil.LOG.log(Level.WARNING, "Cannot read " + file, e);
//#154904
if (!file.delete()) {
XMLUtil.LOG.log(Level.SEVERE, null, new IOException("Corresponding update would not be installed since it is not possible to modify or delete update tracking file " + file));
} else {
XMLUtil.LOG.log(Level.SEVERE, null, new IOException("Update tracking file was deleted since permissions does not allow to modify it: " + file));
try {
os = context.createOS(file);
} catch (Exception ex) {
XMLUtil.LOG.log(Level.WARNING, "Cannot read", ex);
}
}
}
if (os != null) {
try {
XMLUtil.write(document, os);
XMLUtil.LOG.info("File " + file + " modified.");
} catch (IOException e) {
XMLUtil.LOG.log(Level.WARNING, "Cannot write " + file, e);
} finally {
try {
os.close();
} catch (IOException e) {
XMLUtil.LOG.log(Level.WARNING, "Cannot close " + file, e);
}
}
}
}
void deleteUnusedFiles() {
if ( lastVersion == null || newVersion == null ) {
return;
}
for (ModuleFile modFile : lastVersion.getFiles()) {
if ( ! newVersion.containsFile( modFile ) && modFile.getName().indexOf( LOCALE_DIR ) == -1 ) {
safeDelete( modFile );
}
}
}
private void safeDelete(ModuleFile modFile) {
// test file existence
File f = new File( file.getParentFile().getParent() + FILE_SEPARATOR + modFile.getName() );
if ( f.exists() ) {
// test crc
try {
if (! Long.toString(getFileCRC(f)).equals(modFile.getCrc())) {
return;
}
} catch ( IOException ioe ) {
return;
}
// test if file is referenced from other module
scanDir();
boolean found = false;
Iterator<Module> it = installedModules.values ().iterator();
while ( !found && it.hasNext() ) {
Module mod = it.next();
if ( ! mod.equals( this ) ) {
Version v = mod.getNewOrLastVersion();
if ( v != null && v.containsFile( modFile ) ) {
found = true;
}
}
}
if ( ! found ) {
XMLUtil.LOG.info("Deleting file: " + f);
boolean deleted = f.delete();
XMLUtil.LOG.info(".... " + f + " was deleted? " + deleted);
}
}
}
String getL10NSpecificationVersion(String jarpath) {
String localever;
Collections.<Version>sort( versions );
for (Version ver: versions) {
localever = ver.getLocaleVersion( jarpath );
if ( localever != null ) {
return localever;
}
}
return null;
}
private void writeModulesConfig(File config, String searchFor, String candidate, String newCandidate, String oldCandidate, String name) {
config.getParentFile().mkdirs();
Boolean isAutoload = null;
Boolean isEager = null;
Iterator<ModuleFile> it = newVersion.getFiles().iterator();
boolean needToWrite = false;
while (it.hasNext()) {
ModuleFile f = it.next();
String n = f.getName();
String parentDir;
{
File p = new File(f.getName()).getParentFile();
parentDir = p != null ? p.getName() : "";
}
needToWrite = needToWrite || n.indexOf(ModuleDeactivator.MODULES) >= 0 || osgi;
if (n.endsWith(".jar") && ! parentDir.equals("ext")) { // NOI18N
// ok, module candidate
candidate = f.getName();
// the correct candidate looks as e.g. org.netbeans.modules.mymodule
// if no jar looks as codenamebase then the jar file will be found as module's jar
if (searchFor.endsWith(candidate) || candidate.endsWith(searchFor)) {
newCandidate = candidate;
oldCandidate = null;
// autoload and eager will set by module's jar
if ("autoload".equals(parentDir)) {
// NOI18N
isAutoload = Boolean.TRUE;
} else {
isAutoload = Boolean.FALSE;
}
if ("eager".equals(parentDir)) {
// NOI18N
isEager = Boolean.TRUE;
} else {
isEager = Boolean.FALSE;
}
} else {
if (newCandidate == null) {
oldCandidate = (oldCandidate == null ? "" : oldCandidate + ", ") + candidate; // NOI18N
}
}
}
// if no correct name found => set autoload/eager by the last jar file
if (isAutoload == null && "autoload".equals(parentDir)) {
// NOI18N
isAutoload = Boolean.TRUE;
}
if (isEager == null && "eager".equals(parentDir)) {
// NOI18N
isEager = Boolean.TRUE;
}
}
if (!needToWrite) {
XMLUtil.LOG.log(Level.WARNING, "No config file written for module {0}. No jar file present in \"modules\" directory.", codenamebase);
return;
}
assert newCandidate != null || oldCandidate != null : "No jar file present!";
if (newCandidate == null) {
// PENDING: should check but some NBM assumed wrong behaviour before bugfix 53316
assert oldCandidate.equals(candidate) : "More files look as module: " + oldCandidate;
// only temporary
if (!oldCandidate.equals(candidate)) {
XMLUtil.LOG.log(Level.WARNING, "More files look as module: {0}", oldCandidate);
oldCandidate = candidate;
}
// end of temp
}
String moduleName = newCandidate == null ? oldCandidate : newCandidate;
boolean autoload = isAutoload != null && isAutoload.booleanValue();
boolean eager = isEager != null && isEager.booleanValue();
boolean isEnabled = !autoload && !eager;
String spec = newVersion.getVersion();
OutputStream os;
try {
os = context.createOS(config);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
// Please make sure formatting matches what the IDE actually spits
// out; it could matter.
pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
pw.println("<!DOCTYPE module PUBLIC \"-//NetBeans//DTD Module Status 1.0//EN\"");
pw.println(" \"http://www.netbeans.org/dtds/module-status-1_0.dtd\">");
pw.println("<module name=\"" + name + "\">");
pw.println(" <param name=\"autoload\">" + autoload + "</param>");
pw.println(" <param name=\"eager\">" + eager + "</param>");
if (isEnabled) {
pw.println(" <param name=\"enabled\">" + isEnabled + "</param>");
}
pw.println(" <param name=\"jar\">" + moduleName + "</param>");
pw.println(" <param name=\"reloadable\">false</param>");
pw.println(" <param name=\"specversion\">" + spec + "</param>");
pw.println("</module>");
pw.flush();
pw.close();
XMLUtil.LOG.info("New config was written in " + config);
} catch (IOException ex) {
XMLUtil.LOG.log(Level.INFO, null, ex);
}
}
@Override
public String toString() {
return "UpdateTracing.Module[" + this.codenamebase + "(" + this.file + "), OSGI? " + this.osgi + "]";
}
}
public class Version extends Object implements Comparable<Version> {
private final Module module;
Version(Module m) {
this.module = m;
}
/** Holds value of property version. */
private String version;
/** Holds value of property origin. */
private String origin;
/** Holds value of property last. */
private boolean last;
/** Holds value of property install_time. */
private long install_time = 0;
/** Holds value of property files. */
private List<ModuleFile> files = new ArrayList<ModuleFile>();
/** Getter for property version.
* @return Value of property version.
*/
String getVersion() {
return version;
}
/** Setter for property version.
* @param version New value of property version.
*/
void setVersion(String version) {
this.version = version;
}
/** Getter for property origin.
* @return Value of property origin.
*/
String getOrigin() {
return origin;
}
/** Setter for property origin.
* @param origin New value of property origin.
*/
void setOrigin(String origin) {
this.origin = origin;
}
/** Getter for property last.
* @return Value of property last.
*/
boolean isLast() {
return last;
}
/** Setter for property last.
* @param last New value of property last.
*/
void setLast(boolean last) {
this.last = last;
}
/** Getter for property install_time.
* @return Value of property install_time.
*/
long getInstall_time() {
return install_time;
}
/** Setter for property install_time.
* @param install_time New value of property install_time.
*/
void setInstall_time(long install_time) {
this.install_time = install_time;
}
/** Getter for property files.
* @return Value of property files.
*/
List<ModuleFile> getFiles() {
return files;
}
/** Setter for property files.
* @param files New value of property files.
*/
void addL10NFiles(List<ModuleFile> l10nfiles) {
for (ModuleFile lf : l10nfiles) {
String lname = lf.getName();
for ( int i = files.size() - 1; i >=0; i-- ) {
ModuleFile f = files.get( i );
if ( f.getName().equals( lname ) ) {
files.remove( i );
}
}
}
files.addAll( l10nfiles );
}
void addFile( ModuleFile file ) {
files.add( file );
}
public void addFileWithCrc( String filename, String crc ) {
ModuleFile file = new ModuleFile();
file.setName( filename );
file.setCrc( crc );
files.add( file );
}
public void addL10NFileWithCrc( String filename, String crc, String specver ) {
ModuleFile file = new ModuleFile();
file.setName( filename );
file.setCrc( crc );
file.setLocaleversion( specver );
files.add( file );
}
boolean containsFile( ModuleFile file ) {
for (ModuleFile f : files) {
if ( f.getName().equals( file.getName() ) ) {
return true;
}
}
return false;
}
ModuleFile findFile(String filename) {
for (ModuleFile f : files) {
if ( f.getName().equals( filename ) ) {
return f;
}
}
return null;
}
String getLocaleVersion(String filename) {
String locver = null;
ModuleFile f = findFile( filename );
if ( f != null ) {
locver = f.getLocaleversion();
if ( locver == null ) {
locver = version;
}
}
return locver;
}
@Override
public int compareTo (Version oth) {
if ( install_time < oth.getInstall_time() ) {
return 1;
}
else if ( install_time > oth.getInstall_time() ) {
return -1;
} else {
return 0;
}
}
@Override
public String toString() {
return "UpdateTracing.Version[" + this.module + "/" + this.version + ", last? " + this.isLast() + "]";
}
}
class ModuleFile extends Object {
/** Holds value of property name. */
private String name;
/** Holds value of property crc. */
private String crc;
/** Holds value of property localeversion. */
private String localeversion = null;
/** Getter for property name.
* @return Value of property name.
*/
String getName() {
return name;
}
/** Setter for property name.
* @param name New value of property name.
*/
void setName(String name) {
this.name = name;
}
/** Getter for property crc.
* @return Value of property crc.
*/
String getCrc() {
return crc;
}
/** Setter for property crc.
* @param crc New value of property crc.
*/
void setCrc(String crc) {
this.crc = crc;
}
/** Getter for property localeversion.
* @return Value of property localeversion.
*
*/
public String getLocaleversion() {
return this.localeversion;
}
/** Setter for property localeversion.
* @param localeversion New value of property localeversion.
*
*/
public void setLocaleversion(String localeversion) {
this.localeversion = localeversion;
}
@Override
public String toString() {
return "UpdateTracing.ModuleFile[" + this.name + "(" + this.crc + ")" + "]";
}
}
public static class AdditionalInfo extends Object {
private Map<String, String> sources;
private AdditionalInfo (File additionalInfoFile) {
sources = readAdditionalInfoFile (additionalInfoFile);
}
public String getSource (String nbmFileName) {
return sources != null ? sources.get (nbmFileName) : null;
}
private Map<String, String> readAdditionalInfoFile (File f) {
if (f == null || ! f.exists ()) {
throw new IllegalArgumentException ("AdditionalInfo file " + f + " must exists.");
}
Map<String, String> res = null;
/** org.w3c.dom.Document document */
org.w3c.dom.Document document;
InputStream is = null;
try {
is = new FileInputStream (f);
document = XMLUtil.parse (new InputSource (is), false, false, null, null);
} catch (org.xml.sax.SAXException e) {
XMLUtil.LOG.log (Level.WARNING,"Bad " + UpdateTracking.ADDITIONAL_INFO_FILE_NAME + f, e); // NOI18N
return res;
} catch (java.io.IOException e) {
XMLUtil.LOG.log (Level.WARNING,"Missing " + UpdateTracking.ADDITIONAL_INFO_FILE_NAME + f, e); // NOI18N
return res;
} finally {
if (is != null) {
try {
is.close ();
} catch (IOException ioe) {
XMLUtil.LOG.log (Level.INFO, "Cannot close stream for file " + f, ioe); // NOI18N
return res;
}
}
}
org.w3c.dom.Element element = document.getDocumentElement ();
if ((element != null) && element.getTagName ().equals (ELEMENT_ADDITIONAL)) {
res = scanModuleAdditional (element);
}
return res;
}
private Map<String, String> scanModuleAdditional (org.w3c.dom.Element element) {
Map<String, String> res = new HashMap<String, String> ();
org.w3c.dom.NodeList nodes = element.getChildNodes ();
for (int i = 0; i < nodes.getLength (); i++) {
org.w3c.dom.Node node = nodes.item (i);
if (node.getNodeType () == org.w3c.dom.Node.ELEMENT_NODE) {
org.w3c.dom.Element nodeElement = (org.w3c.dom.Element) node;
if (nodeElement.getTagName ().equals (ELEMENT_ADDITIONAL_MODULE)) {
String fileSpec = nodeElement.getAttribute (ATTR_ADDITIONAL_NBM_NAME);
String source = nodeElement.getAttribute (ATTR_ADDITIONAL_SOURCE);
res.put (fileSpec, source);
}
}
}
return res;
}
}
}