com.zimbra.cs.account.ldap.upgrade.BUG_27075.java Source code

Java tutorial

Introduction

Here is the source code for com.zimbra.cs.account.ldap.upgrade.BUG_27075.java

Source

/*
 * ***** BEGIN LICENSE BLOCK *****
 * Zimbra Collaboration Suite Server
 * Copyright (C) 2011, 2013, 2014, 2016 Synacor, Inc.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software Foundation,
 * version 2 of the License.
 *
 * 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, see <https://www.gnu.org/licenses/>.
 * ***** END LICENSE BLOCK *****
 */
package com.zimbra.cs.account.ldap.upgrade;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;

import com.zimbra.common.service.ServiceException;
import com.zimbra.common.util.Version;
import com.zimbra.cs.account.AttributeCardinality;
import com.zimbra.cs.account.AttributeClass;
import com.zimbra.cs.account.AttributeInfo;
import com.zimbra.cs.account.AttributeManager;
import com.zimbra.cs.account.Config;
import com.zimbra.cs.account.Cos;
import com.zimbra.cs.account.Entry;
import com.zimbra.cs.account.Provisioning;
import com.zimbra.cs.ldap.LdapClient;
import com.zimbra.cs.ldap.LdapServerType;
import com.zimbra.cs.ldap.LdapUsage;
import com.zimbra.cs.ldap.ZLdapContext;
import com.zimbra.cs.util.BuildInfo;

public class BUG_27075 extends UpgradeOp {

    private Version mSince;

    @Override
    boolean parseCommandLine(CommandLine cl) {
        String[] args = cl.getArgs();
        if (args == null || args.length != 1) {
            LdapUpgrade.usage(null, this, "missing required argument: since");
            return false;
        }

        try {
            mSince = new Version(args[0]);
        } catch (ServiceException e) {
            LdapUpgrade.usage(null, this, "invalid version: " + args[0]);
            return false;
        }
        return true;
    }

    @Override
    void usage(HelpFormatter helpFormatter) {
        printer.println();
        printer.println("args for bug " + bug + ":");
        printer.println("    {since}  (e.g. 5.0.12)");
        printer.println();
    }

    @Override
    void doUpgrade() throws ServiceException {
        ZLdapContext zlc = LdapClient.getContext(LdapServerType.MASTER, LdapUsage.UPGRADE);
        try {
            doGlobalConfig(zlc);
            doAllCos(zlc);
        } finally {
            LdapClient.closeContext(zlc);
        }
    }

