blob: 8e979291dd765c1107641513ed04f33e1b5c802a [file] [log] [blame]
/*
* 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.openjpa.event;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.util.Arrays;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.util.UserException;
/**
* Callback adapter that invokes a callback method via reflection.
*
* @author Steve Kim
*/
public class MethodLifecycleCallbacks
implements LifecycleCallbacks, Externalizable {
private static final Localizer _loc = Localizer.forPackage
(MethodLifecycleCallbacks.class);
private transient Method _callback;
private boolean _arg;
/**
* Constructor. Supply callback class and its callback method name.
*
* @param arg Whether we expect a further argument such as in AfterDetach
*/
public MethodLifecycleCallbacks(Class cls, String method, boolean arg) {
Class[] args = arg ? new Class[]{ Object.class } : null;
_callback = getMethod(cls, method, args);
_arg = arg;
}
/**
* Constructor. Supply callback method.
*/
public MethodLifecycleCallbacks(Method method, boolean arg) {
_callback = method;
_arg = arg;
}
/**
* The callback method.
*/
public Method getCallbackMethod() {
return _callback;
}
/**
* Returns if this callback expects another argument
*/
public boolean requiresArgument() {
return _arg;
}
@Override
public boolean hasCallback(Object obj, int eventType) {
return true;
}
@Override
public void makeCallback(Object obj, Object arg, int eventType)
throws Exception {
if (!_callback.isAccessible())
AccessController.doPrivileged(J2DoPrivHelper.setAccessibleAction(
_callback, true));
if (_arg)
_callback.invoke(obj, new Object[]{ arg });
else
_callback.invoke(obj, (Object[]) null);
}
@Override
public String toString() {
return getClass().getName() + ":" + _callback;
}
/**
* Helper method to return the named method of the given class, throwing
* the proper exception on error.
*/
protected static Method getMethod(Class cls, String method, Class[] args) {
Class currentClass = cls;
do {
Method[] methods = (Method[]) AccessController.doPrivileged(
J2DoPrivHelper.getDeclaredMethodsAction(currentClass));
for (Method value : methods) {
if (!method.equals(value.getName()))
continue;
if (isAssignable(value.getParameterTypes(), args))
return value;
}
} while ((currentClass = currentClass.getSuperclass()) != null);
// if we get here, no suitable method was found
throw new UserException(_loc.get("method-notfound", cls.getName(),
method, args == null ? null : Arrays.asList(args)));
}
/**
* Returns true if all parameters in the from array are assignable
* from the corresponding parameters of the to array.
*/
private static boolean isAssignable(Class[] from, Class[] to) {
if (from == null)
return to == null || to.length == 0;
if (to == null)
return from == null || from.length == 0;
if (from.length != to.length)
return false;
for (int i = 0; i < from.length; i++) {
if (from[i] != null && !from[i].isAssignableFrom(to[i]))
return false;
}
return true;
}
@Override
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
Class cls = (Class) in.readObject();
String methName = (String) in.readObject();
_arg = in.readBoolean();
Class[] args = _arg ? new Class[]{ Object.class } : null;
_callback = getMethod(cls, methName, args);
}
@Override
public void writeExternal(ObjectOutput out)
throws IOException {
out.writeObject(_callback.getClass());
out.writeObject(_callback.getName());
out.writeBoolean(_arg);
}
}