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

org.granite.client.messaging.JavaResourceAccessor Maven / Gradle / Ivy

/*
  GRANITE DATA SERVICES
  Copyright (C) 2012 GRANITE DATA SERVICES S.A.S.

  This file is part of Granite Data Services.

  Granite Data Services is free software; you can redistribute it and/or modify
  it under the terms of the GNU Library General Public License as published by
  the Free Software Foundation; either version 2 of the License, or (at your
  option) any later version.

  Granite Data Services is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; if not, see .
*/

package org.granite.client.messaging;

import static net.sf.extcos.util.Assert.iae;
import static net.sf.extcos.util.StringUtils.append;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.extcos.internal.ArraySet;
import net.sf.extcos.spi.AnnotationMetadata;
import net.sf.extcos.spi.ClassLoaderHolder;
import net.sf.extcos.spi.ResourceAccessor;
import net.sf.extcos.util.Assert;
import net.sf.extcos.util.ClassUtils;

import org.granite.logging.Logger;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.EmptyVisitor;

/**
 * @author William DRAI
 */
public class JavaResourceAccessor implements ResourceAccessor {
	
	private class BooleanHolder {
		boolean value;
	}

	private class NameHolder {
		String name;
	}

	private abstract class AnnotatedClassVisitor extends EmptyVisitor {
		@Override
		public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
			if (shouldVisitAnnotation(visible)) {
				if (annotations == null)
					annotations = new HashMap();
				
				return new AnnotationVisitorImpl(desc);
			}

			return null;
		}

