001/* 002 * Copyright 2009-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2014 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.persist; 022 023 024 025import java.io.File; 026import java.io.OutputStream; 027import java.io.Serializable; 028import java.util.LinkedHashMap; 029import java.util.List; 030 031import com.unboundid.asn1.ASN1OctetString; 032import com.unboundid.ldap.sdk.Attribute; 033import com.unboundid.ldap.sdk.Entry; 034import com.unboundid.ldap.sdk.Modification; 035import com.unboundid.ldap.sdk.ModificationType; 036import com.unboundid.ldap.sdk.ResultCode; 037import com.unboundid.ldap.sdk.Version; 038import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 039import com.unboundid.ldap.sdk.schema.ObjectClassDefinition; 040import com.unboundid.ldif.LDIFModifyChangeRecord; 041import com.unboundid.ldif.LDIFRecord; 042import com.unboundid.ldif.LDIFWriter; 043import com.unboundid.util.CommandLineTool; 044import com.unboundid.util.Mutable; 045import com.unboundid.util.ThreadSafety; 046import com.unboundid.util.ThreadSafetyLevel; 047import com.unboundid.util.args.ArgumentException; 048import com.unboundid.util.args.ArgumentParser; 049import com.unboundid.util.args.BooleanArgument; 050import com.unboundid.util.args.FileArgument; 051import com.unboundid.util.args.StringArgument; 052 053import static com.unboundid.ldap.sdk.persist.PersistMessages.*; 054import static com.unboundid.util.Debug.*; 055import static com.unboundid.util.StaticUtils.*; 056 057 058 059/** 060 * This class provides a tool which can be used to generate LDAP attribute 061 * type and object class definitions which may be used to store objects 062 * created from a specified Java class. The given class must be included in the 063 * classpath of the JVM used to invoke the tool, and must be marked with the 064 * {@link LDAPObject} annotation. 065 */ 066@Mutable() 067@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 068public final class GenerateSchemaFromSource 069 extends CommandLineTool 070 implements Serializable 071{ 072 /** 073 * The serial version UID for this serializable class. 074 */ 075 private static final long serialVersionUID = 1029934829295836935L; 076 077 078 079 // Arguments used by this tool. 080 private BooleanArgument modifyFormatArg; 081 private FileArgument outputFileArg; 082 private StringArgument classNameArg; 083 084 085 086 /** 087 * Parse the provided command line arguments and perform the appropriate 088 * processing. 089 * 090 * @param args The command line arguments provided to this program. 091 */ 092 public static void main(final String[] args) 093 { 094 final ResultCode resultCode = main(args, System.out, System.err); 095 if (resultCode != ResultCode.SUCCESS) 096 { 097 System.exit(resultCode.intValue()); 098 } 099 } 100 101 102 103 /** 104 * Parse the provided command line arguments and perform the appropriate 105 * processing. 106 * 107 * @param args The command line arguments provided to this program. 108 * @param outStream The output stream to which standard out should be 109 * written. It may be {@code null} if output should be 110 * suppressed. 111 * @param errStream The output stream to which standard error should be 112 * written. It may be {@code null} if error messages 113 * should be suppressed. 114 * 115 * @return A result code indicating whether the processing was successful. 116 */ 117 public static ResultCode main(final String[] args, 118 final OutputStream outStream, 119 final OutputStream errStream) 120 { 121 final GenerateSchemaFromSource tool = 122 new GenerateSchemaFromSource(outStream, errStream); 123 return tool.runTool(args); 124 } 125 126 127 128 /** 129 * Creates a new instance of this tool. 130 * 131 * @param outStream The output stream to which standard out should be 132 * written. It may be {@code null} if output should be 133 * suppressed. 134 * @param errStream The output stream to which standard error should be 135 * written. It may be {@code null} if error messages 136 * should be suppressed. 137 */ 138 public GenerateSchemaFromSource(final OutputStream outStream, 139 final OutputStream errStream) 140 { 141 super(outStream, errStream); 142 } 143 144 145 146 /** 147 * {@inheritDoc} 148 */ 149 @Override() 150 public String getToolName() 151 { 152 return "generate-schema-from-source"; 153 } 154 155 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override() 161 public String getToolDescription() 162 { 163 return INFO_GEN_SCHEMA_TOOL_DESCRIPTION.get(); 164 } 165 166 167 168 /** 169 * Retrieves the version string for this tool. 170 * 171 * @return The version string for this tool. 172 */ 173 @Override() 174 public String getToolVersion() 175 { 176 return Version.NUMERIC_VERSION_STRING; 177 } 178 179 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override() 185 public void addToolArguments(final ArgumentParser parser) 186 throws ArgumentException 187 { 188 classNameArg = new StringArgument('c', "javaClass", true, 1, 189 INFO_GEN_SCHEMA_VALUE_PLACEHOLDER_CLASS.get(), 190 INFO_GEN_SCHEMA_ARG_DESCRIPTION_JAVA_CLASS.get()); 191 parser.addArgument(classNameArg); 192 193 outputFileArg = new FileArgument('f', "outputFile", true, 1, 194 INFO_GEN_SCHEMA_VALUE_PLACEHOLDER_PATH.get(), 195 INFO_GEN_SCHEMA_ARG_DESCRIPTION_OUTPUT_FILE.get(), false, true, true, 196 false); 197 parser.addArgument(outputFileArg); 198 199 modifyFormatArg = new BooleanArgument('m', "modifyFormat", 200 INFO_GEN_SCHEMA_ARG_DESCRIPTION_MODIFY_FORMAT.get()); 201 parser.addArgument(modifyFormatArg); 202 } 203 204 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override() 210 public ResultCode doToolProcessing() 211 { 212 // Load the specified Java class. 213 final String className = classNameArg.getValue(); 214 final Class<?> targetClass; 215 try 216 { 217 targetClass = Class.forName(className); 218 } 219 catch (Exception e) 220 { 221 debugException(e); 222 err(ERR_GEN_SCHEMA_CANNOT_LOAD_CLASS.get(className)); 223 return ResultCode.PARAM_ERROR; 224 } 225 226 227 // Create an LDAP persister for the class and use it to ensure that the 228 // class is valid. 229 final LDAPPersister<?> persister; 230 try 231 { 232 persister = LDAPPersister.getInstance(targetClass); 233 } 234 catch (Exception e) 235 { 236 debugException(e); 237 err(ERR_GEN_SCHEMA_INVALID_CLASS.get(className, getExceptionMessage(e))); 238 return ResultCode.LOCAL_ERROR; 239 } 240 241 242 // Use the persister to generate the attribute type and object class 243 // definitions. 244 final List<AttributeTypeDefinition> attrTypes; 245 try 246 { 247 attrTypes = persister.constructAttributeTypes(); 248 } 249 catch (Exception e) 250 { 251 debugException(e); 252 err(ERR_GEN_SCHEMA_ERROR_CONSTRUCTING_ATTRS.get(className, 253 getExceptionMessage(e))); 254 return ResultCode.LOCAL_ERROR; 255 } 256 257 final List<ObjectClassDefinition> objectClasses; 258 try 259 { 260 objectClasses = persister.constructObjectClasses(); 261 } 262 catch (Exception e) 263 { 264 debugException(e); 265 err(ERR_GEN_SCHEMA_ERROR_CONSTRUCTING_OCS.get(className, 266 getExceptionMessage(e))); 267 return ResultCode.LOCAL_ERROR; 268 } 269 270 271 // Convert the attribute type and object class definitions into their 272 // appropriate string representations. 273 int i=0; 274 final ASN1OctetString[] attrTypeValues = 275 new ASN1OctetString[attrTypes.size()]; 276 for (final AttributeTypeDefinition d : attrTypes) 277 { 278 attrTypeValues[i++] = new ASN1OctetString(d.toString()); 279 } 280 281 i=0; 282 final ASN1OctetString[] ocValues = 283 new ASN1OctetString[objectClasses.size()]; 284 for (final ObjectClassDefinition d : objectClasses) 285 { 286 ocValues[i++] = new ASN1OctetString(d.toString()); 287 } 288 289 290 // Construct the LDIF record to be written. 291 final LDIFRecord schemaRecord; 292 if (modifyFormatArg.isPresent()) 293 { 294 schemaRecord = new LDIFModifyChangeRecord("cn=schema", 295 new Modification(ModificationType.ADD, "attributeTypes", 296 attrTypeValues), 297 new Modification(ModificationType.ADD, "objectClasses", ocValues)); 298 } 299 else 300 { 301 schemaRecord = new Entry("cn=schema", 302 new Attribute("objectClass", "top", "ldapSubentry", "subschema"), 303 new Attribute("cn", "schema"), 304 new Attribute("attributeTypes", attrTypeValues), 305 new Attribute("objectClasses", ocValues)); 306 } 307 308 309 // Write the schema entry to the specified file. 310 final File outputFile = outputFileArg.getValue(); 311 try 312 { 313 final LDIFWriter ldifWriter = new LDIFWriter(outputFile); 314 ldifWriter.writeLDIFRecord(schemaRecord); 315 ldifWriter.close(); 316 } 317 catch (final Exception e) 318 { 319 debugException(e); 320 err(ERR_GEN_SCHEMA_CANNOT_WRITE_SCHEMA.get(outputFile.getAbsolutePath(), 321 getExceptionMessage(e))); 322 return ResultCode.LOCAL_ERROR; 323 } 324 325 326 return ResultCode.SUCCESS; 327 } 328 329 330 331 /** 332 * {@inheritDoc} 333 */ 334 @Override() 335 public LinkedHashMap<String[],String> getExampleUsages() 336 { 337 final LinkedHashMap<String[],String> examples = 338 new LinkedHashMap<String[],String>(1); 339 340 final String[] args = 341 { 342 "--javaClass", "com.example.MyClass", 343 "--outputFile", "MyClass-schema.ldif" 344 }; 345 examples.put(args, INFO_GEN_SCHEMA_EXAMPLE_1.get()); 346 347 return examples; 348 } 349}