Java tutorial
/* * ***** BEGIN LICENSE BLOCK ***** * Zimbra Collaboration Suite Server * Copyright (C) 2011, 2012, 2013, 2014, 2015, 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; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import com.google.common.annotations.VisibleForTesting; import com.zimbra.common.service.ServiceException; import com.zimbra.common.util.ByteUtil; import com.zimbra.common.util.CliUtil; import com.zimbra.common.util.DateUtil; import com.zimbra.common.util.Log; import com.zimbra.common.util.LogFactory; import com.zimbra.common.util.SetUtil; import com.zimbra.common.util.StringUtil; import com.zimbra.common.util.Version; import com.zimbra.common.util.ZimbraLog; import com.zimbra.cs.account.AttributeManager.ObjectClassInfo; import com.zimbra.cs.account.ldap.LdapProv; import com.zimbra.cs.util.MemoryUnitUtil; public class AttributeManagerUtil { private static Log logger = LogFactory.getLog(AttributeManagerUtil.class); private static Options options = new Options(); // multi-line continuation prefix chars private static final String ML_CONT_PREFIX = " "; private final AttributeManager attrMgr; static { options.addOption("h", "help", false, "display this usage info"); options.addOption("o", "output", true, "output file (default it to generate output to stdout)"); options.addOption("a", "action", true, "[generateLdapSchema | generateGlobalConfigLdif | generateDefaultCOSLdif | generateSchemaLdif]"); options.addOption("t", "template", true, "template for LDAP schema"); options.addOption("r", "regenerateFile", true, "Java file to regenerate"); Option iopt = new Option("i", "input", true, "attrs definition xml input file (can repeat)"); iopt.setArgs(Option.UNLIMITED_VALUES); options.addOption(iopt); /* * options for the listAttrs action */ Option copt = new Option("c", "inclass", true, "list attrs in class (can repeat)"); copt.setArgs(Option.UNLIMITED_VALUES); options.addOption(copt); Option nopt = new Option("n", "notinclass", true, "not list attrs in class (can repeat)"); nopt.setArgs(Option.UNLIMITED_VALUES); options.addOption(nopt); Option fopt = new Option("f", "flags", true, "flags to print (can repeat)"); fopt.setArgs(Option.UNLIMITED_VALUES); options.addOption(fopt); } private enum Action { dump, generateDefaultCOSLdif, generateDefaultExternalCOSLdif, generateGetters, generateGlobalConfigLdif, generateLdapSchema, generateMessageProperties, generateProvisioning, generateSchemaLdif, listAttrs } static class CLOptions { private static String get(String key, String defaultValue) { String value = System.getProperty(key); if (value == null) return defaultValue; else return value; } public static String buildVersion() { return get("zimbra.version", "unknown"); } public static String getBaseDn(String entry) { return get(entry + ".basedn", "cn=zimbra"); } public static String getEntryName(String entry, String defaultValue) { return get(entry + ".name", defaultValue); } public static String getEntryId(String entry, String defaultValue) { return get(entry + ".id", defaultValue); } } private AttributeManagerUtil(AttributeManager am) { attrMgr = am; } private Map<String, AttributeInfo> getAttrs() { return attrMgr.getAttrs(); } private Map<String, ObjectClassInfo> getOCs() { return attrMgr.getOCs(); } private Map<Integer, String> getGroupMap() { return attrMgr.getGroupMap(); } private Map<Integer, String> getOCGroupMap() { return attrMgr.getOCGroupMap(); } private String doNotModifyDisclaimer(String prefix) { return FileGenUtil.genDoNotModifyDisclaimer(prefix, AttributeManagerUtil.class.getSimpleName(), CLOptions.buildVersion()); } private void generateDefaultCOSLdif(PrintWriter pw) { pw.println(doNotModifyDisclaimer("#")); pw.println("#"); pw.println("# LDAP entry for the default Zimbra COS."); pw.println("#"); String baseDn = CLOptions.getBaseDn("cos"); String cosName = CLOptions.getEntryName("cos", Provisioning.DEFAULT_COS_NAME); String cosId = CLOptions.getEntryId("cos", "e00428a1-0c00-11d9-836a-000d93afea2a"); pw.println("dn: cn=" + cosName + ",cn=cos," + baseDn); pw.println("cn: " + cosName); pw.println("objectclass: zimbraCOS"); pw.println("zimbraId: " + cosId); pw.println("description: The " + cosName + " COS"); List<String> out = new LinkedList<String>(); for (AttributeInfo attr : getAttrs().values()) { List<String> gcv = attr.getDefaultCosValues(); if (gcv != null) { for (String v : gcv) { out.add(attr.getName() + ": " + v); } } } String[] outs = out.toArray(new String[out.size()]); Arrays.sort(outs); for (String o : outs) { pw.println(o); } } private void generateDefaultExternalCOSLdif(PrintWriter pw) { pw.println(doNotModifyDisclaimer("#")); pw.println("#"); pw.println("# LDAP entry for default COS for external user accounts."); pw.println("#"); String baseDn = CLOptions.getBaseDn("cos"); String cosName = CLOptions.getEntryName("cos", Provisioning.DEFAULT_EXTERNAL_COS_NAME); String cosId = CLOptions.getEntryId("cos", "f27456a8-0c00-11d9-280a-286d93afea2g"); pw.println("dn: cn=" + cosName + ",cn=cos," + baseDn); pw.println("cn: " + cosName); pw.println("objectclass: zimbraCOS"); pw.println("zimbraId: " + cosId); pw.println("description: The default external users COS"); List<String> out = new LinkedList<String>(); for (AttributeInfo attr : getAttrs().values()) { List<String> defaultValues = attr.getDefaultExternalCosValues(); if (defaultValues != null && !defaultValues.isEmpty()) { for (String v : defaultValues) { out.add(attr.getName() + ": " + v); } } else { defaultValues = attr.getDefaultCosValues(); if (defaultValues != null) { for (String v : defaultValues) { out.add(attr.getName() + ": " + v); } } } } String[] outs = out.toArray(new String[out.size()]); Arrays.sort(outs); for (String o : outs) { pw.println(o); } } private void generateGlobalConfigLdif(PrintWriter pw) { pw.println(doNotModifyDisclaimer("#")); pw.println("#"); pw.println("# LDAP entry that contains initial default Zimbra global config."); pw.println("#"); String baseDn = CLOptions.getBaseDn("config"); pw.println("dn: cn=config," + baseDn); pw.println("objectclass: organizationalRole"); pw.println("cn: config"); pw.println("objectclass: zimbraGlobalConfig"); List<String> out = new LinkedList<String>(); for (AttributeInfo attr : getAttrs().values()) { List<String> gcv = attr.getGlobalConfigValues(); if (gcv != null) { for (String v : gcv) { out.add(attr.getName() + ": " + v); } } } String[] outs = out.toArray(new String[out.size()]); Arrays.sort(outs); for (String o : outs) { pw.println(o); } } private List<AttributeInfo> getAttrList(int groupId) { List<AttributeInfo> list = new ArrayList<AttributeInfo>(getAttrs().size()); for (AttributeInfo ai : getAttrs().values()) { if (ai.getId() > -1 && ai.getGroupId() == groupId) { list.add(ai); } } return list; } private void sortAttrsByOID(List<AttributeInfo> list) { Collections.sort(list, new Comparator<AttributeInfo>() { @Override public int compare(AttributeInfo a1, AttributeInfo b1) { return a1.getId() - b1.getId(); } }); } private void sortAttrsByName(List<AttributeInfo> list) { Collections.sort(list, new Comparator<AttributeInfo>() { @Override public int compare(AttributeInfo a1, AttributeInfo b1) { return a1.getName().compareTo(b1.getName()); } }); } private List<ObjectClassInfo> getOCList(int groupId) { List<ObjectClassInfo> list = new ArrayList<ObjectClassInfo>(getOCs().size()); for (ObjectClassInfo oci : getOCs().values()) { if (oci.getId() > -1 && oci.getGroupId() == groupId) { list.add(oci); } } return list; } private void sortOCsByOID(List<ObjectClassInfo> list) { Collections.sort(list, new Comparator<ObjectClassInfo>() { @Override public int compare(ObjectClassInfo oc1, ObjectClassInfo oc2) { return oc1.getId() - oc2.getId(); } }); } private void sortOCsByName(List<ObjectClassInfo> list) { Collections.sort(list, new Comparator<ObjectClassInfo>() { @Override public int compare(ObjectClassInfo oc1, ObjectClassInfo oc2) { return oc1.getName().compareTo(oc2.getName()); } }); } private static String sortCSL(String in) { String[] ss = in.split("\\s*,\\s*"); Arrays.sort(ss); StringBuilder sb = new StringBuilder(in.length()); for (int i = 0; i < ss.length; i++) { sb.append(ss[i]); if (i < (ss.length - 1)) { sb.append(", "); } } return sb.toString(); } // escape QQ and QS for rfc4512 dstring(http://www.ietf.org/rfc/rfc4512.txt 4.1. Schema Definitions) private String rfc4512Dstring(String unescaped) { String escaped = unescaped.replace("\\", "\\5C").replace("'", "\\27"); return escaped; } private void buildSchemaBanner(StringBuilder BANNER) { BANNER.append(doNotModifyDisclaimer("#")); BANNER.append("#\n"); BANNER.append("# Zimbra LDAP Schema\n"); BANNER.append("#\n"); BANNER.append("#\n"); BANNER.append("#\n"); BANNER.append("# our root OID (http://www.iana.org/assignments/enterprise-numbers)\n"); BANNER.append("#\n"); BANNER.append("# 1.3.6.1.4.1.19348\n"); BANNER.append("# 1.3.6.1.4.1.19348.2 LDAP elements\n"); BANNER.append("# 1.3.6.1.4.1.19348.2.1 Attribute Types\n"); BANNER.append("# 1.3.6.1.4.1.19348.2.2 Object Classes\n"); BANNER.append("#"); } private void buildZimbraRootOIDs(StringBuilder ZIMBRA_ROOT_OIDS, String prefix) { ZIMBRA_ROOT_OIDS.append(prefix + "ZimbraRoot 1.3.6.1.4.1.19348\n"); ZIMBRA_ROOT_OIDS.append(prefix + "ZimbraLDAP ZimbraRoot:2\n"); } private void buildAttrDef(StringBuilder ATTRIBUTE_DEFINITIONS, AttributeInfo ai) { String lengthSuffix; String syntax = null; String substr = null; String equality = null; String ordering = null; switch (ai.getType()) { case TYPE_BOOLEAN: syntax = "1.3.6.1.4.1.1466.115.121.1.7"; equality = "booleanMatch"; break; case TYPE_BINARY: // cannot use the binary syntax because it cannot support adding/deleting individual values // in a multi-valued attrs, only replacement(i.e. replace all values) is supported. // // when a value is added to a multi-valued attr, or when an attempt is made to delete a // specific value, will get "no equality matching rule" error from LDAP server, because // there is no equality matching for 1.3.6.1.4.1.1466.115.121.1.5 // // Note: 1.3.6.1.4.1.1466.115.121.1.5 attrs, like userSMIMECertificate, when included in // the zimbra schema, are declared as type="binary" in zimbra-attrs.xml. // Handling for the two (1.3.6.1.4.1.1466.115.121.1.5 and 1.3.6.1.4.1.1466.115.121.1.40) // are *exactly the same* in ZCS. They are: // - transferred as binary on the wire, by setting the JNDI "java.naming.ldap.attributes.binary" // environment property // - stored as base64 encoded string in ZCS memory // - Entry.getAttr(String name) returns the base64 encoded value // - Entry.getBinaryAttr(String name) returns the base64 decoded value // /* lengthSuffix = ""; if (ai.getMax() != Long.MAX_VALUE) { lengthSuffix = "{" + ai.getMax() + "}"; } syntax = "1.3.6.1.4.1.1466.115.121.1.5" + lengthSuffix; break; */ // the same as octet string lengthSuffix = ""; if (ai.getMax() != Long.MAX_VALUE) { lengthSuffix = "{" + ai.getMax() + "}"; } syntax = "1.3.6.1.4.1.1466.115.121.1.40" + lengthSuffix; equality = "octetStringMatch"; break; case TYPE_CERTIFICATE: // This type does have a equality matching rule, so adding/deleting individual values // is supported. // lengthSuffix = ""; if (ai.getMax() != Long.MAX_VALUE) { lengthSuffix = "{" + ai.getMax() + "}"; } syntax = "1.3.6.1.4.1.1466.115.121.1.8" + lengthSuffix; equality = "certificateExactMatch"; break; case TYPE_EMAIL: case TYPE_EMAILP: case TYPE_CS_EMAILP: syntax = "1.3.6.1.4.1.1466.115.121.1.26{256}"; equality = "caseIgnoreIA5Match"; substr = "caseIgnoreSubstringsMatch"; break; case TYPE_GENTIME: syntax = "1.3.6.1.4.1.1466.115.121.1.24"; equality = "generalizedTimeMatch"; ordering = "generalizedTimeOrderingMatch "; break; case TYPE_ID: syntax = "1.3.6.1.4.1.1466.115.121.1.15{256}"; equality = "caseIgnoreMatch"; substr = "caseIgnoreSubstringsMatch"; break; case TYPE_DURATION: syntax = "1.3.6.1.4.1.1466.115.121.1.26{32}"; equality = "caseIgnoreIA5Match"; break; case TYPE_ENUM: int maxLen = Math.max(32, ai.getEnumValueMaxLength()); syntax = "1.3.6.1.4.1.1466.115.121.1.15{" + maxLen + "}"; equality = "caseIgnoreMatch"; substr = "caseIgnoreSubstringsMatch"; break; case TYPE_INTEGER: case TYPE_PORT: case TYPE_LONG: syntax = "1.3.6.1.4.1.1466.115.121.1.27"; equality = "integerMatch"; break; case TYPE_STRING: case TYPE_REGEX: lengthSuffix = ""; if (ai.getMax() != Long.MAX_VALUE) { lengthSuffix = "{" + ai.getMax() + "}"; } syntax = "1.3.6.1.4.1.1466.115.121.1.15" + lengthSuffix; equality = "caseIgnoreMatch"; substr = "caseIgnoreSubstringsMatch"; break; case TYPE_ASTRING: lengthSuffix = ""; if (ai.getMax() != Long.MAX_VALUE) { lengthSuffix = "{" + ai.getMax() + "}"; } syntax = "1.3.6.1.4.1.1466.115.121.1.26" + lengthSuffix; equality = "caseIgnoreIA5Match"; substr = "caseIgnoreSubstringsMatch"; break; case TYPE_OSTRING: lengthSuffix = ""; if (ai.getMax() != Long.MAX_VALUE) { lengthSuffix = "{" + ai.getMax() + "}"; } syntax = "1.3.6.1.4.1.1466.115.121.1.40" + lengthSuffix; equality = "octetStringMatch"; break; case TYPE_CSTRING: lengthSuffix = ""; if (ai.getMax() != Long.MAX_VALUE) { lengthSuffix = "{" + ai.getMax() + "}"; } syntax = "1.3.6.1.4.1.1466.115.121.1.15" + lengthSuffix; equality = "caseExactMatch"; substr = "caseExactSubstringsMatch"; break; case TYPE_PHONE: lengthSuffix = ""; if (ai.getMax() != Long.MAX_VALUE) { lengthSuffix = "{" + ai.getMax() + "}"; } syntax = "1.3.6.1.4.1.1466.115.121.1.50" + lengthSuffix; equality = "telephoneNumberMatch"; substr = "telephoneNumberSubstringsMatch"; break; default: throw new RuntimeException("unknown type encountered!"); } ATTRIBUTE_DEFINITIONS.append("( " + ai.getName() + "\n"); ATTRIBUTE_DEFINITIONS.append(ML_CONT_PREFIX + "NAME ( '" + ai.getName() + "' )\n"); ATTRIBUTE_DEFINITIONS.append(ML_CONT_PREFIX + "DESC '" + rfc4512Dstring(ai.getDescription()) + "'\n"); ATTRIBUTE_DEFINITIONS.append(ML_CONT_PREFIX + "SYNTAX " + syntax); if (equality != null) { ATTRIBUTE_DEFINITIONS.append("\n" + ML_CONT_PREFIX + "EQUALITY " + equality); } if (substr != null) { ATTRIBUTE_DEFINITIONS.append("\n" + ML_CONT_PREFIX + "SUBSTR " + substr); } if (ordering != null) { ATTRIBUTE_DEFINITIONS.append("\n" + ML_CONT_PREFIX + "ORDERING " + ordering); } else if (ai.getOrder() != null) { ATTRIBUTE_DEFINITIONS.append("\n" + ML_CONT_PREFIX + "ORDERING " + ai.getOrder()); } if (ai.getCardinality() == AttributeCardinality.single) { ATTRIBUTE_DEFINITIONS.append("\n" + ML_CONT_PREFIX + "SINGLE-VALUE"); } ATTRIBUTE_DEFINITIONS.append(")"); } private void buildObjectClassOIDs(StringBuilder OC_GROUP_OIDS, StringBuilder OC_OIDS, String prefix) { for (Iterator<Integer> iter = getOCGroupMap().keySet().iterator(); iter.hasNext();) { int i = iter.next(); // OC_GROUP_OIDS OC_GROUP_OIDS.append(prefix + getOCGroupMap().get(i) + " ZimbraLDAP:" + i + "\n"); // List all ocs which we define and which belong in this group List<ObjectClassInfo> list = getOCList(i); // OC_OIDS - sorted by OID sortOCsByOID(list); for (ObjectClassInfo oci : list) { OC_OIDS.append(prefix + oci.getName() + " " + getOCGroupMap().get(i) + ':' + oci.getId() + "\n"); } } } /** * * @param OC_DEFINITIONS * @param prefix * @param blankLineSeperator whether to seperate each OC with a blank line */ private void buildObjectClassDefs(StringBuilder OC_DEFINITIONS, String prefix, boolean blankLineSeperator) { for (AttributeClass cls : AttributeClass.values()) { String ocName = cls.getOCName(); String ocCanonicalName = ocName.toLowerCase(); ObjectClassInfo oci = getOCs().get(ocCanonicalName); if (oci == null) continue; // oc not defined in xml, skip // OC_DEFINITIONS: List<String> comment = oci.getComment(); OC_DEFINITIONS.append("#\n"); for (String line : comment) { if (line.length() > 0) OC_DEFINITIONS.append("# " + line + "\n"); else OC_DEFINITIONS.append("#\n"); } OC_DEFINITIONS.append("#\n"); OC_DEFINITIONS.append(prefix + "( " + oci.getName() + "\n"); OC_DEFINITIONS.append(ML_CONT_PREFIX + "NAME '" + oci.getName() + "'\n"); OC_DEFINITIONS.append(ML_CONT_PREFIX + "DESC '" + rfc4512Dstring(oci.getDescription()) + "'\n"); OC_DEFINITIONS.append(ML_CONT_PREFIX + "SUP "); for (String sup : oci.getSuperOCs()) OC_DEFINITIONS.append(sup); OC_DEFINITIONS.append(" " + oci.getType() + "\n"); StringBuilder value = new StringBuilder(); buildObjectClassAttrs(cls, value); OC_DEFINITIONS.append(value); OC_DEFINITIONS.append(")\n"); if (blankLineSeperator) OC_DEFINITIONS.append("\n"); } } private void buildObjectClassAttrs(AttributeClass cls, StringBuilder value) { List<String> must = new LinkedList<String>(); List<String> may = new LinkedList<String>(); for (AttributeInfo ai : getAttrs().values()) { if (ai.requiredInClass(cls)) { must.add(ai.getName()); } if (ai.optionalInClass(cls)) { may.add(ai.getName()); } } Collections.sort(must); Collections.sort(may); if (!must.isEmpty()) { value.append(ML_CONT_PREFIX + "MUST (\n"); Iterator<String> mustIter = must.iterator(); while (true) { value.append(ML_CONT_PREFIX + " ").append(mustIter.next()); if (!mustIter.hasNext()) { break; } value.append(" $\n"); } value.append("\n" + ML_CONT_PREFIX + ")\n"); } if (!may.isEmpty()) { value.append(ML_CONT_PREFIX + "MAY (\n"); Iterator<String> mayIter = may.iterator(); while (true) { value.append(ML_CONT_PREFIX + " ").append(mayIter.next()); if (!mayIter.hasNext()) { break; } value.append(" $\n"); } value.append("\n" + ML_CONT_PREFIX + ")\n"); } value.append(ML_CONT_PREFIX); } /** * This methods uses xml for generating objectclass OIDs and definitions */ private void generateLdapSchema(PrintWriter pw, String schemaTemplateFile) throws IOException { byte[] templateBytes = ByteUtil.getContent(new File(schemaTemplateFile)); String templateString = new String(templateBytes, "utf-8"); StringBuilder BANNER = new StringBuilder(); StringBuilder ZIMBRA_ROOT_OIDS = new StringBuilder(); StringBuilder GROUP_OIDS = new StringBuilder(); StringBuilder ATTRIBUTE_OIDS = new StringBuilder(); StringBuilder ATTRIBUTE_DEFINITIONS = new StringBuilder(); StringBuilder OC_GROUP_OIDS = new StringBuilder(); StringBuilder OC_OIDS = new StringBuilder(); StringBuilder OC_DEFINITIONS = new StringBuilder(); buildSchemaBanner(BANNER); buildZimbraRootOIDs(ZIMBRA_ROOT_OIDS, "objectIdentifier "); for (Iterator<Integer> iter = getGroupMap().keySet().iterator(); iter.hasNext();) { int i = iter.next(); //GROUP_OIDS GROUP_OIDS.append("objectIdentifier " + getGroupMap().get(i) + " ZimbraLDAP:" + i + "\n"); // List all attrs which we define and which belong in this group List<AttributeInfo> list = getAttrList(i); // ATTRIBUTE_OIDS - sorted by OID sortAttrsByOID(list); for (AttributeInfo ai : list) { String parentOid = ai.getParentOid(); if (parentOid == null) ATTRIBUTE_OIDS.append("objectIdentifier " + ai.getName() + " " + getGroupMap().get(i) + ':' + ai.getId() + "\n"); else ATTRIBUTE_OIDS .append("objectIdentifier " + ai.getName() + " " + parentOid + "." + ai.getId() + "\n"); } // ATTRIBUTE_DEFINITIONS: DESC EQUALITY NAME ORDERING SINGLE-VALUE SUBSTR SYNTAX // - sorted by name sortAttrsByName(list); for (AttributeInfo ai : list) { ATTRIBUTE_DEFINITIONS.append("attributetype "); buildAttrDef(ATTRIBUTE_DEFINITIONS, ai); ATTRIBUTE_DEFINITIONS.append("\n\n"); } } // object class OIDs buildObjectClassOIDs(OC_GROUP_OIDS, OC_OIDS, "objectIdentifier "); // object class definitions buildObjectClassDefs(OC_DEFINITIONS, "objectclass ", true); Map<String, String> templateFillers = new HashMap<String, String>(); templateFillers.put("BANNER", BANNER.toString()); templateFillers.put("ZIMBRA_ROOT_OIDS", ZIMBRA_ROOT_OIDS.toString()); templateFillers.put("GROUP_OIDS", GROUP_OIDS.toString()); templateFillers.put("ATTRIBUTE_OIDS", ATTRIBUTE_OIDS.toString()); templateFillers.put("OC_GROUP_OIDS", OC_GROUP_OIDS.toString()); templateFillers.put("OC_OIDS", OC_OIDS.toString()); templateFillers.put("ATTRIBUTE_DEFINITIONS", ATTRIBUTE_DEFINITIONS.toString()); templateFillers.put("OC_DEFINITIONS", OC_DEFINITIONS.toString()); pw.print(StringUtil.fillTemplate(templateString, templateFillers)); } private void generateSchemaLdif(PrintWriter pw) { StringBuilder BANNER = new StringBuilder(); StringBuilder ZIMBRA_ROOT_OIDS = new StringBuilder(); StringBuilder ATTRIBUTE_GROUP_OIDS = new StringBuilder(); StringBuilder ATTRIBUTE_OIDS = new StringBuilder(); StringBuilder ATTRIBUTE_DEFINITIONS = new StringBuilder(); StringBuilder OC_GROUP_OIDS = new StringBuilder(); StringBuilder OC_OIDS = new StringBuilder(); StringBuilder OC_DEFINITIONS = new StringBuilder(); buildSchemaBanner(BANNER); buildZimbraRootOIDs(ZIMBRA_ROOT_OIDS, "olcObjectIdentifier: "); for (Iterator<Integer> iter = getGroupMap().keySet().iterator(); iter.hasNext();) { int i = iter.next(); //GROUP_OIDS ATTRIBUTE_GROUP_OIDS.append("olcObjectIdentifier: " + getGroupMap().get(i) + " ZimbraLDAP:" + i + "\n"); // List all attrs which we define and which belong in this group List<AttributeInfo> list = getAttrList(i); // ATTRIBUTE_OIDS - sorted by OID sortAttrsByOID(list); for (AttributeInfo ai : list) { String parentOid = ai.getParentOid(); if (parentOid == null) ATTRIBUTE_OIDS.append("olcObjectIdentifier: " + ai.getName() + " " + getGroupMap().get(i) + ':' + ai.getId() + "\n"); else ATTRIBUTE_OIDS.append( "olcObjectIdentifier: " + ai.getName() + " " + parentOid + "." + ai.getId() + "\n"); } // ATTRIBUTE_DEFINITIONS: DESC EQUALITY NAME ORDERING SINGLE-VALUE SUBSTR SYNTAX // - sorted by name sortAttrsByName(list); /* Hack to add the company attribute from Microsoft schema * For generateLdapSchema, it is specified in the zimbra.schema-template file * We don't use a template file for generateSchemaLdif thus hardcode here. * Move to template file if really necessary. * #### From Microsoft Schema olcAttributeTypes ( 1.2.840.113556.1.2.146 NAME ( 'company' ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512} EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SINGLE-VALUE ) */ ATTRIBUTE_DEFINITIONS.append("olcAttributeTypes: ( 1.2.840.113556.1.2.146\n"); ATTRIBUTE_DEFINITIONS.append(ML_CONT_PREFIX + "NAME ( 'company' )\n"); ATTRIBUTE_DEFINITIONS.append(ML_CONT_PREFIX + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{512}\n"); ATTRIBUTE_DEFINITIONS.append(ML_CONT_PREFIX + "EQUALITY caseIgnoreMatch\n"); ATTRIBUTE_DEFINITIONS.append(ML_CONT_PREFIX + "SUBSTR caseIgnoreSubstringsMatch\n"); ATTRIBUTE_DEFINITIONS.append(ML_CONT_PREFIX + "SINGLE-VALUE )\n"); for (AttributeInfo ai : list) { ATTRIBUTE_DEFINITIONS.append("olcAttributeTypes: "); buildAttrDef(ATTRIBUTE_DEFINITIONS, ai); ATTRIBUTE_DEFINITIONS.append("\n"); } } // objectclass OIDs buildObjectClassOIDs(OC_GROUP_OIDS, OC_OIDS, "olcObjectIdentifier: "); // objectclass definitions buildObjectClassDefs(OC_DEFINITIONS, "olcObjectClasses: ", false); pw.println(BANNER); pw.println("dn: cn=zimbra,cn=schema,cn=config"); pw.println("objectClass: olcSchemaConfig"); pw.println("cn: zimbra"); pw.print(ZIMBRA_ROOT_OIDS); pw.print(ATTRIBUTE_GROUP_OIDS); pw.print(ATTRIBUTE_OIDS); pw.print(OC_GROUP_OIDS); pw.print(OC_OIDS); pw.print(ATTRIBUTE_DEFINITIONS); pw.print(OC_DEFINITIONS); } private void generateMessageProperties(String outFile) throws IOException { StringBuilder result = new StringBuilder(); result.append(doNotModifyDisclaimer("#")); result.append("# Zimbra LDAP attributes." + "\n"); result.append("# \n"); List<String> attrs = new ArrayList<String>(getAttrs().keySet()); Collections.sort(attrs); for (String attr : attrs) { AttributeInfo ai = getAttrs().get(attr); String desc = ai.getDescription(); if (desc != null) { String text = FileGenUtil.wrapComments(desc, 80, " ", " \\").substring(2); // strip off the 2 spaces on the first line result.append(ai.getName() + " = " + text + "\n"); } } FileGenUtil.replaceFile(outFile, result.toString()); } private void listAttrs(PrintWriter pw, String[] inClass, String[] notInClass, String[] printFlags) throws ServiceException { if (inClass == null) usage("no class specified"); Set<String> attrsInClass = new HashSet<String>(); for (String c : inClass) { AttributeClass ac = AttributeClass.valueOf(c); SetUtil.union(attrsInClass, attrMgr.getAttrsInClass(ac)); } Set<String> attrsNotInClass = new HashSet<String>(); if (notInClass != null) { for (String c : notInClass) { AttributeClass ac = AttributeClass.valueOf(c); SetUtil.union(attrsNotInClass, attrMgr.getAttrsInClass(ac)); } } attrsInClass = SetUtil.subtract(attrsInClass, attrsNotInClass); List<String> list = new ArrayList<String>(attrsInClass); Collections.sort(list); for (String a : list) { StringBuffer flags = new StringBuffer(); if (printFlags != null) { for (String f : printFlags) { AttributeFlag af = AttributeFlag.valueOf(f); if (attrMgr.hasFlag(af, a)) { if (flags.length() > 0) flags.append(", "); flags.append(af.name()); } } if (flags.length() > 0) { flags.insert(0, "(").append(")"); } } System.out.println(a + " " + flags); } } private static String enumName(AttributeInfo ai) { String enumName = ai.getName(); if (enumName.startsWith("zimbra")) enumName = enumName.substring(6); enumName = StringUtil.escapeJavaIdentifier(enumName.substring(0, 1).toUpperCase() + enumName.substring(1)); return enumName; } private static void generateEnum(StringBuilder result, AttributeInfo ai) throws ServiceException { Map<String, String> values = new LinkedHashMap<String, String>(); for (String v : ai.getEnumSet()) { values.put(v, StringUtil.escapeJavaIdentifier(v)); } String enumName = enumName(ai); result.append(String.format("%n")); result.append(String.format(" public static enum %s {%n", enumName)); Set<Map.Entry<String, String>> set = values.entrySet(); int i = 1; for (Map.Entry<String, String> entry : set) { result.append(String.format(" %s(\"%s\")%s%n", entry.getValue(), entry.getKey(), i == set.size() ? ";" : ",")); i++; } result.append(String.format(" private String mValue;%n")); result.append(String.format(" private %s(String value) { mValue = value; }%n", enumName)); result.append(String.format(" public String toString() { return mValue; }%n")); result.append(String.format(" public static %s fromString(String s) throws ServiceException {%n", enumName)); result.append(String.format(" for (%s value : values()) {%n", enumName)); result.append(String.format(" if (value.mValue.equals(s)) return value;%n")); result.append(String.format(" }%n")); result.append(String.format( " throw ServiceException.INVALID_REQUEST(\"invalid value: \"+s+\", valid values: \"+ Arrays.asList(values()), null);%n")); result.append(String.format(" }%n")); for (Map.Entry<String, String> entry : set) { result.append(String.format(" public boolean is%s() { return this == %s;}%n", StringUtil.capitalize(entry.getValue()), entry.getValue())); } result.append(String.format(" }%n")); } /** * * @param pw * @param inClass * @param optionValue * @throws ServiceException */ private void generateGetters(String inClass, String javaFile) throws ServiceException, IOException { if (inClass == null) usage("no class specified"); AttributeClass ac = AttributeClass.valueOf(inClass); Set<String> attrsInClass = attrMgr.getAttrsInClass(ac); // add in mailRecipient if we need to if (ac == AttributeClass.account) { SetUtil.union(attrsInClass, attrMgr.getAttrsInClass(AttributeClass.mailRecipient)); } List<String> list = new ArrayList<String>(attrsInClass); Collections.sort(list); StringBuilder result = new StringBuilder(); for (String a : list) { AttributeInfo ai = getAttrs().get(a.toLowerCase()); if (ai == null) continue; switch (ai.getType()) { case TYPE_BINARY: case TYPE_CERTIFICATE: case TYPE_DURATION: case TYPE_GENTIME: case TYPE_ENUM: case TYPE_PORT: if (ai.getCardinality() != AttributeCardinality.multi) { generateGetter(result, ai, false, ac); } generateGetter(result, ai, true, ac); generateSetters(result, ai, false, SetterType.set); if (ai.getType() == AttributeType.TYPE_GENTIME || ai.getType() == AttributeType.TYPE_ENUM || ai.getType() == AttributeType.TYPE_PORT) { generateSetters(result, ai, true, SetterType.set); } generateSetters(result, ai, false, SetterType.unset); break; default: if (ai.getName().equalsIgnoreCase("zimbraLocale")) { generateGetter(result, ai, true, ac); } else { generateGetter(result, ai, false, ac); } generateSetters(result, ai, false, SetterType.set); if (ai.getCardinality() == AttributeCardinality.multi) { generateSetters(result, ai, false, SetterType.add); generateSetters(result, ai, false, SetterType.remove); if (ai.isEphemeral()) { generateSetters(result, ai, false, SetterType.has); } if (ai.isExpirable()) { generateSetters(result, ai, false, SetterType.purge); } } generateSetters(result, ai, false, SetterType.unset); break; } } FileGenUtil.replaceJavaFile(javaFile, result.toString()); } private static String defaultValue(AttributeInfo ai, AttributeClass ac) { List<String> values; switch (ac) { case account: case calendarResource: case cos: values = ai.getDefaultCosValues(); break; case domain: if (ai.hasFlag(AttributeFlag.domainInherited)) values = ai.getGlobalConfigValues(); else return null; break; case server: if (ai.hasFlag(AttributeFlag.serverInherited)) values = ai.getGlobalConfigValues(); else return null; break; case globalConfig: values = ai.getGlobalConfigValues(); break; default: return null; } if (values == null || values.size() == 0) return null; if (ai.getCardinality() != AttributeCardinality.multi) { return values.get(0); } else { StringBuilder result = new StringBuilder(); result.append("new String[] {"); boolean first = true; for (String v : values) { if (!first) result.append(","); else first = false; result.append("\""); result.append(v.replace("\"", "\\\"")); result.append("\""); } result.append("}"); return result.toString(); } } @VisibleForTesting public static void generateGetter(StringBuilder result, AttributeInfo ai, boolean asString, AttributeClass ac) throws ServiceException { String javaType; String javaBody; String javaDocReturns; String name = ai.getName(); AttributeType type = asString ? AttributeType.TYPE_STRING : ai.getType(); boolean asStringDoc = false; String methodName = ai.getName(); if (methodName.startsWith("zimbra")) methodName = methodName.substring(6); methodName = (type == AttributeType.TYPE_BOOLEAN ? "is" : "get") + methodName.substring(0, 1).toUpperCase() + methodName.substring(1); if (asString) methodName += "AsString"; String defaultValue = defaultValue(ai, ac); String dynamic = ai.isDynamic() ? "dynamicComponent" : "null"; switch (type) { case TYPE_BOOLEAN: defaultValue = "TRUE".equalsIgnoreCase(defaultValue) ? "true" : "false"; javaType = "boolean"; if (ai.isEphemeral()) { javaBody = String.format("return getEphemeralAttr(Provisioning.A_%s, %s).getBoolValue(%s);", name, dynamic, defaultValue); } else { javaBody = String.format("return getBooleanAttr(Provisioning.A_%s, %s, true);", name, defaultValue); } javaDocReturns = String.format(", or %s if unset", defaultValue); break; case TYPE_BINARY: case TYPE_CERTIFICATE: defaultValue = "null"; javaType = "byte[]"; if (ai.isEphemeral()) { javaBody = String.format( "String v = getEphemeralAttr(Provisioning.A_%s, %s).getValue(%s); return v == null ? null : ByteUtil.decodeLDAPBase64(v);", name, dynamic, defaultValue); } else { javaBody = String.format("return getBinaryAttr(Provisioning.A_%s, true);", name); } javaDocReturns = String.format(", or null if unset", defaultValue); break; case TYPE_INTEGER: if (defaultValue == null) defaultValue = "-1"; javaType = "int"; if (ai.isEphemeral()) { javaBody = String.format("return getEphemeralAttr(Provisioning.A_%s, %s).getIntValue(%s);", name, dynamic, defaultValue); } else { javaBody = String.format("return getIntAttr(Provisioning.A_%s, %s, true);", name, defaultValue); } javaDocReturns = String.format(", or %s if unset", defaultValue); break; case TYPE_PORT: if (defaultValue == null) defaultValue = "-1"; javaType = "int"; if (ai.isEphemeral()) { javaBody = String.format("return getEphemeralAttr(Provisioning.A_%s, %s).getIntValue(%s);", name, dynamic, defaultValue); } else { javaBody = String.format("return getIntAttr(Provisioning.A_%s, %s, true);", name, defaultValue); } javaDocReturns = String.format(", or %s if unset", defaultValue); asStringDoc = true; break; case TYPE_ENUM: javaType = "ZAttrProvisioning." + enumName(ai); if (defaultValue != null) { defaultValue = javaType + "." + StringUtil.escapeJavaIdentifier(defaultValue); } else { defaultValue = "null"; } if (ai.isEphemeral()) { javaBody = String.format( "try { String v = getEphemeralAttr(Provisioning.A_%s, %s).getValue(); return v == null ? %s : ZAttrProvisioning.%s.fromString(v); } catch(com.zimbra.common.service.ServiceException e) { return %s; }", name, dynamic, defaultValue, enumName(ai), defaultValue); } else { javaBody = String.format( "try { String v = getAttr(Provisioning.A_%s, true, true); return v == null ? %s : ZAttrProvisioning.%s.fromString(v); } catch(com.zimbra.common.service.ServiceException e) { return %s; }", name, defaultValue, enumName(ai), defaultValue); } javaDocReturns = String.format(", or %s if unset and/or has invalid value", defaultValue); break; case TYPE_LONG: if (defaultValue == null) defaultValue = "-1"; javaType = "long"; if (ai.isEphemeral()) { javaBody = String.format("return getEphemeralAttr(Provisioning.A_%s, %s).getLongValue(%sL);", name, dynamic, defaultValue); } else { javaBody = String.format("return getLongAttr(Provisioning.A_%s, %sL, true);", name, new MemoryUnitUtil(1024).convertToBytes(defaultValue)); } javaDocReturns = String.format(", or %s if unset", defaultValue); break; case TYPE_DURATION: String defaultDurationStrValue; if (defaultValue != null) { defaultDurationStrValue = " (" + defaultValue + ") "; defaultValue = String.valueOf(DateUtil.getTimeInterval(defaultValue, -1)); } else { defaultValue = "-1"; defaultDurationStrValue = ""; } if (ai.isEphemeral()) { javaBody = String.format("return getEphemeralTimeInterval(Provisioning.A_%s, %s, %sL);", name, dynamic, defaultValue); } else { javaBody = String.format("return getTimeInterval(Provisioning.A_%s, %sL, true);", name, defaultValue); } javaDocReturns = String.format(" in millseconds, or %s%s if unset", defaultValue, defaultDurationStrValue); javaType = "long"; asStringDoc = true; break; case TYPE_GENTIME: javaType = "Date"; if (ai.isEphemeral()) { javaBody = String.format( "String v = getEphemeralAttr(Provisioning.A_%s, %s).getValue(%s); return v == null ? null : LdapDateUtil.parseGeneralizedTime(v);", name, dynamic, defaultValue); } else { javaBody = String.format("return getGeneralizedTimeAttr(Provisioning.A_%s, null, true);", name); } javaDocReturns = " as Date, null if unset or unable to parse"; asStringDoc = true; break; default: if (ai.getCardinality() != AttributeCardinality.multi) { if (defaultValue != null) { defaultValue = "\"" + defaultValue.replace("\"", "\\\"") + "\""; } else { defaultValue = "null"; } javaType = "String"; if (ai.isEphemeral()) { javaBody = String.format("return getEphemeralAttr(Provisioning.A_%s, %s).getValue(%s);", name, dynamic, defaultValue); } else { javaBody = String.format("return getAttr(Provisioning.A_%s, %s, true);", name, defaultValue); } javaDocReturns = String.format(", or %s if unset", defaultValue); } else { if (ai.isEphemeral()) { javaType = "String"; javaBody = String.format("return getEphemeralAttr(Provisioning.A_%s, %s).getValue(%s);", name, dynamic, defaultValue); } else { javaType = "String[]"; if (defaultValue == null) { javaBody = String.format("return getMultiAttr(Provisioning.A_%s, true, true);", name); } else { javaBody = String.format( "String[] value = getMultiAttr(Provisioning.A_%s, true, true); return value.length > 0 ? value : %s;", name, defaultValue); } } javaDocReturns = ", or empty array if unset"; } break; } result.append("\n /**\n"); if (ai.getDescription() != null) { result.append(FileGenUtil.wrapComments(StringUtil.escapeHtml(ai.getDescription()), 70, " * ")); result.append("\n"); } if (ai.getType() == AttributeType.TYPE_ENUM) { result.append(" *\n"); result.append(String.format(" * <p>Valid values: %s%n", ai.getEnumSet().toString())); } if (asStringDoc) { result.append(" *\n"); result.append(String.format(" * <p>Use %sAsString to access value as a string.%n", methodName)); result.append(" *\n"); result.append(String.format(" * @see #%sAsString()%n", methodName)); } if (ai.isEphemeral()) { result.append(" *\n"); result.append(" * Ephemeral attribute - requests routed to EphemeralStore\n"); result.append(" *\n"); result.append( " * @throws com.zimbra.common.service.ServiceException if error on accessing ephemeral data\n"); } result.append(" *\n"); result.append(String.format(" * @return %s%s%n", name, javaDocReturns)); if (ai.getSince() != null) { result.append(" *\n"); result.append(String.format(" * @since ZCS %s%n", versionListAsString(ai.getSince()))); } result.append(" */\n"); result.append(String.format(" @ZAttr(id=%d)%n", ai.getId())); result.append(String.format(" public %s %s(%s)", javaType, methodName, ai.isDynamic() ? "String dynamicComponent" : "")); if (ai.isEphemeral()) { result.append(" throws com.zimbra.common.service.ServiceException"); } result.append(String.format(" {%n %s%n }%n", javaBody)); } private static String versionListAsString(List<Version> versions) { if (versions == null || versions.size() == 0) { return ""; } else if (versions.size() == 1) { return versions.iterator().next().toString(); } else { StringBuilder sb = new StringBuilder(); for (Version version : versions) { sb.append(version.toString()).append(","); } sb.setLength(sb.length() - 1); return sb.toString(); } } @VisibleForTesting static enum SetterType { set, add, unset, remove, /* these two are for ephemeral attrs */ purge, has } private static void generateSetters(StringBuilder result, AttributeInfo ai, boolean asString, SetterType setterType) throws ServiceException { generateSetter(result, ai, asString, setterType, true); generateSetter(result, ai, asString, setterType, false); } @VisibleForTesting public static void generateSetter(StringBuilder result, AttributeInfo ai, boolean asString, SetterType setterType, boolean noMap) throws ServiceException { if (ai.isEphemeral()) { if (!noMap) { return; //don't generate any epheemeral setters with the map parameter } else if (ai.isDynamic() && (setterType == SetterType.unset || setterType == SetterType.set)) { //don't generate ephemeral setters/unsetters for dynamic ephemeral attributes, //since we don't support deleting all values for a key. return; } } String javaType; String putParam; String name = ai.getName(); AttributeType type = asString ? AttributeType.TYPE_STRING : ai.getType(); String methodName = ai.getName(); if (methodName.startsWith("zimbra")) methodName = methodName.substring(6); methodName = setterType.name() + methodName.substring(0, 1).toUpperCase() + methodName.substring(1); if (asString) methodName += "AsString"; switch (type) { case TYPE_BOOLEAN: javaType = "boolean"; putParam = String.format("%s ? Provisioning.TRUE : Provisioning.FALSE", name); break; case TYPE_BINARY: case TYPE_CERTIFICATE: javaType = "byte[]"; putParam = String.format("%s==null ? \"\" : ByteUtil.encodeLDAPBase64(%s)", name, name); break; case TYPE_INTEGER: case TYPE_PORT: javaType = "int"; putParam = String.format("Integer.toString(%s)", name); break; case TYPE_LONG: javaType = "long"; putParam = String.format("Long.toString(%s)", name); break; case TYPE_GENTIME: javaType = "Date"; putParam = String.format("%s==null ? \"\" : LdapDateUtil.toGeneralizedTime(%s)", name, name); break; case TYPE_ENUM: javaType = "ZAttrProvisioning." + enumName(ai); putParam = String.format("%s.toString()", name); break; default: if (ai.getCardinality() != AttributeCardinality.multi) { javaType = "String"; putParam = String.format("%s", name); } else { if (setterType == SetterType.set) javaType = "String[]"; else javaType = "String"; putParam = String.format("%s", name); } break; } String mapType = "Map<String,Object>"; result.append("\n /**\n"); if (ai.getDescription() != null) { result.append(FileGenUtil.wrapComments(StringUtil.escapeHtml(ai.getDescription()), 70, " * ")); result.append("\n"); } if (ai.getType() == AttributeType.TYPE_ENUM) { result.append(" *\n"); result.append(String.format(" * <p>Valid values: %s%n", ai.getEnumSet().toString())); } result.append(" *\n"); StringBuilder paramDoc = new StringBuilder(); String body = ""; if (ai.isEphemeral()) { paramDoc.append(" * Ephemeral attribute - requests routed to EphemeralStore\n"); paramDoc.append(" *\n"); } String expiry = ai.isExpirable() ? "expiration" : "null"; String dynamic = ai.isDynamic() ? "dynamicComponent" : "null"; switch (setterType) { case set: if (ai.isEphemeral()) { body = String.format(" modifyEphemeralAttr(Provisioning.A_%s, %s, %s, false, %s);%n", name, dynamic, putParam, expiry); } else { body = String.format(" attrs.put(Provisioning.A_%s, %s);%n", name, putParam); } paramDoc.append(String.format(" * @param %s new value%n", name)); break; case add: if (ai.isEphemeral()) { body = String.format(" modifyEphemeralAttr(Provisioning.A_%s, %s, %s, true, %s);%n", name, dynamic, putParam, expiry); } else { body = String.format(" StringUtil.addToMultiMap(attrs, \"+\" + Provisioning.A_%s, %s);%n", name, name); } paramDoc.append(String.format(" * @param %s new to add to existing values%n", name)); break; case remove: if (ai.isEphemeral()) { body = String.format(" deleteEphemeralAttr(Provisioning.A_%s, %s, %s);%n", name, dynamic, putParam); } else { body = String.format(" StringUtil.addToMultiMap(attrs, \"-\" + Provisioning.A_%s, %s);%n", name, name); } paramDoc.append(String.format(" * @param %s existing value to remove%n", name)); break; case unset: if (ai.isEphemeral()) { body = String.format(" deleteEphemeralAttr(Provisioning.A_%s);%n", name); } else { body = String.format(" attrs.put(Provisioning.A_%s, \"\");%n", name); } // paramDoc = null; break; case purge: body = String.format(" purgeEphemeralAttr(Provisioning.A_%s);%n", name); break; case has: body = String.format(" return hasEphemeralAttr(Provisioning.A_%s, %s);%n", name, dynamic); break; default: break; } if (paramDoc != null) result.append(paramDoc.toString()); if (!noMap) { result.append( String.format(" * @param attrs existing map to populate, or null to create a new map%n")); result.append(" * @return populated map to pass into Provisioning.modifyAttrs\n"); } else { result.append(" * @throws com.zimbra.common.service.ServiceException if error during update\n"); } if (ai.getSince() != null) { result.append(" *\n"); result.append(String.format(" * @since ZCS %s%n", versionListAsString(ai.getSince()))); } result.append(" */\n"); result.append(String.format(" @ZAttr(id=%d)%n", ai.getId())); if (noMap) { String expiryParam = ai.isExpirable() ? ", com.zimbra.cs.ephemeral.EphemeralInput.Expiration expiration" : ""; if (ai.isEphemeral()) { switch (setterType) { case set: result.append(String.format( " public void %s(%s %s%s) throws com.zimbra.common.service.ServiceException {%n", methodName, javaType, name, expiryParam)); break; case add: if (ai.isDynamic()) { result.append(String.format( " public void %s(String dynamicComponent, %s %s%s) throws com.zimbra.common.service.ServiceException {%n", methodName, javaType, name, expiryParam)); } else { result.append(String.format( " public void %s(%s %s%s) throws com.zimbra.common.service.ServiceException {%n", methodName, javaType, name, expiryParam)); } break; case unset: result.append(String.format( " public void %s() throws com.zimbra.common.service.ServiceException {%n", methodName)); break; case remove: if (ai.isDynamic()) { result.append(String.format( " public void %s(String dynamicComponent, %s %s) throws com.zimbra.common.service.ServiceException {%n", methodName, javaType, name)); } else { result.append(String.format( " public void %s(%s %s) throws com.zimbra.common.service.ServiceException {%n", methodName, javaType, name)); } break; case purge: result.append(String.format( " public void %s() throws com.zimbra.common.service.ServiceException {%n", methodName)); break; case has: if (ai.isDynamic()) { result.append(String.format( " public boolean %s(String dynamicComponent) throws com.zimbra.common.service.ServiceException {%n", methodName)); } else { result.append(String.format( " public boolean %s() throws com.zimbra.common.service.ServiceException {%n", methodName)); } break; } } else { if (setterType != SetterType.unset) result.append(String.format( " public void %s(%s %s) throws com.zimbra.common.service.ServiceException {%n", methodName, javaType, name)); else result.append(String.format( " public void %s() throws com.zimbra.common.service.ServiceException {%n", methodName)); } if (!ai.isEphemeral()) { result.append( String.format(" HashMap<String,Object> attrs = new HashMap<String,Object>();%n")); } result.append(body); if (!ai.isEphemeral()) { result.append(String.format(" getProvisioning().modifyAttrs(this, attrs);%n")); } } else { if (setterType != SetterType.unset) result.append(String.format(" public %s %s(%s %s, %s attrs) {%n", mapType, methodName, javaType, name, mapType)); else result.append(String.format(" public %s %s(%s attrs) {%n", mapType, methodName, mapType)); result.append(String.format(" if (attrs == null) attrs = new HashMap<String,Object>();%n")); result.append(body); result.append(String.format(" return attrs;%n")); } result.append(String.format(" }%n")); } /** * * @param pw * @param optionValue * @throws ServiceException */ private void generateProvisioningConstants(String javaFile) throws ServiceException, IOException { List<String> list = new ArrayList<String>(getAttrs().keySet()); Collections.sort(list); StringBuilder result = new StringBuilder(); for (String a : list) { AttributeInfo ai = getAttrs().get(a.toLowerCase()); if (ai == null || ai.getType() != AttributeType.TYPE_ENUM) continue; generateEnum(result, ai); } for (String a : list) { AttributeInfo ai = getAttrs().get(a.toLowerCase()); if (ai == null) continue; result.append("\n /**\n"); if (ai.getDescription() != null) { result.append(FileGenUtil.wrapComments(StringUtil.escapeHtml(ai.getDescription()), 70, " * ")); result.append("\n"); } if (ai.getSince() != null) { result.append(" *\n"); result.append(String.format(" * @since ZCS %s%n", versionListAsString(ai.getSince()))); } result.append(" */\n"); result.append(String.format(" @ZAttr(id=%d)%n", ai.getId())); result.append( String.format(" public static final String A_%s = \"%s\";%n", ai.getName(), ai.getName())); } FileGenUtil.replaceJavaFile(javaFile, result.toString()); } private static void usage(String errmsg) { if (errmsg != null) { logger.error(errmsg); } HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("AttributeManagerUtil [options] where [options] are one of:", options); System.exit((errmsg == null) ? 0 : 1); } private static CommandLine parseArgs(String args[]) { StringBuffer gotCL = new StringBuffer("cmdline: "); for (int i = 0; i < args.length; i++) { gotCL.append("'").append(args[i]).append("' "); } //mLog.info(gotCL); CommandLineParser parser = new GnuParser(); CommandLine cl = null; try { cl = parser.parse(options, args); } catch (ParseException pe) { usage(pe.getMessage()); } if (cl.hasOption('h')) { usage(null); } return cl; } public static void main(String[] args) throws IOException, ServiceException { CliUtil.toolSetup(); CommandLine cl = parseArgs(args); if (!cl.hasOption('a')) usage("no action specified"); String actionStr = cl.getOptionValue('a'); Action action = null; try { action = Action.valueOf(actionStr); } catch (IllegalArgumentException iae) { usage("unknown action: " + actionStr); } AttributeManager am = null; if (action != Action.dump && action != Action.listAttrs) { if (!cl.hasOption('i')) usage("no input attribute xml files specified"); am = new AttributeManager(cl.getOptionValue('i')); if (am.hasErrors()) { ZimbraLog.misc.warn(am.getErrors()); System.exit(1); } } OutputStream os = System.out; if (cl.hasOption('o')) { os = new FileOutputStream(cl.getOptionValue('o')); } PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(os, "utf8"))); AttributeManagerUtil amu = new AttributeManagerUtil(am); switch (action) { case dump: LdapProv.getInst().dumpLdapSchema(pw); break; case generateDefaultCOSLdif: amu.generateDefaultCOSLdif(pw); break; case generateDefaultExternalCOSLdif: amu.generateDefaultExternalCOSLdif(pw); break; case generateGetters: amu.generateGetters(cl.getOptionValue('c'), cl.getOptionValue('r')); break; case generateGlobalConfigLdif: amu.generateGlobalConfigLdif(pw); break; case generateLdapSchema: if (!cl.hasOption('t')) { usage("no schema template specified"); } amu.generateLdapSchema(pw, cl.getOptionValue('t')); break; case generateMessageProperties: amu.generateMessageProperties(cl.getOptionValue('r')); break; case generateProvisioning: amu.generateProvisioningConstants(cl.getOptionValue('r')); break; case generateSchemaLdif: amu.generateSchemaLdif(pw); break; case listAttrs: amu.listAttrs(pw, cl.getOptionValues('c'), cl.getOptionValues('n'), cl.getOptionValues('f')); break; } pw.close(); } }