All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.nullschool.grains.generate.GrainGenerator Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2013 Cameron Beccario
 *
 * 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 net.nullschool.grains.generate;

import net.nullschool.grains.GrainSchema;
import net.nullschool.grains.GrainTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.IntrospectionException;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;

import static net.nullschool.grains.generate.NamingPolicy.Name;
import static net.nullschool.util.ThreadTools.newDaemonThreadFactory;
import static net.nullschool.util.ThreadTools.newNamingThreadFactory;
import static net.nullschool.collect.basic.BasicCollections.*;


/**
 * 2013-02-13

* * The grain generator. Searches for schemas and generates grain/builder/factory implementations for them. This * class uses an executor to generate these files concurrently on multiple threads. * * @author Cameron Beccario */ public class GrainGenerator implements Callable { private static final Logger log = LoggerFactory.getLogger(GrainGenerator.class); private final Configuration config; private final NamingPolicy namingPolicy; /** * An executor task that generates the contents of one file. */ private class GenerateTask implements Callable { private final GrainGeneratorDriver generator; private final Class schema; private final TemplateHandle handle; private final Path out; private GenerateTask(GrainGeneratorDriver generator, Class schema, TemplateHandle handle, Path out) { this.generator = generator; this.schema = schema; this.handle = handle; this.out = out; } private boolean contentsIdentical(byte[] bytes, Path filename) throws IOException { return Files.exists(filename) && Files.size(filename) == bytes.length && Arrays.equals(Files.readAllBytes(filename), bytes); } /** * Writes the result of code generation to disk as a file with the specified name. * * @param result the generated code. * @param filename the file to create. * @return true if file write succeeded. * @throws IOException if a filesystem error occurs. */ private boolean write(GenerationResult result, Path filename) throws IOException { if (!result.getErrors().isEmpty()) { // Code generation did not succeed, so would be incorrect to write the result to disk. log.error("[{}] While generating {}:", Thread.currentThread().getName(), filename); for (String error : result.getErrors()) { log.error("[{}] {}", Thread.currentThread().getName(), error); } throw new RuntimeException("Failed to write: " + filename); } // Encode the result with the requested charset. byte[] bytes = result.getText().getBytes(config.getCharset()); // Compare with what's already on disk, if anything. Let's not rewrite the file if its contents are // identical. This avoids sending noise to file system listeners. if (contentsIdentical(bytes, filename)) { log.debug("[{}] Unchanged: {}", Thread.currentThread().getName(), filename); return false; } else { log.debug("[{}] Generating {}", Thread.currentThread().getName(), filename); Files.write(filename, bytes); return true; } } @Override public Boolean call() throws IOException { GenerationResult result = generator.generate(schema, handle); return write(result, out); } } private String filename(Class schema, Name name) { return namingPolicy.getSimpleName(schema, name) + ".java"; } private List configureTasks(Class schema, GrainGeneratorDriver generator) throws ClassNotFoundException, IOException, IntrospectionException { Path outputDirectory = config.getOutput().resolve(GrainTools.targetPackageOf(schema).replace('.', '/')); Files.createDirectories(outputDirectory); List tasks = new ArrayList<>(); // Create a task for each of the three files to generate. tasks.add( new GenerateTask( generator, schema, TemplateHandles.newFactoryEnumTemplate(config), outputDirectory.resolve(filename(schema, Name.factory)))); // FooFactory.java tasks.add( new GenerateTask( generator, schema, TemplateHandles.newGrainInterfaceTemplate(config), outputDirectory.resolve(filename(schema, Name.grain)))); // FooGrain.java tasks.add( new GenerateTask( generator, schema, TemplateHandles.newBuilderInterfaceTemplate(config), outputDirectory.resolve(filename(schema, Name.builder)))); // FooBuilder.java return tasks; } public GrainGenerator(Configuration configuration) { this.config = Objects.requireNonNull(configuration); this.namingPolicy = new NamingPolicy(); } @Override public Void call() throws Exception { long start = System.currentTimeMillis(); // Create an executor. ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors(), newDaemonThreadFactory(newNamingThreadFactory("%s", Executors.defaultThreadFactory()))); GrainGeneratorDriver generator = new GrainGeneratorDriver(config, namingPolicy); List tasks = new ArrayList<>(); // Search for grain schemas. Set> schemas = new HashSet<>(); for (String searchPackage : config.getSearchPackages()) { schemas.addAll( new Reflector(searchPackage).findClassesAnnotatedWith(GrainSchema.class, config.getSearchLoader())); } // For each schema found, create a bunch of generate tasks and submit them to the executor. for (Class schema : schemas) { tasks.addAll(configureTasks(schema, generator)); } // Wait for all tasks to finish. int count = 0; for (Future future : executor.invokeAll(tasks)) { if (future.get()) { count++; } } // Done! log.info( "Generation took {} ms.{}", System.currentTimeMillis() - start, count > 0 ? " Generated " + count + " files." : " Nothing to generate - all sources up to date."); executor.shutdown(); return null; } /** * Used only for testing. */ public static void main(String[] args) throws Exception { Thread.currentThread().setName("main"); Configuration config = new Configuration(); config.setOutput(Paths.get(args[0])); config.setSearchPackages(asSet(Arrays.asList(args).subList(1, args.length))); new GrainGenerator(config).call(); Thread.sleep(10); // yeah, just so logging catches up. } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy