001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.bean;
016    
017    import com.liferay.portal.kernel.bean.ConstantsBeanFactory;
018    import com.liferay.portal.kernel.memory.EqualityWeakReference;
019    import com.liferay.portal.kernel.util.ReflectionUtil;
020    
021    import java.lang.ref.Reference;
022    import java.lang.ref.ReferenceQueue;
023    import java.lang.ref.WeakReference;
024    import java.lang.reflect.Field;
025    import java.lang.reflect.Method;
026    import java.lang.reflect.Modifier;
027    
028    import java.util.concurrent.ConcurrentHashMap;
029    import java.util.concurrent.ConcurrentMap;
030    
031    import org.objectweb.asm.ClassWriter;
032    import org.objectweb.asm.MethodVisitor;
033    import org.objectweb.asm.Opcodes;
034    import org.objectweb.asm.Type;
035    
036    /**
037     * @author Shuyang Zhou
038     */
039    public class ConstantsBeanFactoryImpl implements ConstantsBeanFactory {
040    
041            public Object getConstantsBean(Class<?> constantsClass) {
042                    Reference<?> constantsBeanReference = constantsBeans.get(
043                            new EqualityWeakReference<Class<?>>(constantsClass));
044    
045                    Object constantsBean = null;
046    
047                    if (constantsBeanReference != null) {
048                            constantsBean = constantsBeanReference.get();
049                    }
050    
051                    if (constantsBean == null) {
052                            constantsBean = createConstantsBean(constantsClass);
053    
054                            constantsBeans.put(
055                                    new EqualityWeakReference<Class<?>>(
056                                            constantsClass, constantsClassReferenceQueue),
057                                    new WeakReference<Object>(constantsBean));
058                    }
059    
060                    while (true) {
061                            EqualityWeakReference<Class<?>> staleConstantsClassReference =
062                                    (EqualityWeakReference<Class<?>>)
063                                            constantsClassReferenceQueue.poll();
064    
065                            if (staleConstantsClassReference == null) {
066                                    break;
067                            }
068    
069                            constantsBeans.remove(staleConstantsClassReference);
070                    }
071    
072                    return constantsBean;
073            }
074    
075            protected static Object createConstantsBean(Class<?> constantsClass) {
076                    ClassLoader classLoader = constantsClass.getClassLoader();
077    
078                    String constantsBeanClassName = constantsClass.getName() + "Bean";
079    
080                    Class<?> constantsBeanClass = null;
081    
082                    synchronized (classLoader) {
083                            try {
084                                    constantsBeanClass = classLoader.loadClass(
085                                            constantsBeanClassName);
086                            }
087                            catch (ClassNotFoundException cnfe) {
088                            }
089    
090                            try {
091                                    if (constantsBeanClass == null) {
092                                            Method defineClassMethod = ReflectionUtil.getDeclaredMethod(
093                                                    ClassLoader.class, "defineClass", String.class,
094                                                    byte[].class, int.class, int.class);
095    
096                                            byte[] classData = generateConstantsBeanClassData(
097                                                    constantsClass);
098    
099                                            constantsBeanClass = (Class<?>)defineClassMethod.invoke(
100                                                    classLoader, constantsBeanClassName, classData, 0,
101                                                    classData.length);
102                                    }
103    
104                                    return constantsBeanClass.newInstance();
105                            }
106                            catch (Exception e) {
107                                    throw new RuntimeException(e);
108                            }
109                    }
110            }
111    
112            protected static byte[] generateConstantsBeanClassData(
113                    Class<?> constantsClass) {
114    
115                    String constantsClassBinaryName = getClassBinaryName(constantsClass);
116    
117                    String constantsBeanClassBinaryName = constantsClassBinaryName + "Bean";
118    
119                    String objectClassBinaryName = getClassBinaryName(Object.class);
120    
121                    ClassWriter classWriter = new ClassWriter(0);
122    
123                    classWriter.visit(
124                            Opcodes.V1_5, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER,
125                            constantsBeanClassBinaryName, null, objectClassBinaryName, null);
126    
127                    MethodVisitor methodVisitor = classWriter.visitMethod(
128                            Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
129    
130                    methodVisitor.visitCode();
131                    methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
132                    methodVisitor.visitMethodInsn(
133                            Opcodes.INVOKESPECIAL, objectClassBinaryName, "<init>", "()V");
134                    methodVisitor.visitInsn(Opcodes.RETURN);
135                    methodVisitor.visitMaxs(1, 1);
136                    methodVisitor.visitEnd();
137    
138                    Field[] fields = constantsClass.getFields();
139    
140                    for (Field field :fields) {
141                            if (Modifier.isStatic(field.getModifiers())) {
142                                    Class<?> fieldClass = field.getType();
143    
144                                    Type fieldType = Type.getType(fieldClass);
145    
146                                    methodVisitor = classWriter.visitMethod(
147                                            Opcodes.ACC_PUBLIC, "get" + field.getName(),
148                                            "()" + fieldType.getDescriptor(), null, null);
149    
150                                    methodVisitor.visitCode();
151                                    methodVisitor.visitFieldInsn(
152                                            Opcodes.GETSTATIC, constantsClassBinaryName,
153                                            field.getName(), fieldType.getDescriptor());
154    
155                                    int returnOpcode = Opcodes.ARETURN;
156    
157                                    if (fieldClass.isPrimitive()) {
158                                            if (fieldClass == Float.TYPE) {
159                                                    returnOpcode = Opcodes.FRETURN;
160                                            }
161                                            else if (fieldClass == Double.TYPE) {
162                                                    returnOpcode = Opcodes.DRETURN;
163                                            }
164                                            else if (fieldClass == Long.TYPE) {
165                                                    returnOpcode = Opcodes.LRETURN;
166                                            }
167                                            else {
168                                                    returnOpcode = Opcodes.IRETURN;
169                                            }
170                                    }
171    
172                                    methodVisitor.visitInsn(returnOpcode);
173    
174                                    methodVisitor.visitMaxs(fieldType.getSize(), 1);
175                                    methodVisitor.visitEnd();
176                            }
177                    }
178    
179                    classWriter.visitEnd();
180    
181                    return classWriter.toByteArray();
182            }
183    
184            protected static String getClassBinaryName(Class<?> clazz) {
185                    String className = clazz.getName();
186    
187                    return className.replace('.', '/');
188            }
189    
190            protected static ConcurrentMap
191                    <EqualityWeakReference<Class<?>>, Reference<?>>
192                            constantsBeans =
193                                    new ConcurrentHashMap
194                                            <EqualityWeakReference<Class<?>>, Reference<?>>();
195            protected static ReferenceQueue<Class<?>> constantsClassReferenceQueue =
196                    new ReferenceQueue<Class<?>>();
197    
198    }