/* * 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; } }