Java tutorial
/* * ****************************************************************************** * Copyright 2014-2015 Spectra Logic Corporation. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"). You may not use * this file except in compliance with the License. A copy of the License is located at * * http://www.apache.org/licenses/LICENSE-2.0 * * or in the "license" file accompanying this file. * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * **************************************************************************** */ package com.spectralogic.ds3autogen.java; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.spectralogic.ds3autogen.api.CodeGenerator; import com.spectralogic.ds3autogen.api.FileUtils; import com.spectralogic.ds3autogen.api.models.apispec.Ds3ApiSpec; import com.spectralogic.ds3autogen.api.models.apispec.Ds3Request; import com.spectralogic.ds3autogen.api.models.apispec.Ds3Type; import com.spectralogic.ds3autogen.api.models.docspec.Ds3DocSpec; import com.spectralogic.ds3autogen.java.converters.ClientConverter; import com.spectralogic.ds3autogen.java.generators.requestmodels.*; import com.spectralogic.ds3autogen.java.generators.responsemodels.*; import com.spectralogic.ds3autogen.java.generators.responseparser.*; import com.spectralogic.ds3autogen.java.generators.typemodels.*; import com.spectralogic.ds3autogen.java.helpers.JavaHelper; import com.spectralogic.ds3autogen.java.models.*; import com.spectralogic.ds3autogen.utils.Ds3TypeClassificationUtil; import com.spectralogic.ds3autogen.utils.Helper; import freemarker.template.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.file.Path; import java.nio.file.Paths; import static com.spectralogic.ds3autogen.java.models.Constants.*; import static com.spectralogic.ds3autogen.java.utils.JavaModuleUtil.getCommandPackage; import static com.spectralogic.ds3autogen.utils.ConverterUtil.*; import static com.spectralogic.ds3autogen.utils.Ds3RequestClassificationUtil.*; import static com.spectralogic.ds3autogen.utils.Ds3TypeClassificationUtil.*; /** * Generates Java SDK code based on the contents of a Ds3ApiSpec. * * Generated Code: * Request handlers * Response handlers * Ds3Client * Ds3ClientImpl * Models */ public class JavaCodeGenerator implements CodeGenerator { private static final Logger LOG = LoggerFactory.getLogger(JavaCodeGenerator.class); private static final Path baseProjectPath = Paths.get("ds3-sdk/src/main/java/"); private final Configuration config = new Configuration(Configuration.VERSION_2_3_23); private FileUtils fileUtils; private Path destDir; public JavaCodeGenerator() throws TemplateModelException { config.setDefaultEncoding("UTF-8"); config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); config.setClassForTemplateLoading(JavaCodeGenerator.class, "/tmpls"); config.setSharedVariable("javaHelper", JavaHelper.getInstance()); config.setSharedVariable("helper", Helper.getInstance()); } @Override public void generate(final Ds3ApiSpec spec, final FileUtils fileUtils, final Path destDir, final Ds3DocSpec docSpec) throws IOException { this.fileUtils = fileUtils; this.destDir = destDir; try { final ImmutableList<Ds3Request> requests = spec.getRequests(); final ImmutableMap<String, Ds3Type> types = removeUnusedTypes(spec.getTypes(), spec.getRequests()); generateCommands(requests, types, docSpec); } catch (final TemplateException e) { e.printStackTrace(); } } /** * Generates all code associated with the Ds3ApiSpec * @throws IOException * @throws TemplateException */ private void generateCommands(final ImmutableList<Ds3Request> requests, final ImmutableMap<String, Ds3Type> types, final Ds3DocSpec docSpec) throws IOException, TemplateException { generateAllRequests(requests, docSpec); generateAllModels(types); generateClient(requests, docSpec); } /** * Generates the Models described within the Ds3ApiSpec that are being used * by at least one request * @throws IOException * @throws TemplateException */ private void generateAllModels(final ImmutableMap<String, Ds3Type> types) throws IOException, TemplateException { if (isEmpty(types)) { LOG.info("There were no models to generate"); return; } for (final Ds3Type ds3Type : types.values()) { generateModel(ds3Type); } } /** * Generates code for a Model from a Ds3Type * @param ds3Type A Ds3Type * @throws IOException * @throws TemplateException */ private void generateModel(final Ds3Type ds3Type) throws IOException, TemplateException { final Template modelTmpl = getModelTemplate(ds3Type); final Model model = toModel(ds3Type, getModelPackage()); final Path modelPath = toModelFilePath(model.getName()); LOG.info("Getting outputstream for file: {}", modelPath.toString()); try (final OutputStream outStream = fileUtils.getOutputFile(modelPath); final Writer writer = new OutputStreamWriter(outStream)) { modelTmpl.process(model, writer); } } /** * Converts a Ds3Type into a Model */ private Model toModel(final Ds3Type ds3Type, final String packageName) { final TypeModelGenerator<?> modelGenerator = getModelGenerator(ds3Type); return modelGenerator.generate(ds3Type, packageName); } /** * Retrieves the associated type model generator for the specified Ds3TYpe */ private TypeModelGenerator<?> getModelGenerator(final Ds3Type ds3Type) { if (isChecksumType(ds3Type)) { return new ChecksumTypeGenerator(); } if (Ds3TypeClassificationUtil.isJobsApiBean(ds3Type)) { return new JobsApiBeanTypeGenerator(); } if (isCommonPrefixesType(ds3Type)) { return new CommonPrefixGenerator(); } return new BaseTypeGenerator(); } /** * Gets the Model template that is used to generate the given Ds3Type content * @param ds3Type A Ds3Type * @return The appropriate template to generate the given Ds3Type * @throws IOException */ private Template getModelTemplate(final Ds3Type ds3Type) throws IOException { if (isHttpErrorType(ds3Type)) { return config.getTemplate("models/http_error_template.ftl"); } if (isChecksumType(ds3Type)) { return config.getTemplate("models/checksum_type_template.ftl"); } if (isS3Object(ds3Type)) { return config.getTemplate("models/s3object_model_template.ftl"); } if (isBulkObject(ds3Type)) { return config.getTemplate("models/bulk_object_template.ftl"); } if (hasContent(ds3Type.getEnumConstants())) { return config.getTemplate("models/enum_model_template.ftl"); } if (hasContent(ds3Type.getElements())) { return config.getTemplate("models/model_template.ftl"); } throw new IllegalArgumentException("Type must have Elements and/or EnumConstants"); } /** * Determines if a given Ds3Type is a BulkObject */ private boolean isBulkObject(final Ds3Type ds3type) { return ds3type.getName().endsWith(".BulkObject"); } /** * Determines if a given Ds3Type is the S3Object */ private boolean isS3Object(final Ds3Type ds3type) { return ds3type.getName().endsWith(".S3Object"); } /** * Gets the package name for where the Models will be generated * @return The package name of where the Models are going to be generated */ private String getModelPackage() { return ROOT_PACKAGE_PATH + MODELS_PACKAGE; } /** * Converts a Model name into a Model file path * @param modelName The name of a Model * @return The file path of a Model */ private Path toModelFilePath(final String modelName) { return destDir.resolve(baseProjectPath .resolve(Paths.get(getModelPackage().replace(".", "/") + "/" + modelName + ".java"))); } /** * Generates the Request Handler, Response Handler, and Response Parser * for all specified requests * @throws IOException * @throws TemplateException */ private void generateAllRequests(final ImmutableList<Ds3Request> requests, final Ds3DocSpec docSpec) throws IOException, TemplateException { if (isEmpty(requests)) { LOG.info("There were no requests to generate"); return; } for (final Ds3Request request : requests) { generateRequest(request, docSpec); generateResponse(request); generateResponseParser(request); } } /** * Generates the Response Parser code for the specified Ds3Request */ private void generateResponseParser(final Ds3Request ds3Request) throws IOException, TemplateException { final Template tmpl = getResponseParserTemplate(ds3Request); final ResponseParser responseParser = toResponseParser(ds3Request); final Path responsePath = toResponseParserPath(responseParser.getName()); LOG.info("Getting outputstream for file: {}", responsePath.toString()); try (final OutputStream outStream = fileUtils.getOutputFile(responsePath); final Writer writer = new OutputStreamWriter(outStream)) { tmpl.process(responseParser, writer); } } /** * Converts a file name into the path containing said file within the client path * @param fileName The name of a file * @return The client path to the given file */ protected Path toResponseParserPath(final String fileName) { return destDir.resolve(baseProjectPath .resolve(Paths.get(RESPONSE_PARSER_PACKAGE_PATH.replace(".", "/") + "/" + fileName + ".java"))); } /** * Retrieves the response parser template used to generate the specified request */ protected Template getResponseParserTemplate(final Ds3Request ds3Request) throws IOException { if (isBulkRequest(ds3Request)) { return config.getTemplate("responseparser/bulk_response_parser.ftl"); } if (isAllocateJobChunkRequest(ds3Request)) { return config.getTemplate("responseparser/allocate_job_chunk_parser.ftl"); } if (isHeadObjectRequest(ds3Request)) { return config.getTemplate("responseparser/head_object_parser.ftl"); } if (isGetObjectAmazonS3Request(ds3Request)) { return config.getTemplate("responseparser/get_object_parser.ftl"); } if (isGetJobChunksReadyForClientProcessingRequest(ds3Request)) { return config.getTemplate("responseparser/get_job_chunks_ready_parser.ftl"); } return config.getTemplate("responseparser/response_parser_template.ftl"); } /** * Converts a Ds3Request into a Response Parser model */ protected static ResponseParser toResponseParser(final Ds3Request ds3Request) { final ResponseParserGenerator<?> generator = getResponseParserGenerator(ds3Request); return generator.generate(ds3Request, RESPONSE_PARSER_PACKAGE_PATH); } /** * Retrieves the response parser generator used to generate the specified request */ protected static ResponseParserGenerator<?> getResponseParserGenerator(final Ds3Request ds3Request) { if (isHeadBucketRequest(ds3Request)) { return new HeadBucketParserGenerator(); } if (isAllocateJobChunkRequest(ds3Request)) { return new AllocateJobChunkParserGenerator(); } if (isHeadObjectRequest(ds3Request)) { return new HeadObjectParserGenerator(); } if (isGetObjectAmazonS3Request(ds3Request)) { return new GetObjectParserGenerator(); } if (isGetJobChunksReadyForClientProcessingRequest(ds3Request)) { return new GetJobChunksReadyParserGenerator(); } return new BaseResponseParserGenerator(); } /** * Generates the Client and ClientImpl code that contains all non-SpectraInternal requests * that are described within the Ds3ApiSpec * @throws IOException * @throws TemplateException */ private void generateClient(final ImmutableList<Ds3Request> requests, final Ds3DocSpec docSpec) throws IOException, TemplateException { if (isEmpty(requests)) { LOG.info("Not generating client: no requests."); return; } final Template clientTmpl = config.getTemplate("client/ds3client_template.ftl"); final Client client = ClientConverter.toClient(requests, ROOT_PACKAGE_PATH, docSpec); final Path clientPath = toClientPath("Ds3Client.java"); LOG.info("Getting outputstream for file: {}", clientPath.toString()); try (final OutputStream outStream = fileUtils.getOutputFile(clientPath); final Writer writer = new OutputStreamWriter(outStream)) { clientTmpl.process(client, writer); } final Template clientImplTmpl = config.getTemplate("client/ds3client_impl_template.ftl"); final Path clientImplPath = toClientPath("Ds3ClientImpl.java"); LOG.info("Getting outputstream for file: {}", clientPath.toString()); try (final OutputStream outStream = fileUtils.getOutputFile(clientImplPath); final Writer writer = new OutputStreamWriter(outStream)) { clientImplTmpl.process(client, writer); } } /** * Converts a file name into the path containing said file within the client path * @param fileName The name of a file * @return The client path to the given file */ private Path toClientPath(final String fileName) { return destDir .resolve(baseProjectPath.resolve(Paths.get(ROOT_PACKAGE_PATH.replace(".", "/") + "/" + fileName))); } /** * Generates the code for the Response handler described in a Ds3Request * @param ds3Request A Ds3Request * @throws IOException * @throws TemplateException */ private void generateResponse(final Ds3Request ds3Request) throws IOException, TemplateException { final Template tmpl = getResponseTemplate(ds3Request); final Response response = toResponse(ds3Request); final Path responsePath = getPathFromPackage(ds3Request, response.getName()); LOG.info("Getting outputstream for file: {}", responsePath.toString()); try (final OutputStream outStream = fileUtils.getOutputFile(responsePath); final Writer writer = new OutputStreamWriter(outStream)) { tmpl.process(response, writer); } } /** * Converts a Ds3Request into a Response model * @param ds3Request A Ds3Request * @return A Response */ private static Response toResponse(final Ds3Request ds3Request) { final ResponseModelGenerator<?> modelGenerator = getResponseGenerator(ds3Request); return modelGenerator.generate(ds3Request, getCommandPackage(ds3Request)); } /** * Retrieves the associated response generator for the specified Ds3Request */ protected static ResponseModelGenerator<?> getResponseGenerator(final Ds3Request ds3Request) { if (isHeadObjectRequest(ds3Request)) { return new HeadObjectResponseGenerator(); } if (isHeadBucketRequest(ds3Request)) { return new HeadBucketResponseGenerator(); } if (isAllocateJobChunkRequest(ds3Request) || isGetJobChunksReadyForClientProcessingRequest(ds3Request)) { return new RetryAfterResponseGenerator(); } if (isBulkRequest(ds3Request)) { return new BulkResponseGenerator(); } if (isGetObjectAmazonS3Request(ds3Request)) { return new GetObjectResponseGenerator(); } //This should be the last test so that it does not overwrite any special cased generators if (supportsPaginationRequest(ds3Request)) { return new PaginationResponseGenerator(); } return new BaseResponseGenerator(); } /** * Gets the Response template that is used to generate the given Ds3Request's * Response handler * @param ds3Request A Ds3Request * @return The appropriate template to generate the required Response * @throws IOException */ private Template getResponseTemplate(final Ds3Request ds3Request) throws IOException { if (isHeadObjectRequest(ds3Request)) { return config.getTemplate("response/head_object_response.ftl"); } if (isHeadBucketRequest(ds3Request)) { return config.getTemplate("response/head_bucket_response.ftl"); } if (isAllocateJobChunkRequest(ds3Request)) { return config.getTemplate("response/allocate_chunk_response.ftl"); } if (isGetJobChunksReadyForClientProcessingRequest(ds3Request)) { return config.getTemplate("response/chunks_ready_response.ftl"); } if (isBulkRequest(ds3Request)) { return config.getTemplate("response/bulk_response.ftl"); } //This should be the last test so that it does not overwrite any special cased templates if (supportsPaginationRequest(ds3Request)) { return config.getTemplate("response/pagination_response.ftl"); } return config.getTemplate("response/response_template.ftl"); } /** * Generates the code for the Request handler described in a Ds3Request * @param ds3Request A Ds3Request * @throws IOException * @throws TemplateException */ private void generateRequest(final Ds3Request ds3Request, final Ds3DocSpec docSpec) throws IOException, TemplateException { final Template tmpl = getRequestTemplate(ds3Request); final Request request = toRequest(ds3Request, docSpec); final Path requestPath = getPathFromPackage(ds3Request, request.getName()); LOG.info("Getting outputstream for file: {}", requestPath.toString()); try (final OutputStream outStream = fileUtils.getOutputFile(requestPath); final Writer writer = new OutputStreamWriter(outStream)) { tmpl.process(request, writer); } } /** * Returns the file system path for a request given it's package * @param ds3Request A Ds3Request * @param fileName The file name to be given to this Ds3Request * @return The system path to this Ds3Request's generated code */ private Path getPathFromPackage(final Ds3Request ds3Request, final String fileName) { return destDir.resolve(baseProjectPath .resolve(Paths.get(getCommandPackage(ds3Request).replace(".", "/") + "/" + fileName + ".java"))); } /** * Converts a Ds3Request into a Request model * @param ds3Request A Ds3Request * @return A Request model */ private Request toRequest(final Ds3Request ds3Request, final Ds3DocSpec docSpec) { final RequestModelGenerator<?> modelGenerator = getRequestGenerator(ds3Request); return modelGenerator.generate(ds3Request, getCommandPackage(ds3Request), docSpec); } /** * Retrieves the associated request generator for the specified Ds3Request */ protected static RequestModelGenerator<?> getRequestGenerator(final Ds3Request ds3Request) { if (hasStringRequestPayload(ds3Request)) { return new StringRequestPayloadGenerator(); } if (isBulkRequest(ds3Request)) { return new BulkRequestGenerator(); } if (hasListObjectsRequestPayload(ds3Request)) { return new ObjectsRequestPayloadGenerator(); } if (isCreateObjectRequest(ds3Request)) { return new CreateObjectRequestGenerator(); } if (isCreateNotificationRequest(ds3Request)) { return new CreateNotificationRequestGenerator(); } if ((isGetNotificationRequest(ds3Request) || isDeleteNotificationRequest(ds3Request)) && ds3Request.includeIdInPath()) { return new NotificationRequestGenerator(); } if (isGetObjectRequest(ds3Request)) { return new GetObjectRequestGenerator(); } if (isMultiFileDeleteRequest(ds3Request)) { return new MultiFileDeleteRequestGenerator(); } if (isCreateMultiPartUploadPartRequest(ds3Request)) { return new StreamRequestPayloadGenerator(); } if (isCompleteMultiPartUploadRequest(ds3Request)) { return new CompleteMultipartUploadRequestGenerator(); } return new BaseRequestGenerator(); } /** * Gets the appropriate template that will generate the code for this * Ds3Request's request handler * @param ds3Request A Ds3Request * @return The appropriate template to generate the required Request * @throws IOException */ private Template getRequestTemplate(final Ds3Request ds3Request) throws IOException { if (isBulkRequest(ds3Request)) { return config.getTemplate("request/bulk_request_template.ftl"); } else if (hasStringRequestPayload(ds3Request)) { return config.getTemplate("request/request_with_string_payload_template.ftl"); } else if (hasListObjectsRequestPayload(ds3Request)) { return config.getTemplate("request/objects_request_payload_request_template.ftl"); } else if (isMultiFileDeleteRequest(ds3Request)) { return config.getTemplate("request/multi_file_delete_request_template.ftl"); } else if (isGetObjectRequest(ds3Request)) { return config.getTemplate("request/get_object_template.ftl"); } else if (isCreateObjectRequest(ds3Request)) { return config.getTemplate("request/create_object_template.ftl"); } else if (isDeleteNotificationRequest(ds3Request)) { return config.getTemplate("request/delete_notification_request_template.ftl"); } else if (isCreateNotificationRequest(ds3Request)) { return config.getTemplate("request/create_notification_request_template.ftl"); } else if (isGetNotificationRequest(ds3Request) && ds3Request.includeIdInPath()) { return config.getTemplate("request/get_notification_request_template.ftl"); } else if (isGetJobRequest(ds3Request)) { return config.getTemplate("request/get_job_request_template.ftl"); } else if (isCompleteMultiPartUploadRequest(ds3Request)) { return config.getTemplate("request/complete_multipart_upload_template.ftl"); } return config.getTemplate("request/request_template.ftl"); } }