    /*
     * return values as: value1, value2, value3, ...
     */
    private String formatMultiValue(Collection<String> values) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (String v : values) {
            if (!first)
                sb.append(", ");
            sb.append(v);
            first = false;
        }
        return sb.toString();
    }

    private boolean needsUpgrade(AttributeManager am, String attr) throws ServiceException {
        String since = mSince.toString();

        if (!am.beforeVersion(attr, since) && !am.isFuture(attr))
            return true;

        //
        // bug 38426
        //
        // 5.0.17_GA is after 6.0.0_BETA2
        //
        // We need to fixup:
        //    (1) 6.0.0_BETA1 -> 6.0.0_* upgrades
        //            - if the 6.0.0_BETA1 was freshly installed before a 5.0.17 attr was added
        //            - if the 6.0.0_BETA1 was upgraded from a 5.0.X before a 5.0.17 attr was added
        //    and
        //    (2) 6.0.0_BETA2 -> 6.0.0_* upgrades
        //            - if the 6.0.0_BETA2 was freshly installed before a 5.0.17 attr was added
        //            - this is fixed in 6.0.0_BETA3, if the system was upgraded to
        //              6.0.0_BETA2 from a 6.0.0_BETA1 described above, attrs added in 5.0.17
        //              that were missing in the 6.0.0_BETA1 are still missing in the 6.0.0_BETA2
        //
        if (am.addedIn(attr, "5.0.17")) {
            boolean fromATroubledInstall = (mSince.compare("6.0.0_BETA1") == 0
                    || mSince.compare("6.0.0_BETA2") == 0);
            if (fromATroubledInstall) {
                return true;
            }
        }

        /*
         * bug 56667
         *
         * zimbraFreebusyExchangeServerType was added in 6.0.11, *after* 7.0.0 and before 7.0.1
         *
         * [from] 7.0.0 -> [to] higher version
         * upgrades will miss it.
         *
         * [from] 7.0.1 and above -> [to] higher version
         * upgrades will be fine because:
         *   - if the [from] is a fresh install, it will have the default value set.
         *   - if the [from] is from an upgrade:
         *         - if from 7.0.0, fixed by this fix.
         *         - if from below 7.0.0:
         *               - if 6.0.11 and above, no problem
         *               - if below 6.0.11, taken care by the regular logic, no problem.
         */
        if (Provisioning.A_zimbraFreebusyExchangeServerType.equalsIgnoreCase(attr)) {
            boolean fromATroubledInstall = (mSince.compare("7.0.0") == 0);
            if (fromATroubledInstall) {
                return true;
            }
        }

        /*
         * bug 58084
         *
         * zimbraMailEmptyFolderBatchThreshold was added in 6.0.13, *after* 7.1.0 and before 7.1.1
         *
         */
        if (Provisioning.A_zimbraMailEmptyFolderBatchThreshold.equalsIgnoreCase(attr)) {
            boolean fromATroubledInstall = (mSince.compare("7.0.0") >= 0 && mSince.compare("7.1.1") < 0);
            if (fromATroubledInstall) {
                return true;
            }
        }

        return false;
    }

    private void doEntry(ZLdapContext zlc, Entry entry, String entryName, AttributeClass klass)
            throws ServiceException {

        printer.println();
        printer.println("------------------------------");
        printer.println("Upgrading " + entryName + ": ");

        AttributeManager am = AttributeManager.getInstance();

        Set<String> attrs = am.getAttrsInClass(klass);
        Map<String, Object> attrValues = new HashMap<String, Object>();
        for (String attr : attrs) {
            AttributeInfo ai = am.getAttributeInfo(attr);
            if (ai == null)
                continue;

            List<Version> attrVersion = ai.getSince();

            if (needsUpgrade(am, attr)) {
                if (verbose) {
                    printer.println("");
                    printer.println("Checking " + entryName + " attribute: " + attr + attrVersion);
                }

                String curVal = entry.getAttr(attr);
                if (curVal != null) {
                    // already has a value, skip it
                    if (verbose) {
                        if (ai.getCardinality() == AttributeCardinality.multi)
                            curVal = formatMultiValue(entry.getMultiAttrSet(attr));
                        printer.println("    skipping - already has value: " + curVal);
                    }
                    continue;
                }

                /*
                 * use the upgrade values if set, otherwise use the default values
                 *
                 * Note, we support the case when we need to leave the value unset
                 * on upgrades, but set a value on new installs.  In AttributeManager,
                 * if <globalConfigValueUpgrade> or <defaultCOSValueUpgrade> element
                 * is present but does not have a value, AttributeInfo.getGlobalConfigValuesUpgrade()/
                 * getDefaultCosValuesUpgrade() will return an empty List.  If the upgrade
                 * element is not present, the two methods will return null.  We check
                 * null here and if it is null then use the same default value for new
                 * installs.
                 */
                List<String> values = null;
                if (klass == AttributeClass.globalConfig) {
                    values = ai.getGlobalConfigValuesUpgrade();
                    if (values == null)
                        values = ai.getGlobalConfigValues();
                } else if (klass == AttributeClass.cos) {
                    values = ai.getDefaultCosValuesUpgrade();
                    if (values == null)
                        values = ai.getDefaultCosValues();
                } else {
                    printer.println("Internal error: invalid attribute class " + klass.name());
                    return;
                }

                if (values == null || values.size() == 0) {
                    if (verbose) {
                        printer.println("    skipping - does not have a default value");
                    }
                    continue;
                }

                attrValues.clear();
                if (ai.getCardinality() != AttributeCardinality.multi) {
                    printer.println("    setting " + entryName + " attribute " + attr + attrVersion + " to: "
                            + values.get(0));
                    attrValues.put(attr, values.get(0));
                } else {
                    printer.println("    setting " + entryName + " attribute " + attr + attrVersion + " to: "
                            + formatMultiValue(values));
                    attrValues.put(attr, values.toArray(new String[0]));
                }

                try {
                    modifyAttrs(zlc, entry, attrValues);
                } catch (ServiceException e) {
                    // log the exception and continue
                    printer.println("Caught ServiceException while modifying " + entryName + " attribute " + attr);
                    printer.printStackTrace(e);
                }
            }
        }
    }

    private void doBug38425(Entry entry, String entryName) {
        String theAttr = Provisioning.A_zimbraPrefMailDefaultCharset;
        String sinceVer = mSince.toString();
        String thisVer = BuildInfo.VERSION;

        if (sinceVer.startsWith("6.0.0_BETA1") && thisVer.startsWith("6.0.0_BETA2")) {
            String curVal = entry.getAttr(theAttr);
            if ("UTF-8".equalsIgnoreCase(curVal)) {
                HashMap<String, Object> attrs = new HashMap<String, Object>();
                attrs.put(theAttr, "");
                try {
                    printer.println("Unsetting " + theAttr + " on " + entryName);
                    prov.modifyAttrs(entry, attrs);
                } catch (ServiceException e) {
                    printer.println("Caught ServiceException while unsetting " + theAttr + " on " + entryName);
                    printer.printStackTrace(e);
                }
            }
        }
    }

    private void doBug79208(Config config) throws ServiceException {
        // perform this step if the following condition is met (IV = installed version)
        // ((IV < 7.2.3) OR ((IV >= 8.0.0) AND (IV < 8.0.3))
        boolean stepRequired = mSince.compare("7.2.3") < 0
                || (mSince.compare("8.0.0") >= 0 && mSince.compare("8.0.3") < 0);
        if (!stepRequired) {
            return;
        }
        // check if the attribute already exists. It's possible that server may have been upgraded from
        // 7.2.2->7.2.3, 7.2.3->8.0.0 and then 8.0.0->8.0.3. The 7.2.2->7.2.3 would have already added the attribute.
        HashMap<String, Object> attrs = new HashMap<String, Object>();
        String attr = Provisioning.A_zimbraHttpThreadPoolMaxIdleTimeMillis;
        String curVal = config.getAttr(attr, null);
        if (curVal == null) {
            attrs.put(attr, config.getHttpThreadPoolMaxIdleTimeMillis());
        }
        attr = Provisioning.A_zimbraHttpConnectorMaxIdleTimeMillis;
        curVal = config.getAttr(attr, null);
        if (curVal == null) {
            attrs.put(attr, config.getHttpConnectorMaxIdleTimeMillis());
        }
        if (!attrs.isEmpty()) {
            try {
                printer.println("Setting " + attrs.keySet() + " on globalConfig");
                prov.modifyAttrs(config, attrs);
            } catch (ServiceException e) {
                printer.println("Caught ServiceException while setting " + attrs.keySet() + " on globalConfig");
                printer.printStackTrace(e);
            }
        }
    }

    private void doBug83551(Config config) throws ServiceException {
        // perform this step if the following condition is met (IV = installed version)
        // ((IV < 7.2.4) OR ((IV >= 8.0.0) AND (IV < 8.0.5))
        boolean stepRequired = mSince.compare("7.2.5") < 0
                || (mSince.compare("8.0.0") >= 0 && mSince.compare("8.0.5") < 0);
        if (!stepRequired) {
            return;
        }
        // check if the attribute already exists. It's possible that server may have been upgraded from
        // 7.2.2->7.2.5, 7.2.5->8.0.0 and then 8.0.0->8.0.5. The 7.2.2->7.2.5 would have already added the attribute.
        HashMap<String, Object> attrs = new HashMap<String, Object>();
        String attr = Provisioning.A_zimbraWebGzipEnabled;
        String curVal = config.getAttr(attr, null);
        if (curVal == null) {
            attrs.put(attr, ("" + config.isWebGzipEnabled()).toUpperCase());
        }

        attr = Provisioning.A_zimbraHttpCompressionEnabled;
        curVal = config.getAttr(attr, null);
        if (curVal == null) {
            attrs.put(attr, ("" + config.isHttpCompressionEnabled()).toUpperCase());
        }

        if (!attrs.isEmpty()) {
            try {
                printer.println("Setting " + attrs.keySet() + " on globalConfig");
                prov.modifyAttrs(config, attrs);
            } catch (ServiceException e) {
                printer.println("Caught ServiceException while setting " + attrs.keySet() + " on globalConfig");
                printer.printStackTrace(e);
            }
        }
    }

    private void doGlobalConfig(ZLdapContext zlc) throws ServiceException {
        Config config = prov.getConfig();
        doEntry(zlc, config, "global config", AttributeClass.globalConfig);
        doBug79208(config);
        doBug83551(config);
    }

    private void doAllCos(ZLdapContext zlc) throws ServiceException {
        List<Cos> coses = prov.getAllCos();

        for (Cos cos : coses) {
            String name = "cos " + cos.getName();
            doEntry(zlc, cos, name, AttributeClass.cos);
            doBug38425(cos, name);
        }
    }
}