		protected abstract boolean shouldVisitAnnotation(boolean visible);
	}

	private class GeneralVisitor extends AnnotatedClassVisitor {
		
		private final NameHolder nameHolder;
		private final BooleanHolder isClassHolder;

		private GeneralVisitor(final NameHolder nameHolder, final BooleanHolder isClassHolder) {
			this.nameHolder = nameHolder;
			this.isClassHolder = isClassHolder;
		}

		@Override
		public void visit(final int version, final int access, final String name,
				final String signature, final String superName,
				final String[] interfaces) {
			
			if (!(Modifier.isAbstract(access) || Modifier.isInterface(access))) {
				isClassHolder.value = true;
				nameHolder.name = name;

				readInterfaces(superName, interfaces);
				readSuperClasses(superName);
			}
		}

		@Override
		public void visitInnerClass(final String name, final String outerName,
				final String innerName, final int access) {
			
			if (isClassHolder.value && nameHolder.name != null && nameHolder.name.equals(name))
				isClassHolder.value = false;
		}

		@Override
		public void visitOuterClass(final String owner, final String name, final String desc) {
			isClassHolder.value = false;
		}

		@Override
		protected boolean shouldVisitAnnotation(final boolean visible) {
			return isClassHolder.value && visible;
		}
	}

	private class AnnotationVisitorImpl extends EmptyVisitor {
		
		private final AnnotationMetadataImpl metadata;
		private final String className;

		private AnnotationVisitorImpl(final String desc) {
			metadata = new AnnotationMetadataImpl();
			className = Type.getType(desc).getClassName();
		}

		@Override
		public void visit(final String name, final Object value) {
			metadata.putParameter(name, value);
		}

		@Override
		public void visitEnum(final String name, final String desc, final String value) {
			try {
				String enumName = Type.getType(desc).getClassName();
				Class enumClass = ClassLoaderHolder.getClassLoader().loadClass(enumName);
				Method valueOf = enumClass.getDeclaredMethod("valueOf",	String.class);
				Object object = valueOf.invoke(null, value);
				metadata.putParameter(name, object);
			} 
			catch (Exception e) { 
				// ignored 
			}
		}

		@Override
		public void visitEnd() {
			try {
				Class annotationClass = ClassLoaderHolder.getClassLoader().loadClass(className);
				
				// Check declared default values of attributes in the annotation type.
				Method[] annotationAttributes = annotationClass.getMethods();
				for (Method annotationAttribute : annotationAttributes) {
					String attributeName = annotationAttribute.getName();
					Object defaultValue = annotationAttribute.getDefaultValue();
					if (defaultValue != null && !metadata.hasKey(attributeName))
						metadata.putParameter(attributeName, defaultValue);					
				}
				annotations.put(className, metadata);
			}
			catch (ClassNotFoundException ex) {
				// Class not found - can't determine meta-annotations.
			}
		}
	}

	private class AnnotationMetadataImpl implements AnnotationMetadata {
		
		private final Map parameters = new HashMap();
		
		@Override
		public Object getValue(final String key) {
			return parameters.get(key);
		}

		@Override
		public boolean hasKey(final String key) {
			return parameters.containsKey(key);
		}

		protected void putParameter(final String key, final Object value) {
			parameters.put(key, value);
		}
	}

	private static Logger logger = Logger.getLogger(JavaResourceAccessor.class);

	private static Method defineClass;
	private static Method resolveClass;

	static {
		try {
			AccessController.doPrivileged(
					new PrivilegedExceptionAction() {
						@Override
						public Void run() throws Exception{
							Class cl = Class.forName("java.lang.ClassLoader");

							defineClass = cl.getDeclaredMethod("defineClass",
									new Class[] { String.class, byte[].class,
									int.class, int.class });

							resolveClass = cl.getDeclaredMethod("resolveClass",
									Class.class);

							return null;
						}
					});
		}
		catch (PrivilegedActionException pae) {
			throw new RuntimeException("cannot initialize Java Resource Accessor", pae.getException());
		}
	}

	private static final int ASM_FLAGS = ClassReader.SKIP_DEBUG + ClassReader.SKIP_CODE + ClassReader.SKIP_FRAMES;

	private byte[] resourceBytes;
	private URL resourceUrl;
	private String className;

	private Map annotations;
	private Set interfaces;
	private Set superClasses;
	private boolean isClass;

	@Override
	public Class generateClass() {
		if (!isClass)
			return null;
		
		Class clazz = null;
		ClassLoader loader = ClassLoaderHolder.getClassLoader();

		try {
			defineClass.setAccessible(true);
			resolveClass.setAccessible(true);

			clazz = (Class)defineClass.invoke(loader,
					className, resourceBytes, 0, resourceBytes.length);
			resolveClass.invoke(loader, clazz);
		} 
		catch (InvocationTargetException e) {
			if (e.getCause() instanceof LinkageError) {
				try {
					clazz = Class.forName(className, true, loader);
				} 
				catch (ClassNotFoundException e1) {
					logger.error(append("Error creating class from URL [", resourceUrl.toString(), "]"), e1);
				}
			} 
			else {
				logger.error(append("Error creating class from URL [",
						resourceUrl.toString(), "]"), e.getCause());
			}
		} 
		catch (Exception e) {
			logger.error(append("Error creating class from URL [",
					resourceUrl.toString(), "]"), e);
		} 
		finally {
			defineClass.setAccessible(false);
			resolveClass.setAccessible(false);
		}

		return clazz;
	}

	@Override
	public AnnotationMetadata getAnnotationMetadata(final Class annotation) {
		
		if (isClass && annotations != null && annotations.containsKey(annotation.getCanonicalName()))
			return annotations.get(annotation.getCanonicalName());

		return null;
	}

	@Override
	public boolean hasInterface(final Class interfaze) {
		if (isClass && interfaces != null)
			return interfaces.contains(interfaze.getCanonicalName());

		return false;
	}

	@Override
	public boolean isClass() {
		return isClass;
	}

	@Override
	public boolean isSubclassOf(final Class clazz) {
		if (clazz == Object.class)
			return true;

		if (isClass && superClasses != null)
			return superClasses.contains(clazz.getCanonicalName());

		return false;
	}

	@Override
	public void setResourceUrl(final URL resourceUrl) {
		Assert.notNull(resourceUrl, iae());

		try {
			this.resourceBytes = readBytes(resourceUrl);
			this.resourceUrl = resourceUrl;
			readClassData();
		} 
		catch (IOException e) {
			isClass = false;
			logger.error("Error reading resource", e);
		}
	}

	private byte[] readBytes(final URL resourceUrl) throws IOException {
		InputStream classStream = new BufferedInputStream(resourceUrl.openStream());
		List buffer = new ArrayList();
		int readByte;

		while((readByte = classStream.read()) != -1) {
			buffer.add((byte)readByte);
		}

		byte[] bytes = new byte[buffer.size()];
		for (int i = 0; i < buffer.size(); i++)
			bytes[i] = buffer.get(i);

		return bytes;
	}

	private void readClassData() {
		BooleanHolder isClassHolder = new BooleanHolder();
		NameHolder nameHolder = new NameHolder();

		ClassReader reader = new ClassReader(resourceBytes);
		reader.accept(new GeneralVisitor(nameHolder, isClassHolder), ASM_FLAGS);

		isClass = isClassHolder.value;

		if (isClass)
			className = ClassUtils.convertResourcePathToClassName(nameHolder.name);
		else {
			// if the resource isn't a valid class, clean memory
			annotations   = null;
			interfaces    = null;
			superClasses  = null;
			resourceBytes = null;
			resourceUrl   = null;
		}
	}

	private void readSuperClasses(final String superName) {
		if (!"java/lang/Object".equals(superName)) {
			if (superClasses == null) {
				superClasses = new ArraySet();
			}

			String superClass = ClassUtils.convertResourcePathToClassName(
					superName);
			superClasses.add(superClass);

			try {
				ClassReader reader = new ClassReader(superClass);
				reader.accept(new AnnotatedClassVisitor() {
					@Override
					public void visit(final int version, final int access, final String name,
							final String signature, final String superName, final String[] interfaces) {
						readSuperClasses(superName);
					}

					@Override
					protected boolean shouldVisitAnnotation(final boolean visible) {
						return visible;
					}
				}, ASM_FLAGS);
			} 
			catch (Exception e) {
				// ignored
			}
		}
	}

	private void readInterfaces(final String superName, final String[] interfaces) {
		if (this.interfaces == null && interfaces.length > 0)
			this.interfaces = new ArraySet();

		for (String interfaze : interfaces) {
			this.interfaces.add(
					ClassUtils.convertResourcePathToClassName(interfaze));
			readSuperInterfaces(interfaze);
		}

		readInheritedInterfaces(superName);
	}

	private void readInheritedInterfaces(final String superName) {
		if ("java/lang/Object".equals(superName))
			return;

		readSuperInterfaces(superName);
	}

	private void readSuperInterfaces(final String type) {
		try {
			ClassReader reader = new ClassReader(ClassUtils.convertResourcePathToClassName(type));

			reader.accept(new EmptyVisitor() {
				@Override
				public void visit(final int version, final int access, final String name,
						final String signature, final String superName, final String[] interfaces) {
					readInterfaces(superName, interfaces);
				}
			}, ASM_FLAGS);
		} 
		catch (Exception e) {
			// ignored
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy