/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* Licensed 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.seasar.framework.ejb.impl;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateful;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptors;
import javax.interceptor.InvocationContext;
import org.seasar.framework.ejb.EJB3BusinessMethodDesc;
import org.seasar.framework.ejb.EJB3Desc;
import org.seasar.framework.ejb.EJB3InterceptorDesc;
import org.seasar.framework.ejb.SEJBException;
/**
* EJB3のセッションビーンを表現するクラスです。
*
* @author koichik
*/
public class EJB3DescImpl implements EJB3Desc {
/** セッションビーンのクラス */
protected Class<?> beanClass;
/** セッションビーンのクラス名 */
protected String beanClassName;
/** このセッションビーンがステートレスなら{@code true} */
protected boolean stateless;
/** このセッションビーンがステートフルなら{@code true} */
protected boolean stateful;
/** このセッションビーンの名前 */
protected String name;
/** このセッションビーンが実装するビジネスインターフェースの{@link List} */
protected List<Class<?>> businessInterfaces = new ArrayList<Class<?>>();
/** このセッションビーンがコンテナ管理トランザクションを使用する場合は{@code true} */
protected boolean cmt = true;
/** このセッションビーンに適用されるインターセプタ定義の{@link List} */
protected List<EJB3InterceptorDesc> interceptors = new ArrayList<EJB3InterceptorDesc>();
/** このセッションビーンが持つビジネスメソッドの{@link List} */
protected List<EJB3BusinessMethodDesc> businessMethods = new ArrayList<EJB3BusinessMethodDesc>();
/** {@link AroundInvoke}で注釈されたメソッドの{@link List} */
protected LinkedList<Method> aroundInvokeMethods = new LinkedList<Method>();
/** {@link PostConstruct}で注釈されたメソッドの{@link List} */
protected LinkedList<Method> postConstructMethods = new LinkedList<Method>();
/**
* インスタンスを構築します。
*
* @param beanClass
* セッションビーンのクラス
*/
public EJB3DescImpl(final Class<?> beanClass) {
this.beanClass = beanClass;
this.beanClassName = beanClass.getName();
introspection();
}
/**
* コンストラクタ引数で指定されたクラスがEJB3セッションビーの場合は{@code true}を返します。
*
* @return コンストラクタ引数で指定されたクラスがEJB3セッションビーの場合は{@code true}
*/
public boolean isEJB3() {
return stateless || stateful;
}
public boolean isStateless() {
return stateless;
}
public boolean isStateful() {
return stateful;
}
public String getName() {
return name;
}
public Class<?> getBeanClass() {
return beanClass;
}
public List<Class<?>> getBusinessInterfaces() {
return businessInterfaces;
}
public boolean isCMT() {
return cmt;
}
public List<EJB3InterceptorDesc> getInterceptors() {
return interceptors;
}
public EJB3BusinessMethodDesc getBusinessMethod(final Method method) {
for (final EJB3BusinessMethodDesc businessMethodDesc : businessMethods) {
final Method businessMethod = businessMethodDesc.getMethod();
if (!method.getName().equals(businessMethod.getName())) {
continue;
}
if (!Arrays.equals(method.getParameterTypes(), businessMethod
.getParameterTypes())) {
continue;
}
return businessMethodDesc;
}
return null;
}
public List<EJB3BusinessMethodDesc> getBusinessMethods() {
return businessMethods;
}
public List<Method> getAroundInvokeMethods() {
return aroundInvokeMethods;
}
public List<Method> getPostConstructMethods() {
return postConstructMethods;
}
/**
* コンストラクタ引数で指定されたクラスを解析します。
*/
protected void introspection() {
final int modifiers = beanClass.getModifiers();
if (beanClass.isInterface() || Modifier.isAbstract(modifiers)
|| Modifier.isFinal(modifiers) || !Modifier.isPublic(modifiers)) {
return;
}
final Stateless slsb = beanClass.getAnnotation(Stateless.class);
if (slsb != null) {
stateless = true;
name = slsb.name();
}
final Stateful sfsb = beanClass.getAnnotation(Stateful.class);
if (sfsb != null) {
stateful = true;
name = sfsb.name();
}
if (!stateless && !stateful) {
return;
}
if (stateless && stateful) {
throw new SEJBException("ESSR0400", beanClassName);
}
detectLocalBusinessInterfaces();
detectRemoteBusinessInterfaces();
if (businessInterfaces.isEmpty()) {
detectImplicitBusinessInterfaces();
if (businessInterfaces.isEmpty()) {
throw new SEJBException("ESSR0401", beanClassName);
}
if (businessInterfaces.size() > 1) {
throw new SEJBException("ESSR0402", beanClassName);
}
}
detectTransactionManagementType();
detectInterceptors();
detectBusinessMethods();
detectAroundInvokeMethods();
detectPostConstructMethods();
}
/**
* {@link Local}アノテーションで指定されたビジネスインターフェースを検出します。
*/
protected void detectLocalBusinessInterfaces() {
final Local local = beanClass.getAnnotation(Local.class);
if (local != null) {
for (final Class<?> type : local.value()) {
if (isBusinessInterface(type)) {
businessInterfaces.add(type);
}
}
return;
}
for (final Class<?> type : beanClass.getInterfaces()) {
final Local annotation = type.getAnnotation(Local.class);
if (annotation != null) {
businessInterfaces.add(type);
}
}
}
/**
* {@link Remote}アノテーションで指定されたビジネスインターフェースを検出します。
*/
protected void detectRemoteBusinessInterfaces() {
final Remote remote = beanClass.getAnnotation(Remote.class);
if (remote != null) {
for (final Class<?> type : remote.value()) {
if (isBusinessInterface(type)) {
businessInterfaces.add(type);
}
}
}
for (final Class<?> type : beanClass.getInterfaces()) {
final Remote annotation = type.getAnnotation(Remote.class);
if (annotation != null) {
businessInterfaces.add(type);
}
}
}
/**
* 暗黙的なビジネスインターフェースを検出します。
*/
protected void detectImplicitBusinessInterfaces() {
for (final Class<?> type : beanClass.getInterfaces()) {
if (isBusinessInterface(type)) {
businessInterfaces.add(type);
}
}
}
/**
* {@link TransactionManagementType}を検出します。
*/
protected void detectTransactionManagementType() {
final TransactionManagement txManegement = beanClass
.getAnnotation(TransactionManagement.class);
if (txManegement != null) {
final TransactionManagementType type = txManegement.value();
if (TransactionManagementType.BEAN.equals(type)) {
cmt = false;
return;
}
}
}
/**
* インターセプタを検出します。
*/
protected void detectInterceptors() {
final Interceptors annotation = beanClass
.getAnnotation(Interceptors.class);
if (annotation == null) {
return;
}
for (Class<?> interceptor : annotation.value()) {
interceptors.add(new EJB3InterceptorDescImpl(this, interceptor));
}
}
/**
* ビジネスメソッドを検出します。
*/
protected void detectBusinessMethods() {
for (final Method method : beanClass.getMethods()) {
if (method.isBridge() || method.isSynthetic()) {
continue;
}
final int modifier = method.getModifiers();
if (Modifier.isStatic(modifier) || Modifier.isFinal(modifier)) {
continue;
}
if (!isBusinessMethod(method)) {
continue;
}
businessMethods.add(new EJB3BusinessMethodDescImpl(this, method));
}
}
/**
* {@link AroundInvoke}で注釈されたメソッドを検出します。
*/
public void detectAroundInvokeMethods() {
final Set<String> methods = new HashSet<String>();
for (Class<?> clazz = beanClass; clazz != Object.class; clazz = clazz
.getSuperclass()) {
for (final Method method : clazz.getDeclaredMethods()) {
if (method.isBridge()) {
continue;
}
final AroundInvoke aroundInvoke = method
.getAnnotation(AroundInvoke.class);
final int modifiers = method.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
if (aroundInvoke != null) {
throw new SEJBException("ESSR0409", "AroundInvoke",
beanClassName, method);
}
continue;
}
final Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1
|| paramTypes[0] != InvocationContext.class) {
if (aroundInvoke != null) {
throw new SEJBException("ESSR0409", "AroundInvoke",
beanClassName, method);
}
continue;
}
if (method.getReturnType() != Object.class) {
if (aroundInvoke != null) {
throw new SEJBException("ESSR0409", "AroundInvoke",
beanClassName, method);
}
continue;
}
final String methodName = method.getName();
if (methods.contains(methodName)) {
continue;
}
methods.add(methodName);
if (aroundInvoke == null) {
continue;
}
if (isBusinessMethod(method)) {
throw new SEJBException("ESSR0409", "AroundInvoke",
beanClassName, method);
}
aroundInvokeMethods.addFirst(method);
}
}
}
/**
* {@link PostConstruct}で注釈されたメソッドを検出します。
*/
public void detectPostConstructMethods() {
final Set<String> methods = new HashSet<String>();
for (Class<?> clazz = beanClass; clazz != Object.class; clazz = clazz
.getSuperclass()) {
for (final Method method : clazz.getDeclaredMethods()) {
if (method.isBridge()) {
continue;
}
final PostConstruct postConstruct = method
.getAnnotation(PostConstruct.class);
final int modifiers = method.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
if (postConstruct != null) {
throw new SEJBException("ESSR0409", "PostConstruct",
beanClassName, method);
}
continue;
}
final Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 0) {
if (postConstruct != null) {
throw new SEJBException("ESSR0409", "PostConstruct",
beanClassName, method);
}
continue;
}
if (method.getReturnType() != void.class) {
if (postConstruct != null) {
throw new SEJBException("ESSR0409", "PostConstruct",
beanClassName, method);
}
continue;
}
final String methodName = method.getName();
if (methods.contains(methodName)) {
continue;
}
methods.add(methodName);
if (postConstruct == null) {
continue;
}
if (isBusinessMethod(method)) {
throw new SEJBException("ESSR0409", "PostConstruct", method);
}
postConstructMethods.addFirst(method);
}
}
}
/**
* {@code type}がビジネスインターフェースなら{@code true}を返します。
*
* @param type
* セッションビーンが実装しているインターフェースの型
* @return {@code type}がビジネスインターフェースなら{@code true}
*/
protected boolean isBusinessInterface(final Class<?> type) {
if (!type.isInterface()) {
throw new SEJBException("ESSR0407", beanClassName, type.getName());
}
if (!type.isAssignableFrom(beanClass)) {
throw new SEJBException("ESSR0408", beanClassName, type.getName());
}
if (Serializable.class.equals(type)) {
return false;
}
if (Exception.class.equals(type)) {
return false;
}
if (type.getName().startsWith("javax.ejb")) {
return false;
}
return true;
}
/**
* {@code method}がビジネスメソッドなら{@code ture}を返します。
*
* @param method
* セッションビーンのメソッド
* @return {@code method}がビジネスメソッドなら{@code ture}
*/
protected boolean isBusinessMethod(final Method method) {
for (final Class<?> type : businessInterfaces) {
try {
type.getDeclaredMethod(method.getName(), method
.getParameterTypes());
return true;
} catch (final NoSuchMethodException ignore) {
}
}
return false;
}
}