/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.giraph.compiling; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import org.apache.log4j.Logger; import com.google.common.base.Preconditions; import net.openhft.compiler.CachedCompiler; /** * Helper class for creating classes at runtime */ public class RuntimeClassGenerator { private static final Logger LOG = Logger.getLogger( RuntimeClassGenerator.class); /** * In order not to create multiple classes for exactly same Code generator * and String, this keeps all known classes */ private static final Map<ClassCodeGenerator, Map<String, Class<?>>> KNOWN_CLASSES = new HashMap<>(); /** Counter for number of created classes */ private static final AtomicInteger CREATED_CLASSES_COUNTER = new AtomicInteger(); protected RuntimeClassGenerator() { } public static synchronized <T> Class<T> getOrCreateClass( ClassCodeGenerator classCodeGenerator, String codeSnippet) { Map<String, Class<?>> knownClassCodeGenerators = KNOWN_CLASSES.get(classCodeGenerator); if (knownClassCodeGenerators == null) { knownClassCodeGenerators = new HashMap<>(); KNOWN_CLASSES.put(classCodeGenerator, knownClassCodeGenerators); } else { Class<?> generatedClass = knownClassCodeGenerators.get(codeSnippet); if (generatedClass != null) { if (LOG.isDebugEnabled()) { LOG.debug( "Reusing class for " + classCodeGenerator + " " + codeSnippet); } return (Class<T>) generatedClass; } } String packageName = classCodeGenerator.getClass().getPackage().getName(); String className = classCodeGenerator.getClass().getSimpleName() + CREATED_CLASSES_COUNTER.getAndIncrement(); String fullClassName = packageName + "." + className; String classCode = classCodeGenerator.generateCode( packageName, className, codeSnippet); if (LOG.isDebugEnabled()) { LOG.debug("Creating " + fullClassName + " with \n" + classCode); } Class<T> generatedClass = generateClass(fullClassName, classCode); knownClassCodeGenerators.put(codeSnippet, generatedClass); return generatedClass; } /** * Create class with a name, in the package and with source code. */ public static synchronized <T> Class<T> createClassOnce( String className, String packageName, String classCode) { return generateClass(packageName + "." + className, classCode); } private static <T> Class<T> generateClass( String fullClassName, String classCode) { try { Class<T> generatedClass = new CachedCompiler(null, null).loadFromJava( fullClassName, classCode); // Confirm that class was loaded into default class loader Preconditions.checkState(generatedClass == Class.forName(fullClassName)); return generatedClass; } catch (ClassNotFoundException e) { throw new IllegalArgumentException( "Couldn't be compile class " + fullClassName + " , with code: " + classCode, e); } } /** * For test */ static int getCreatedClassesCount() { return CREATED_CLASSES_COUNTER.get(); } }