Java tutorial
/** * Copyright (C) 2014 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License 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.ctriposs.rest4j.tools.idlgen; import com.ctriposs.dolphin.generator.GeneratorResult; import com.ctriposs.rest4j.common.RestConstants; import com.ctriposs.rest4j.internal.server.model.ResourceModel; import com.ctriposs.rest4j.internal.server.model.ResourceModelEncoder; import com.ctriposs.rest4j.internal.server.model.ResourceModelEncoder.DocsProvider; import com.ctriposs.rest4j.internal.server.model.Rest4JApiBuilder; import com.ctriposs.rest4j.restspec.ResourceSchema; import com.ctriposs.rest4j.restspec.RestSpecCodec; import com.ctriposs.rest4j.server.Rest4JConfig; import com.ctriposs.rest4j.server.util.FileClassNameScanner; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.io.output.NullWriter; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Given a set of package names, scans all Rest4J resource classes in the packages and generate corresponding * idl (.restspec.json) files. * */ public class Rest4JResourceModelExporter { private static final Logger log = LoggerFactory.getLogger(Rest4JResourceModelExporter.class); private static final RestSpecCodec _codec = new RestSpecCodec(); /** * @param apiName the name of the API * @param classpath classpath to to load the resources. this is purely for Javadoc Doclet {@link Rest4JDoclet} * @param sourcePaths paths to scan for resource Java source files. this is purely for Javadoc Doclet {@link Rest4JDoclet} * @param resourcePackages packages to scan for resources * @param outdir directory in which to output the IDL files * @return a result that includes collection of files generated and modified. Note: getSourceFiles() on the result * will always return an empty List as the code generation operates on classpaths and the ClassLoader and not files. * @throws IOException could be {@link java.io.FileNotFoundException} if unable to write the output file, * otherwise, {@link IOException} if failure happened when writing the output file */ public GeneratorResult export(String apiName, String[] classpath, String[] sourcePaths, String[] resourcePackages, String outdir) throws IOException { return export(apiName, classpath, sourcePaths, resourcePackages, null, outdir); } /** * @param apiName the name of the API * @param classpath classpath to to load the resources. this is purely for Javadoc Doclet {@link Rest4JDoclet} * @param sourcePaths paths to scan for resource Java source files. this is purely for Javadoc Doclet {@link Rest4JDoclet} * if both resourcePackages and resourceClasses is null, all classes defined in the directories will be scanned * @param resourcePackages packages to scan for resources * @param resourceClasses specific classes as resources * @param outdir directory in which to output the IDL files * @return a result that includes collection of files generated and modified. Note: getSourceFiles() on the result * will always return an empty List as the code generation operates on classpaths and the ClassLoader and not files. * @throws IOException could be {@link java.io.FileNotFoundException} if unable to write the output file, * otherwise, {@link IOException} if failure happened when writing the output file */ public GeneratorResult export(String apiName, String[] classpath, String[] sourcePaths, String[] resourcePackages, String[] resourceClasses, String outdir) throws IOException { return export(apiName, classpath, sourcePaths, resourcePackages, resourceClasses, outdir, Collections.<DocsProvider>emptyList()); } /** * @param apiName the name of the API * @param classpath classpath to to load the resources. this is purely for Javadoc Doclet {@link Rest4JDoclet} * @param sourcePaths paths to scan for resource Java source files. this is purely for Javadoc Doclet {@link Rest4JDoclet} * if both resourcePackages and resourceClasses is null, all classes defined in the directories will be scanned * @param resourcePackages packages to scan for resources * @param resourceClasses specific classes as resources * @param outdir directory in which to output the IDL files * @param additionalDocProviders names of additional classes in the classpath that implement DocsProvider, if empty, * only javadoc will be supported. * @return a result that includes collection of files generated and modified. Note: getSourceFiles() on the result * will always return an empty List as the code generation operates on classpaths and the ClassLoader and not files. * @throws IOException could be {@link java.io.FileNotFoundException} if unable to write the output file, * otherwise, {@link IOException} if failure happened when writing the output file */ public GeneratorResult export(String apiName, String[] classpath, String[] sourcePaths, String[] resourcePackages, String[] resourceClasses, String outdir, List<DocsProvider> additionalDocProviders) throws IOException { final Rest4JConfig config = new Rest4JConfig(); if (resourcePackages != null) { config.addResourcePackageNames(resourcePackages); } final Map<String, String> classFileNames = new HashMap<String, String>(); for (String path : sourcePaths) { classFileNames.putAll(FileClassNameScanner.scan(path)); } Collection<String> sourceFileNames = null; if (resourceClasses != null || resourcePackages == null) { if (resourceClasses != null) { config.addResourceClassNames(resourceClasses); sourceFileNames = new ArrayList<String>(resourceClasses.length); for (String resourceClass : resourceClasses) { final String resourceFileName = classFileNames.get(resourceClass); if (resourceFileName == null) { log.warn("Unable to find source file for class " + resourceClass + " . No documentation will be generated for it."); } else { sourceFileNames.add(resourceFileName); } } } else { config.addResourceClassNames(classFileNames.keySet()); sourceFileNames = classFileNames.values(); } } log.info("Executing Rest4J annotation processor..."); final Rest4JApiBuilder apiBuilder = new Rest4JApiBuilder(config); final Map<String, ResourceModel> rootResourceMap = apiBuilder.build(); if (rootResourceMap.isEmpty()) { return new Result(); } // We always include the doc provider for javadoc DocsProvider javadocProvider = new DocletDocsProvider(apiName, classpath, sourcePaths, resourcePackages); DocsProvider docsProvider; if (additionalDocProviders == null || additionalDocProviders.isEmpty()) { docsProvider = javadocProvider; } else { // dynamically load doc providers for additional language, if available List<DocsProvider> languageSpecificDocsProviders = new ArrayList<DocsProvider>(); languageSpecificDocsProviders.add(javadocProvider); languageSpecificDocsProviders .addAll(MultiLanguageDocsProvider.loadExternalProviders(additionalDocProviders)); docsProvider = new MultiLanguageDocsProvider(languageSpecificDocsProviders); } log.info("Registering source files with doc providers..."); docsProvider.registerSourceFiles(classFileNames.values()); log.info("Exporting IDL files..."); final GeneratorResult result = generateIDLFiles(apiName, outdir, rootResourceMap, docsProvider); log.info("Done!"); return result; } /** * @param apiName the name of the API * @param sourcePaths paths to scan for resource Java source files * @param resourcePackages packages to scan for resources * @param resourceClasses specific classes as resources * @param outdir directory in which to output the IDL files * @param split if true, IDL will be split into multiple files, one per root resource * @return a result that includes collection of files generated and modified. Note: getSourceFiles() on the result * will always return an empty List as the code generation operates on classpaths and the ClassLoader and not files. * @throws IOException could be {@link java.io.FileNotFoundException} if unable to write the output file, * otherwise, {@link IOException} if failure happened when writing the output file */ @Deprecated public GeneratorResult export(String apiName, String[] sourcePaths, String[] resourcePackages, String[] resourceClasses, String outdir, boolean split) throws IOException { if (!split) { throw new IllegalStateException("Resource models should always be exported in split mode"); } return export(apiName, null, sourcePaths, resourcePackages, resourceClasses, outdir); } private GeneratorResult generateIDLFiles(String apiName, String outdir, Map<String, ResourceModel> rootResourceMap, DocsProvider docsProvider) throws IOException { Result result = new Result(); final File outdirFile = new File(outdir); if (!outdirFile.exists()) { if (!outdirFile.mkdirs()) { throw new RuntimeException("Output directory '" + outdir + "' could not be created!"); } } if (!outdirFile.isDirectory()) { throw new RuntimeException("Output directory '" + outdir + "' is not a directory"); } if (!outdirFile.canRead() || !outdirFile.canWrite()) { throw new RuntimeException("Output directory '" + outdir + "' must be writeable"); } final ResourceModelEncoder encoder = new ResourceModelEncoder(docsProvider); final List<ResourceSchema> rootResourceNodes = new ArrayList<ResourceSchema>(); for (Entry<String, ResourceModel> entry : rootResourceMap.entrySet()) { final ResourceSchema rootResourceNode = encoder.buildResourceSchema(entry.getValue()); rootResourceNodes.add(rootResourceNode); } for (ResourceSchema rootResourceNode : rootResourceNodes) { String fileName = rootResourceNode.getName(); if (rootResourceNode.getNamespace() != null) { final String namespace = rootResourceNode.getNamespace(); fileName = namespace + "." + fileName; } if (apiName != null && !apiName.isEmpty()) { fileName = apiName + "-" + fileName; } File writtenFile = writeIDLFile(outdirFile, fileName, rootResourceNode); result.addModifiedFile(writtenFile); result.addTargetFile(writtenFile); } return result; } class Result implements GeneratorResult { private List<File> targetFiles = new ArrayList<File>(); private List<File> modifiedFiles = new ArrayList<File>(); public void addTargetFile(File file) { targetFiles.add(file); } public void addModifiedFile(File file) { modifiedFiles.add(file); } @Override public Collection<File> getSourceFiles() { throw new UnsupportedOperationException( "getSourceFiles is not supported for the Rest4JResourceModelExporter"); } @Override public Collection<File> getTargetFiles() { return targetFiles; } @Override public Collection<File> getModifiedFiles() { return modifiedFiles; } } private File writeIDLFile(File outdirFile, String fileName, ResourceSchema rootResourceNode) throws IOException { fileName += RestConstants.RESOURCE_MODEL_FILENAME_EXTENSION; log.info("Writing file '" + fileName + '\''); final File file = new File(outdirFile, fileName); _codec.writeResourceSchema(rootResourceNode, new FileOutputStream(file)); return file; } }