diff --git a/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java b/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java index 8ebd499a96..0581761f13 100644 --- a/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java +++ b/org.adempiere.pipo/src/org/adempiere/pipo/srv/PipoDictionaryService.java @@ -1,3 +1,14 @@ +/****************************************************************************** + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ package org.adempiere.pipo.srv; import java.io.File; @@ -27,9 +38,26 @@ public class PipoDictionaryService implements IDictionaryService { try { PackIn packIn = new PackIn(); packIn.setPackageName(context.getBundle().getSymbolicName()); - packIn.setPackageVersion((String) context.getBundle().getHeaders().get("Bundle-Version")); + + //get package version from file name suffix or bundle header + String packageVersion = null; + String fileName = packageFile.getName(); + int versionSeparatorPos = fileName.lastIndexOf("_"); + if (versionSeparatorPos > 0) { + int dotPos = fileName.lastIndexOf("."); + if (dotPos > 0 && dotPos > versionSeparatorPos) { + String version = fileName.substring(versionSeparatorPos+1, dotPos); + if (version.split("[.]").length == 3) { + packageVersion = version; + } + } + } + //no version string from file name suffix, get it from bundle header + if (packageVersion == null) + packageVersion = (String) context.getBundle().getHeaders().get("Bundle-Version"); + + packIn.setPackageVersion(packageVersion); packIn.setUpdateDictionary(false); -// packIn.setPackageDirectory(getPackageDir()); X_AD_Package_Imp_Proc adPackageImp = new X_AD_Package_Imp_Proc(Env.getCtx(), 0, trxName); diff --git a/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/Incremental2PackActivator.java b/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/Incremental2PackActivator.java new file mode 100644 index 0000000000..6567865070 --- /dev/null +++ b/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/Incremental2PackActivator.java @@ -0,0 +1,307 @@ +/****************************************************************************** + * Copyright (C) 2013 Heng Sin Low * + * Copyright (C) 2013 Trek Global * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ +package org.adempiere.plugin.utils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; +import java.util.logging.Level; + +import org.adempiere.base.IDictionaryService; +import org.adempiere.util.ServerContext; +import org.compiere.Adempiere; +import org.compiere.model.Query; +import org.compiere.model.ServerStateChangeEvent; +import org.compiere.model.ServerStateChangeListener; +import org.compiere.model.X_AD_Package_Imp; +import org.compiere.util.CLogger; +import org.compiere.util.Env; +import org.compiere.util.Trx; +import org.compiere.util.Util; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; + +/** + * + * @author hengsin + * + */ +public class Incremental2PackActivator implements BundleActivator, ServiceTrackerCustomizer { + + protected final static CLogger logger = CLogger.getCLogger(Incremental2PackActivator.class.getName()); + private BundleContext context; + private ServiceTracker serviceTracker; + private IDictionaryService service; + + @Override + public void start(BundleContext context) throws Exception { + this.context = context; + if (logger.isLoggable(Level.INFO)) logger.info(getName() + " " + getVersion() + " starting..."); + serviceTracker = new ServiceTracker(context, IDictionaryService.class.getName(), this); + serviceTracker.open(); + start(); + if (logger.isLoggable(Level.INFO)) logger.info(getName() + " " + getVersion() + " ready."); + } + + public String getName() { + return context.getBundle().getSymbolicName(); + } + + public String getVersion() { + String version = (String) context.getBundle().getHeaders().get("Bundle-Version"); + // e.g. 1.0.0.qualifier, check only the "1.0.0" part + String[] components = version.split("[.]"); + StringBuilder versionBuilder = new StringBuilder(components[0]); + if (components.length >= 3) { + versionBuilder.append(".").append(components[1]).append(".").append(components[2]); + } else if (components.length == 2) { + versionBuilder.append(".").append(components[1]).append(".0"); + } else { + versionBuilder.append(".0.0"); + } + return versionBuilder.toString(); + } + + public String getDescription() { + return getName(); + } + + private void installPackage() { + String trxName = Trx.createTrxName(); + try { + // e.g. 1.0.0.qualifier, check only the "1.0.0" part + String bundleVersionPart = getVersion(); + String installedVersionPart = null; + String where = "Name=?"; + Query q = new Query(Env.getCtx(), X_AD_Package_Imp.Table_Name, + where.toString(), null); + q.setParameters(new Object[] { getName() }); + List pkgs = q.list(); + if (pkgs != null && !pkgs.isEmpty()) { + for(X_AD_Package_Imp pkg : pkgs) { + String packageVersionPart = pkg.getPK_Version(); + String[] part = packageVersionPart.split("[.]"); + if (installedVersionPart == null) { + if (part.length > 3) { + installedVersionPart = part[0]+"."+part[1]+"."+part[2]; + } else { + installedVersionPart = packageVersionPart; + } + } else { + Version installedVersion = new Version(installedVersionPart); + if (part.length > 3) { + packageVersionPart = part[0]+"."+part[1]+"."+part[2]; + } + Version packageVersion = new Version(packageVersionPart); + if (packageVersion.compareTo(installedVersion) > 0) { + installedVersionPart = packageVersionPart; + } + } + } + } + packIn(trxName, installedVersionPart, bundleVersionPart); + afterPackIn(); + Trx.get(trxName, false).commit(); + } finally { + if (Trx.get(trxName, false) != null) { + Trx.get(trxName, false).close(); + } + } + } + + private static class TwoPackEntry { + private URL url; + private String version; + private TwoPackEntry(URL url, String version) { + this.url=url; + this.version = version; + } + } + + protected void packIn(String trxName, String installedVersionPart, String bundleVersionPart) { + List list = new ArrayList(); + + //2Pack_1.0.0.zip, 2Pack_1.0.1.zip, etc + Enumeration urls = context.getBundle().findEntries("/META-INF", "2Pack_*.zip", false); + Version bundleVersion = new Version(bundleVersionPart); + if (!Util.isEmpty(installedVersionPart)) { + Version installedVersion = new Version(installedVersionPart); + while(urls.hasMoreElements()) { + URL u = urls.nextElement(); + String version = extractVersionString(u); + Version packageVersion = new Version(version); + if (packageVersion.compareTo(bundleVersion) <= 0 && packageVersion.compareTo(installedVersion) > 0) + list.add(new TwoPackEntry(u, version)); + } + } else { + while(urls.hasMoreElements()) { + URL u = urls.nextElement(); + String version = extractVersionString(u); + Version packageVersion = new Version(version); + if (packageVersion.compareTo(bundleVersion) <= 0) + list.add(new TwoPackEntry(u, version)); + } + } + + Collections.sort(list, new Comparator() { + @Override + public int compare(TwoPackEntry o1, TwoPackEntry o2) { + return new Version(o1.version).compareTo(new Version(o2.version)); + } + }); + + for(TwoPackEntry entry : list) { + packIn(trxName, entry.url); + } + } + + private String extractVersionString(URL u) { + String p = u.getPath(); + int upos=p.lastIndexOf("_"); + int dpos=p.lastIndexOf("."); + String v = p.substring(upos+1, dpos); + return v; + } + + protected void packIn(String trxName, URL packout) { + if (packout != null && service != null) { + String path = packout.getPath(); + String suffix = path.substring(path.lastIndexOf("_")); + System.out.println("Installing " + getName() + " " + path + " ..."); + FileOutputStream zipstream = null; + try { + // copy the resource to a temporary file to process it with 2pack + InputStream stream = packout.openStream(); + File zipfile = File.createTempFile(getName(), suffix); + zipstream = new FileOutputStream(zipfile); + byte[] buffer = new byte[1024]; + int read; + while((read = stream.read(buffer)) != -1){ + zipstream.write(buffer, 0, read); + } + // call 2pack + service.merge(context, zipfile); + } catch (Throwable e) { + logger.log(Level.SEVERE, "Pack in failed.", e); + } finally{ + if (zipstream != null) { + try { + zipstream.close(); + } catch (Exception e2) {} + } + } + System.out.println(getName() + " " + packout.getPath() + " installed"); + } + } + + protected BundleContext getContext() { + return context; + } + + protected void setContext(BundleContext context) { + this.context = context; + } + + @Override + public void stop(BundleContext context) throws Exception { + stop(); + serviceTracker.close(); + this.context = null; + if (logger.isLoggable(Level.INFO)) logger.info(context.getBundle().getSymbolicName() + " " + + context.getBundle().getHeaders().get("Bundle-Version") + + " stopped."); + } + + protected void afterPackIn() { + }; + + /** + * call when bundle have been started ( after this.context have been set ) + */ + protected void start() { + }; + + /** + * call when bundle is stop ( before this.context is set to null ) + */ + protected void stop() { + } + + @Override + public IDictionaryService addingService( + ServiceReference reference) { + service = context.getService(reference); + if (Adempiere.getThreadPoolExecutor() != null) { + Adempiere.getThreadPoolExecutor().execute(new Runnable() { + @Override + public void run() { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(Incremental2PackActivator.class.getClassLoader()); + setupPackInContext(); + installPackage(); + } finally { + ServerContext.dispose(); + service = null; + Thread.currentThread().setContextClassLoader(cl); + } + } + }); + } else { + Adempiere.addServerStateChangeListener(new ServerStateChangeListener() { + @Override + public void stateChange(ServerStateChangeEvent event) { + if (event.getEventType() == ServerStateChangeEvent.SERVER_START && service != null) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(Incremental2PackActivator.class.getClassLoader()); + setupPackInContext(); + installPackage(); + } finally { + ServerContext.dispose(); + service = null; + Thread.currentThread().setContextClassLoader(cl); + } + } + } + }); + } + return null; + } + + @Override + public void modifiedService(ServiceReference reference, + IDictionaryService service) { + } + + @Override + public void removedService(ServiceReference reference, + IDictionaryService service) { + } + + protected void setupPackInContext() { + Properties serverContext = new Properties(); + ServerContext.setCurrentInstance(serverContext); + }; +} diff --git a/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/Version.java b/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/Version.java new file mode 100644 index 0000000000..d511a1f786 --- /dev/null +++ b/org.adempiere.plugin.utils/src/org/adempiere/plugin/utils/Version.java @@ -0,0 +1,66 @@ +/****************************************************************************** + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ +package org.adempiere.plugin.utils; + +/** + * + * @author hengsin + * + */ +public class Version implements Comparable { + + private String version; + + public final String get() { + return this.version; + } + + public Version(String version) { + if(version == null) + throw new IllegalArgumentException("Version can not be null"); + if(!version.matches("[0-9]+(\\.[0-9]+)*")) + throw new IllegalArgumentException("Invalid version format"); + this.version = version; + } + + @Override + public int compareTo(Version that) { + if(that == null) + return 1; + String[] thisParts = this.get().split("\\."); + String[] thatParts = that.get().split("\\."); + int length = Math.max(thisParts.length, thatParts.length); + for(int i = 0; i < length; i++) { + int thisPart = i < thisParts.length ? + Integer.parseInt(thisParts[i]) : 0; + int thatPart = i < thatParts.length ? + Integer.parseInt(thatParts[i]) : 0; + if(thisPart < thatPart) + return -1; + if(thisPart > thatPart) + return 1; + } + return 0; + } + + @Override + public boolean equals(Object that) { + if(this == that) + return true; + if(that == null) + return false; + if(this.getClass() != that.getClass()) + return false; + return this.compareTo((Version) that) == 0; + } + +} \ No newline at end of file