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 */
021 package com.unboundid.ldap.sdk.persist;
022
023
024
025 import java.io.File;
026 import java.io.OutputStream;
027 import java.io.Serializable;
028 import java.util.LinkedHashMap;
029 import java.util.List;
030
031 import com.unboundid.asn1.ASN1OctetString;
032 import com.unboundid.ldap.sdk.Attribute;
033 import com.unboundid.ldap.sdk.Entry;
034 import com.unboundid.ldap.sdk.Modification;
035 import com.unboundid.ldap.sdk.ModificationType;
036 import com.unboundid.ldap.sdk.ResultCode;
037 import com.unboundid.ldap.sdk.Version;
038 import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
039 import com.unboundid.ldap.sdk.schema.ObjectClassDefinition;
040 import com.unboundid.ldif.LDIFModifyChangeRecord;
041 import com.unboundid.ldif.LDIFRecord;
042 import com.unboundid.ldif.LDIFWriter;
043 import com.unboundid.util.CommandLineTool;
044 import com.unboundid.util.Mutable;
045 import com.unboundid.util.ThreadSafety;
046 import com.unboundid.util.ThreadSafetyLevel;
047 import com.unboundid.util.args.ArgumentException;
048 import com.unboundid.util.args.ArgumentParser;
049 import com.unboundid.util.args.BooleanArgument;
050 import com.unboundid.util.args.FileArgument;
051 import com.unboundid.util.args.StringArgument;
052
053 import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
054 import static com.unboundid.util.Debug.*;
055 import 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)
068 public 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 }