| /* |
| * 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.openejb.config.rules; |
| |
| import org.apache.openejb.OpenEJBException; |
| import org.apache.openejb.api.Proxy; |
| import org.apache.openejb.jee.EnterpriseBean; |
| import org.apache.openejb.jee.RemoteBean; |
| import org.apache.openejb.jee.EntityBean; |
| import org.apache.openejb.jee.SessionBean; |
| import org.apache.openejb.jee.Interceptor; |
| import org.apache.openejb.config.EjbModule; |
| import org.apache.openejb.util.SafeToolkit; |
| import org.apache.openejb.util.Strings; |
| import org.apache.xbean.finder.ClassFinder; |
| |
| import javax.ejb.EJBLocalHome; |
| import javax.ejb.EJBLocalObject; |
| import javax.ejb.EJBHome; |
| import javax.ejb.EJBObject; |
| import javax.ejb.Local; |
| import javax.ejb.Remote; |
| import javax.jws.WebService; |
| import javax.persistence.PersistenceContext; |
| |
| import static java.lang.reflect.Modifier.isAbstract; |
| import java.lang.reflect.Method; |
| import java.lang.annotation.Annotation; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * @version $Rev: 1177041 $ $Date: 2011-09-28 13:14:32 -0700 (Wed, 28 Sep 2011) $ |
| */ |
| public class CheckClasses extends ValidationBase { |
| |
| private static final List<Class<? extends Annotation>> beanOnlyAnnotations = new ArrayList<Class<? extends Annotation>>(); |
| |
| static { |
| beanOnlyAnnotations.add(javax.annotation.PostConstruct.class); |
| beanOnlyAnnotations.add(javax.annotation.PreDestroy.class); |
| beanOnlyAnnotations.add(javax.annotation.Resource.class); |
| beanOnlyAnnotations.add(javax.annotation.Resources.class); |
| beanOnlyAnnotations.add(javax.annotation.security.DeclareRoles.class); |
| beanOnlyAnnotations.add(javax.annotation.security.DenyAll.class); |
| beanOnlyAnnotations.add(javax.annotation.security.PermitAll.class); |
| beanOnlyAnnotations.add(javax.annotation.security.RolesAllowed.class); |
| beanOnlyAnnotations.add(javax.annotation.security.RunAs.class); |
| |
| beanOnlyAnnotations.add(javax.ejb.EJB.class); |
| beanOnlyAnnotations.add(javax.ejb.EJBs.class); |
| beanOnlyAnnotations.add(javax.ejb.Init.class); |
| beanOnlyAnnotations.add(javax.ejb.PostActivate.class); |
| beanOnlyAnnotations.add(javax.ejb.PrePassivate.class); |
| beanOnlyAnnotations.add(javax.ejb.Remove.class); |
| beanOnlyAnnotations.add(javax.ejb.Timeout.class); |
| beanOnlyAnnotations.add(javax.ejb.TransactionAttribute.class); |
| beanOnlyAnnotations.add(javax.ejb.TransactionManagement.class); |
| } |
| |
| public void validate(EjbModule ejbModule) { |
| for (EnterpriseBean bean : ejbModule.getEjbJar().getEnterpriseBeans()) { |
| try { |
| Class<?> beanClass = check_hasEjbClass(bean); |
| |
| // All the subsequent checks require the bean class |
| if (beanClass == null) continue; |
| |
| if (!(bean instanceof RemoteBean)) continue; |
| RemoteBean b = (RemoteBean) bean; |
| |
| check_isEjbClass(b); |
| check_hasDependentClasses(b, b.getEjbClass(), "ejb-class"); |
| check_hasInterface(b); |
| |
| if (b.getRemote() != null){ |
| checkInterface(b, beanClass, "remote", b.getRemote()); |
| } |
| |
| if (b.getHome() != null) { |
| checkInterface(b, beanClass, "home", b.getHome()); |
| } |
| |
| if (b.getLocal() != null) { |
| checkInterface(b, beanClass, "local", b.getLocal()); |
| } |
| |
| if (b.getLocalHome() != null) { |
| checkInterface(b, beanClass, "local-home", b.getLocalHome()); |
| } |
| |
| if (b instanceof SessionBean) { |
| SessionBean sessionBean = (SessionBean) b; |
| |
| for (String interfce : sessionBean.getBusinessLocal()) { |
| checkInterface(b, beanClass, "business-local", interfce); |
| } |
| |
| for (String interfce : sessionBean.getBusinessRemote()) { |
| checkInterface(b, beanClass, "business-remote", interfce); |
| } |
| } |
| } catch (RuntimeException e) { |
| throw new RuntimeException(bean.getEjbName(), e); |
| } |
| } |
| |
| for (Interceptor interceptor : ejbModule.getEjbJar().getInterceptors()) { |
| check_hasInterceptorClass(interceptor); |
| } |
| } |
| |
| private void checkInterface(RemoteBean b, Class<?> beanClass, String tag, String className) { |
| Class<?> interfce = lookForClass(className, tag, b.getEjbName()); |
| |
| if (interfce == null) return; |
| |
| check_hasDependentClasses(b, className, tag); |
| |
| tag = Strings.lcfirst(Strings.camelCase(tag)); |
| |
| if (isValidInterface(b, interfce, beanClass, tag)); |
| |
| ClassFinder finder = new ClassFinder(interfce); |
| |
| for (Class<? extends Annotation> annotation : beanOnlyAnnotations) { |
| |
| if (interfce.isAnnotationPresent(annotation)){ |
| warn(b, "interface.beanOnlyAnnotation", annotation.getSimpleName(), interfce.getName(), b.getEjbClass()); |
| } |
| |
| for (Method method : finder.findAnnotatedMethods(annotation)) { |
| warn(b, "interfaceMethod.beanOnlyAnnotation", annotation.getSimpleName(), interfce.getName(), method.getName(), b.getEjbClass()); |
| } |
| } |
| |
| } |
| |
| private void check_hasInterface(RemoteBean b) { |
| if (b.getRemote() != null) return; |
| if (b.getLocal() != null) return; |
| |
| Class<?> beanClass = null; |
| try { |
| beanClass = loadClass(b.getEjbClass()); |
| } catch (OpenEJBException e) { |
| } |
| |
| if (b instanceof EntityBean){ |
| fail(b, "noInterfaceDeclared.entity", beanClass.getSimpleName()); |
| return; |
| } |
| |
| if (b.getBusinessLocal().size() > 0) return; |
| if (b.getBusinessRemote().size() > 0) return; |
| |
| if (((SessionBean) b).getServiceEndpoint() != null) return; |
| |
| if (beanClass.isAnnotationPresent(WebService.class)) return; |
| |
| //fail(b, "noInterfaceDeclared.session"); |
| } |
| |
| private void check_hasDependentClasses(RemoteBean b, String className, String type) { |
| try { |
| ClassLoader cl = module.getClassLoader(); |
| Class<?> clazz = cl.loadClass(className); |
| for (Object item : clazz.getFields()) { item.toString(); } |
| for (Object item : clazz.getMethods()) { item.toString(); } |
| for (Object item : clazz.getConstructors()) { item.toString(); } |
| for (Object item : clazz.getAnnotations()) { item.toString(); } |
| // checking for any declared enum constants |
| for(Class klass: clazz.getClasses()){ |
| if(klass.isEnum()){ |
| klass.toString(); |
| } |
| } |
| } catch (ClassNotFoundException e) { |
| /* |
| # 0 - Referring Class name |
| # 1 - Dependent Class name |
| # 2 - Element (home, ejb-class, remote) |
| # 3 - Bean name |
| */ |
| String missingClass = e.getMessage(); |
| fail(b, "missing.dependent.class", className, missingClass, type, b.getEjbName()); |
| } catch (NoClassDefFoundError e) { |
| /* |
| # 0 - Referring Class name |
| # 1 - Dependent Class name |
| # 2 - Element (home, ejb-class, remote) |
| # 3 - Bean name |
| */ |
| String missingClass = e.getMessage(); |
| fail(b, "missing.dependent.class", className, missingClass, type, b.getEjbName()); |
| } |
| } |
| |
| public Class<?> check_hasEjbClass(EnterpriseBean b) { |
| |
| String ejbName = b.getEjbName(); |
| |
| Class<?> beanClass = lookForClass(b.getEjbClass(), "ejb-class", ejbName); |
| boolean isDynamicProxyImpl = beanClass.getAnnotation(PersistenceContext.class) != null |
| || beanClass.getAnnotation(Proxy.class) != null; |
| |
| if (beanClass == null) return null; |
| |
| if (beanClass.isInterface() && !isDynamicProxyImpl){ |
| fail(ejbName, "interfaceDeclaredAsBean", beanClass.getName()); |
| } |
| |
| if (isCmp(b)) return beanClass; |
| |
| if (isAbstract(beanClass.getModifiers()) && !isDynamicProxyImpl){ |
| fail(ejbName, "abstractDeclaredAsBean", beanClass.getName()); |
| } |
| |
| return beanClass; |
| } |
| |
| private void check_hasInterceptorClass(Interceptor i) { |
| |
| lookForClass(i.getInterceptorClass(), "interceptor-class", "Interceptor"); |
| |
| } |
| |
| private void check_isEjbClass(RemoteBean b) { |
| |
| if (b instanceof SessionBean) { |
| |
| // DMB: Beans in ejb 3 are not required to implement javax.ejb.SessionBean |
| // but it would still be nice to think of some sort of check to do here. |
| // compareTypes(b, b.getEjbClass(), javax.ejb.SessionBean.class); |
| |
| } else if (b instanceof EntityBean) { |
| |
| compareTypes(b, b.getEjbClass(), javax.ejb.EntityBean.class); |
| |
| } |
| |
| } |
| |
| private Class<?> lookForClass(String clazz, String type, String ejbName) { |
| try { |
| return loadClass(clazz); |
| } catch (OpenEJBException e) { |
| /* |
| # 0 - Class name |
| # 1 - Element (home, ejb-class, remote) |
| # 2 - Bean name |
| */ |
| |
| fail(ejbName, "missing.class", clazz, type, ejbName); |
| |
| } catch (NoClassDefFoundError e) { |
| /* |
| # 0 - Class name |
| # 1 - Element (home, ejb-class, remote) |
| # 2 - Bean name |
| # 3 - Misslocated Class name |
| */ |
| fail(ejbName, "misslocated.class", clazz, type, ejbName, e.getMessage()); |
| |
| throw e; |
| } |
| |
| return null; |
| } |
| |
| private boolean isValidInterface(RemoteBean b, Class clazz, Class beanClass, String tag) { |
| |
| if (clazz.equals(beanClass)) { |
| |
| fail(b, "xml." + tag + ".beanClass", clazz.getName()); |
| |
| } else if (!clazz.isInterface()) { |
| |
| fail(b, "xml." + tag + ".notInterface", clazz.getName()); |
| |
| } else if (EJBHome.class.isAssignableFrom(clazz)) { |
| |
| if (tag.equals("home")) return true; |
| |
| fail(b, "xml." + tag + ".ejbHome", clazz.getName()); |
| |
| } else if (EJBLocalHome.class.isAssignableFrom(clazz)) { |
| |
| if (tag.equals("localHome")) return true; |
| |
| fail(b, "xml." + tag + ".ejbLocalHome", clazz.getName()); |
| |
| } else if (EJBObject.class.isAssignableFrom(clazz)) { |
| |
| if (tag.equals("remote")) return true; |
| |
| fail(b, "xml." + tag + ".ejbObject", clazz.getName()); |
| |
| } else if (EJBLocalObject.class.isAssignableFrom(clazz)) { |
| |
| if (tag.equals("local")) return true; |
| |
| fail(b, "xml." + tag + ".ejbLocalObject", clazz.getName()); |
| |
| } else { |
| if (tag.equals("businessLocal") || tag.equals("businessRemote")) { |
| |
| return true; |
| |
| } else if (clazz.isAnnotationPresent(Local.class)) { |
| |
| fail(b, "xml." + tag + ".businessLocal", clazz.getName()); |
| |
| } else if (clazz.isAnnotationPresent(Remote.class)) { |
| |
| fail(b, "xml." + tag + ".businessRemote", clazz.getName()); |
| |
| } else { |
| |
| fail(b, "xml." + tag + ".unknown", clazz.getName()); |
| |
| } |
| |
| } |
| |
| // must be tagged as <home>, <local-home>, <remote>, or <local> |
| |
| return false; |
| } |
| |
| private void compareTypes(RemoteBean b, String clazz1, Class<?> class2) { |
| Class<?> class1 = null; |
| try { |
| class1 = loadClass(clazz1); |
| } catch (OpenEJBException e) { |
| return; |
| } |
| |
| if (class1 != null && !class2.isAssignableFrom(class1)) { |
| fail(b, "wrong.class.type", clazz1, class2.getName()); |
| } |
| } |
| |
| protected Class<?> loadClass(String clazz) throws OpenEJBException { |
| ClassLoader cl = module.getClassLoader(); |
| try { |
| return Class.forName(clazz, false, cl); |
| } catch (ClassNotFoundException cnfe) { |
| throw new OpenEJBException(SafeToolkit.messages.format("cl0007", clazz, module.getJarLocation()), cnfe); |
| } |
| } |
| } |
| |