1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Middleware LLC.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 *
24 */
25 package org.hibernate.proxy.pojo.cglib;
26
27 import java.io.Serializable;
28 import java.lang.reflect.InvocationTargetException;
29 import java.lang.reflect.Method;
30
31 import org.hibernate.repackage.cglib.proxy.Callback;
32 import org.hibernate.repackage.cglib.proxy.CallbackFilter;
33 import org.hibernate.repackage.cglib.proxy.Enhancer;
34 import org.hibernate.repackage.cglib.proxy.InvocationHandler;
35 import org.hibernate.repackage.cglib.proxy.NoOp;
36
37 import org.hibernate.HibernateException;
38 import org.hibernate.LazyInitializationException;
39 import org.hibernate.proxy.pojo.BasicLazyInitializer;
40 import org.hibernate.proxy.HibernateProxy;
41 import org.hibernate.engine.SessionImplementor;
42 import org.hibernate.type.AbstractComponentType;
43 import org.hibernate.util.ReflectHelper;
44
45 import org.slf4j.LoggerFactory;
46
47 /**
48 * A <tt>LazyInitializer</tt> implemented using the CGLIB bytecode generation library
49 */
50 public final class CGLIBLazyInitializer extends BasicLazyInitializer implements InvocationHandler {
51
52 private static final CallbackFilter FINALIZE_FILTER = new CallbackFilter() {
53 public int accept(Method method) {
54 if ( method.getParameterTypes().length == 0 && method.getName().equals("finalize") ){
55 return 1;
56 }
57 else {
58 return 0;
59 }
60 }
61 };
62
63 private Class[] interfaces;
64 private boolean constructed = false;
65
66 static HibernateProxy getProxy(final String entityName, final Class persistentClass,
67 final Class[] interfaces, final Method getIdentifierMethod,
68 final Method setIdentifierMethod, AbstractComponentType componentIdType,
69 final Serializable id, final SessionImplementor session) throws HibernateException {
70 // note: interfaces is assumed to already contain HibernateProxy.class
71
72 try {
73 final CGLIBLazyInitializer instance = new CGLIBLazyInitializer(
74 entityName,
75 persistentClass,
76 interfaces,
77 id,
78 getIdentifierMethod,
79 setIdentifierMethod,
80 componentIdType,
81 session
82 );
83
84 final HibernateProxy proxy;
85 Class factory = getProxyFactory(persistentClass, interfaces);
86 proxy = getProxyInstance(factory, instance);
87 instance.constructed = true;
88 return proxy;
89 }
90 catch (Throwable t) {
91 LoggerFactory.getLogger( BasicLazyInitializer.class )
92 .error( "CGLIB Enhancement failed: " + entityName, t );
93 throw new HibernateException( "CGLIB Enhancement failed: " + entityName, t );
94 }
95 }
96
97 public static HibernateProxy getProxy(final Class factory, final String entityName,
98 final Class persistentClass, final Class[] interfaces,
99 final Method getIdentifierMethod, final Method setIdentifierMethod,
100 final AbstractComponentType componentIdType, final Serializable id,
101 final SessionImplementor session) throws HibernateException {
102
103 final CGLIBLazyInitializer instance = new CGLIBLazyInitializer(
104 entityName,
105 persistentClass,
106 interfaces,
107 id,
108 getIdentifierMethod,
109 setIdentifierMethod,
110 componentIdType,
111 session
112 );
113
114 final HibernateProxy proxy;
115 try {
116 proxy = getProxyInstance(factory, instance);
117 }
118 catch (Exception e) {
119 throw new HibernateException( "CGLIB Enhancement failed: " + persistentClass.getName(), e );
120 }
121 instance.constructed = true;
122
123 return proxy;
124 }
125
126 private static HibernateProxy getProxyInstance(Class factory, CGLIBLazyInitializer instance) throws InstantiationException, IllegalAccessException {
127 HibernateProxy proxy;
128 try {
129 Enhancer.registerCallbacks(factory, new Callback[]{ instance, null });
130 proxy = (HibernateProxy)factory.newInstance();
131 } finally {
132 // HHH-2481 make sure the callback gets cleared, otherwise the instance stays in a static thread local.
133 Enhancer.registerCallbacks(factory, null);
134 }
135 return proxy;
136 }
137
138 public static Class getProxyFactory(Class persistentClass, Class[] interfaces)
139 throws HibernateException {
140 Enhancer e = new Enhancer();
141 e.setSuperclass( interfaces.length == 1 ? persistentClass : null );
142 e.setInterfaces(interfaces);
143 e.setCallbackTypes(new Class[]{
144 InvocationHandler.class,
145 NoOp.class,
146 });
147 e.setCallbackFilter(FINALIZE_FILTER);
148 e.setUseFactory(false);
149 e.setInterceptDuringConstruction( false );
150 return e.createClass();
151 }
152
153 private CGLIBLazyInitializer(final String entityName, final Class persistentClass,
154 final Class[] interfaces, final Serializable id, final Method getIdentifierMethod,
155 final Method setIdentifierMethod, final AbstractComponentType componentIdType,
156 final SessionImplementor session) {
157 super(
158 entityName,
159 persistentClass,
160 id,
161 getIdentifierMethod,
162 setIdentifierMethod,
163 componentIdType,
164 session
165 );
166 this.interfaces = interfaces;
167 }
168
169 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
170 if ( constructed ) {
171 Object result = invoke( method, args, proxy );
172 if ( result == INVOKE_IMPLEMENTATION ) {
173 Object target = getImplementation();
174 try {
175 final Object returnValue;
176 if ( ReflectHelper.isPublic( persistentClass, method ) ) {
177 if ( !method.getDeclaringClass().isInstance( target ) ) {
178 throw new ClassCastException( target.getClass().getName() );
179 }
180 returnValue = method.invoke( target, args );
181 }
182 else {
183 if ( !method.isAccessible() ) {
184 method.setAccessible( true );
185 }
186 returnValue = method.invoke( target, args );
187 }
188 return returnValue == target ? proxy : returnValue;
189 }
190 catch ( InvocationTargetException ite ) {
191 throw ite.getTargetException();
192 }
193 }
194 else {
195 return result;
196 }
197 }
198 else {
199 // while constructor is running
200 if ( method.getName().equals( "getHibernateLazyInitializer" ) ) {
201 return this;
202 }
203 else {
204 throw new LazyInitializationException( "unexpected case hit, method=" + method.getName() );
205 }
206 }
207 }
208
209 protected Object serializableProxy() {
210 return new SerializableProxy(
211 getEntityName(),
212 persistentClass,
213 interfaces,
214 getIdentifier(),
215 getIdentifierMethod,
216 setIdentifierMethod,
217 componentIdType
218 );
219 }
220
221 }