/******************************************************************************* * Copyright (c) 2001, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jst.j2ee.model.internal.validation; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.jem.java.Field; import org.eclipse.jem.java.JavaClass; import org.eclipse.jem.java.JavaHelpers; import org.eclipse.jem.java.Method; import org.eclipse.jst.j2ee.ejb.EnterpriseBean; import org.eclipse.wst.validation.internal.core.ValidationException; import org.eclipse.wst.validation.internal.provisional.core.IMessage; /** * @version 1.0 * @author */ public abstract class ABeanClassVRule extends ATypeVRule { protected final static long[] BASE_TYPES = new long[]{ITypeConstants.JAVA_LANG_OBJECT}; protected final static int MESSAGE_REMOTE_EXCEPTION_SEVERITY = IEJBValidationContext.WARNING; // Deprecated use of RemoteException. @Override public long[] getBaseTypes() { return BASE_TYPES; } public Object getTarget(Object parent, Object clazz) { if(parent == null) { return null; } return ((EnterpriseBean)parent).getEjbClass(); } public long getDefaultMethodType() { return HELPER; } public void validate(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz) throws ValidationCancelledException, InvalidInputException, ValidationException { if(!followAbstractRules(clazz)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2014, IEJBValidationContext.WARNING, bean, clazz, this); vc.addMessage(message); } if(!followPublicRules(clazz)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2022, IEJBValidationContext.WARNING, bean, clazz, this); vc.addMessage(message); } if(!followFinalRules(clazz)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2015, IEJBValidationContext.WARNING, bean, clazz, this); vc.addMessage(message); } validateAppendixB(vc, bean, clazz); } public final static List getRemoteHomeMethodsExtended(List[] methodsExtendedList) { return methodsExtendedList[0]; } public final static List getLocalHomeMethodsExtended(List[] methodsExtendedList) { return methodsExtendedList[1]; } public final static List getRemoteComponentMethodsExtended(List[] methodsExtendedList) { return methodsExtendedList[2]; } public final static List getLocalComponentMethodsExtended(List[] methodsExtendedList) { return methodsExtendedList[3]; } public final static List getBeanClassMethodsExtended(List[] methodsExtendedList) { return methodsExtendedList[4]; } public final List[] getMethodsExtended(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz) { // A bean class needs the following classes' extended methods: // 1. remote home // 2. local home // 3. remote component // 4. local component // 5. bean class (when searching for a matching ejbPostCreate method). // If a particular bean doesn't use a remote view or local view, // then the corresponding entry in the array will be null. List[] result = new List[5]; JavaClass remoteHome = bean.getHomeInterface(); if(remoteHome != null) { result[0] = remoteHome.getMethodsExtended(); } else { result[0] = null; } JavaClass localHome = bean.getLocalHomeInterface(); if(localHome != null) { result[1] = localHome.getMethodsExtended(); } else { result[1] = null; } JavaClass remoteComponent = bean.getRemoteInterface(); if(remoteComponent != null) { result[2] = remoteComponent.getMethodsExtended(); } else { result[2] = null; } JavaClass localComponent = bean.getLocalInterface(); if(localComponent != null) { result[3] = localComponent.getMethodsExtended(); } else { result[3] = null; } JavaClass beanClass = bean.getEjbClass(); if(beanClass != null) { result[4] = beanClass.getMethodsExtended(); } else { result[4] = null; } return result; } public final List[] getFieldsExtended(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz) { // Never check that a bean class' field is defined on another class // of the bean. return null; } protected abstract boolean shouldBeFinal(JavaClass clazz); protected abstract boolean shouldBePublic(JavaClass clazz); protected abstract boolean shouldBeAbstract(JavaClass clazz); protected abstract boolean shouldValidateTransientField(); protected abstract JavaHelpers getEjbCreateReturnType(EnterpriseBean bean, Method method) throws InvalidInputException; protected abstract String getEjbCreateReturnTypeName(EnterpriseBean bean, Method method) throws InvalidInputException; public abstract String getMatchingHomeMethodName(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, List[] methodsExtendedList); public String getMatchingEjbHomeMethodName(IEJBValidationContext vc, Method method) { // Section number stating this name convention? String methodName = method.getName(); // Strip the "ejbHome" off, strip off the next character, and replace that character with a lowercase version of that character StringBuffer buffer = new StringBuffer(Character.toLowerCase(methodName.charAt(0))); buffer.append(method.getName().substring(8)); return buffer.toString(); } public String getMatchingEjbCreateMethodName(IEJBValidationContext vc, Method method) { StringBuffer buffer = new StringBuffer(IMethodAndFieldConstants.PREFIX_C); // Strip the "ejbC" off of "create" and replace with "c" buffer.append(method.getName().substring(4)); return buffer.toString(); } public String getMatchingEjbPostCreateMethodName(IEJBValidationContext vc, Method method) { StringBuffer buffer = new StringBuffer(IMethodAndFieldConstants.PREFIX_C); // Strip the "ejbPostC" off of "create" and replace with "c" buffer.append(method.getName().substring(8)); return buffer.toString(); } public String getMatchingEjbFindMethodName(IEJBValidationContext vc, Method method) { StringBuffer buffer = new StringBuffer(IMethodAndFieldConstants.PREFIX_F); // Strip the "ejbF" off of "ejbFind" and replace with "f" buffer.append(method.getName().substring(4)); return buffer.toString(); } public String getMatchingBusinessMethodName(IEJBValidationContext vc, Method method) { return method.getName(); } /* * The given method is not supposed to have a matching method. */ public String getNoMatchingMethodName(IEJBValidationContext vc, Method method) { return null; // not supposed to have a matching method } public final boolean followFinalRules(JavaClass clazz) { if(shouldBeFinal(clazz)) { return clazz.isFinal(); } return !clazz.isFinal(); } public final boolean followPublicRules(JavaClass clazz) { if(shouldBePublic(clazz)) { return clazz.isPublic(); } return !clazz.isPublic(); } public final boolean followAbstractRules(JavaClass clazz) { if(shouldBeAbstract(clazz)) { return clazz.isAbstract(); } return !clazz.isAbstract(); } public void validateEjbCreateMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, List[] methodsExtendedLists) throws ValidationCancelledException, InvalidInputException, ValidationException { if(!ValidationRuleUtility.isPublic(method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2421, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } if(method.isFinal()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2422, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } if(method.isStatic()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2423, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } JavaHelpers retType = getEjbCreateReturnType(bean, method); if((retType == null) || (!ValidationRuleUtility.isAssignableFrom(method.getReturnType(), retType))) { String retTypeName = (retType == null) ? getEjbCreateReturnTypeName(bean, method) : retType.getJavaName(); IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2424, IEJBValidationContext.WARNING, bean, clazz, method, new String[]{retTypeName}, this); vc.addMessage(message); } validateRMI_IIOPTypeRules(vc, bean, clazz, method, methodsExtendedLists, true); if(validateEjbCreateMethodExceptions()) { if(!followRemoteExceptionRules(bean, method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2503_ejbCreate, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } } } public boolean validateEjbCreateMethodExceptions() { return true; } public void validateEjbPostCreateMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method) throws ValidationCancelledException, InvalidInputException, ValidationException { if(!ValidationRuleUtility.isPublic(method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2426, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } if(method.isFinal()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2427, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } if(method.isStatic()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2428, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } if(!method.isVoid()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2505_ejbPostCreate, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } if(!followRemoteExceptionRules(bean, method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2503_ejbPostCreate, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } } public final void validateEjbHomeMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, List[] methodsExtendedLists) throws ValidationCancelledException, InvalidInputException, ValidationException { if(!ValidationRuleUtility.isPublic(method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2431, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } if(method.isStatic()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2439, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } validateRMI_IIOPTypeRules(vc, bean, clazz, method, methodsExtendedLists, true); if(!followRemoteExceptionRules(bean, method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2503_ejbHome, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } // "ejbHome" is 7 characters // method will follow the convention, "ejbHome" + Char.toUpper(0) + substring(1), so reverse that convention here StringBuffer buffer = new StringBuffer(); buffer.append(Character.toLowerCase(method.getName().charAt(7))); // put the character back into lower-case buffer.append(method.getName().substring(8)); // substring starts at the character following "ejbHome" and the first character -- substring is 0 indexed String methodToMatchName = buffer.toString(); Method matchingRemoteHomeMethod = ValidationRuleUtility.getMethod(method, methodToMatchName, getRemoteHomeMethodsExtended(methodsExtendedLists)); if(matchingRemoteHomeMethod != null) { return; } Method matchingLocalHomeMethod = ValidationRuleUtility.getMethod(method, methodToMatchName, getLocalHomeMethodsExtended(methodsExtendedLists)); if(matchingLocalHomeMethod != null) { return; } IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2430, IEJBValidationContext.INFO, bean, clazz, method, new String[]{methodToMatchName}, this); vc.addMessage(message); } public final void validateBusinessMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, List[] methodsExtendedLists) throws ValidationCancelledException, InvalidInputException, ValidationException { // IWAD4048 = Business methods must not start with "ejb". Read section 7.10.4 of the EJB 2.0 specification. // IWAD4201 = This method name must not start with "ejb". Read section 10.6.8 of the EJB 2.0 specification. // IWAD4317 = The method name must not start with "ejb". Read section 12.2.7 of the EJB 2.0 specification. if(method.getName().startsWith(IMethodAndFieldConstants.PREFIX_EJB)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2411, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } // IWAD4049 = This method must be public. Read section 7.10.4 of the EJB 2.0 specification. // IWAD4202 = This method must be public. Read section 10.6.8 of the EJB 2.0 specification. // IWAD4318 = The method must be public. Read section 12.2.7 of the EJB 2.0 specification. if(!ValidationRuleUtility.isPublic(method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2441, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } // IWAD4050 = This method must not be final. Read section 7.10.4 of the EJB 2.0 specification. // IWAD4203 = This method must not be final. Read section 10.6.8 of the EJB 2.0 specification. // IWAD4319 = The method must not be final. Read section 12.2.7 of the EJB 2.0 specification. if(method.isFinal()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2442, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } // IWAD4051 = This method must not be static. Read section 7.10.4 of the EJB 2.0 specification. // IWAD4204 = This method must not be static. Read section 10.6.8 of the EJB 2.0 specification. // IWAD4320 = The method must not be static. Read section 12.2.7 of the EJB 2.0 specification. if(method.isStatic()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2443, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } // IWAD4052 = {0} must be a legal RMI-IIOP type. Read section 7.10.4 of the EJB 2.0 specification. // IWAD4205 = {0} must be a legal RMI-IIOP type. Read section 10.6.8 of the EJB 2.0 specification. // IWAD4321 = {0} must be a legal type for RMI-IIOP. Read section 12.2.7 of the EJB 2.0 specification. validateRMI_IIOPTypeRules(vc, bean, clazz, method, methodsExtendedLists, true); // IWAD4053 = This method cannot throw java.rmi.RemoteException. Read section 7.10.4, 18.3.8, 18.6 of the EJB 2.0 specification. // IWAD4206 = This method must not throw java.rmi.RemoteException. Read section 10.6.8, 18.3.8, 18.6 of the EJB 2.0 specification. // IWAD4322 = This method must not throw java.rmi.RemoteException. Read section 12.2.7, 18.3.8, 18.6 of the EJB 2.0 specification. if(!followRemoteExceptionRules(bean, method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2503_bus, getMessageRemoteExceptionSeverity(), bean, clazz, method, this); vc.addMessage(message); } validateMatchingComponentMethod(vc, bean, clazz, method, methodsExtendedLists); } public void validateEjbRemoveMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method) throws ValidationCancelledException, InvalidInputException, ValidationException { // IWAD4402 = This method must be public. Read section 15.7.5 of the EJB 2.0 specification. if(!ValidationRuleUtility.isPublic(method)) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2492, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } // IWAD4403 = This method must not be final. Read section 15.7.5 of the EJB 2.0 specification. if(method.isFinal()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2493, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } // IWAD4404 = This method must not be static. Read section 15.7.5 of the EJB 2.0 specification. if(method.isStatic()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2494, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } // IWAD4405 = This method must return void. Read section 15.7.5 of the EJB 2.0 specification. if(!method.isVoid()) { IMessage message = MessageUtility.getUtility().getMessage(vc, IMessagePrefixEjb20Constants.CHKJ2505_ejbRemove, IEJBValidationContext.WARNING, bean, clazz, method, this); vc.addMessage(message); } } /* * @see IClassVRule#validate(IEJBValidationContext, JavaClass, Field) */ public void validate(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Field field, List[] fieldExtendedLists) throws ValidationCancelledException, InvalidInputException, ValidationException { // IWAD4024 = A transient field should not be the {0} type. Read section 7.4.1 of the EJB 2.0 specification. // IWAD4025 = Transient fields are discouraged. Read section 7.4.1 of the EJB 2.0 specification. // IWAD4115 = {0} must not be defined in this class. Read section 10.3.1 of the EJB 2.0 specification. // IWAD4259 = <cmp-field> field must be the {0} type. Read section 11.2.1 of the EJB 2.0 specification. // IWAD4260 = <cmr-field> field must be the {0} type. Read section 11.2.1 of the EJB 2.0 specification. if(shouldValidateTransientField()) { validateTransientField(vc, bean, clazz, field); } } public void validateTransientField(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Field field) throws ValidationCancelledException, InvalidInputException, ValidationException { // By default do nothing } @Override public long getFieldType(EnterpriseBean bean, JavaClass clazz, Field field) { if(field == null) { return EXCLUDED_FIELD; } else if(field.getName().equals(IMethodAndFieldConstants.FIELDNAME_SERIALVERSIONUID)) { return SERIALVERSIONUID; } else { return OTHER_FIELD; } } public void validateMatchingMethodExceptions(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, JavaClass matchingClazz, Method matchingMethod) { // Every exception thrown on the bean class' method must be thrown on the component/home method /* Don't check for this here - let the home & component do it. Set exceptions = ValidationRuleUtility.getNotSubsetExceptions(bean, method, matchingMethod); Iterator eiterator = exceptions.iterator(); while(eiterator.hasNext()) { } */ // Every exception thrown by the component/home method should be thrown or there may be compile errors Set exceptions = ValidationRuleUtility.getNotSubsetSubtypeExceptions(bean, matchingMethod, method); Iterator eiterator = exceptions.iterator(); while(eiterator.hasNext()) { JavaClass exception = (JavaClass)eiterator.next(); IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2456, IEJBValidationContext.WARNING, bean, clazz, method, new String[]{exception.getJavaName(), matchingClazz.getJavaName()}, this); vc.addMessage(message); } } public final void validateMatchingHomeMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, List[] methodsExtendedLists) { Method remoteMethod = ValidationRuleUtility.getMethod(method, getMatchingHomeMethodName(vc, bean, clazz, method, methodsExtendedLists), getRemoteHomeMethodsExtended(methodsExtendedLists)); if(remoteMethod != null) { validateMatchingMethodExceptions(vc, bean, clazz, method, bean.getHomeInterface(), remoteMethod); } Method localMethod = ValidationRuleUtility.getMethod(method, getMatchingHomeMethodName(vc, bean, clazz, method, methodsExtendedLists), getLocalHomeMethodsExtended(methodsExtendedLists)); if(localMethod != null) { validateMatchingMethodExceptions(vc, bean, clazz, method, bean.getLocalHomeInterface(), localMethod); } } public final void validateMatchingComponentMethod(IEJBValidationContext vc, EnterpriseBean bean, JavaClass clazz, Method method, List[] methodsExtendedLists) { Method remoteMethod = ValidationRuleUtility.getMethod(method, method.getName(), getRemoteComponentMethodsExtended(methodsExtendedLists)); if(remoteMethod != null) { validateMatchingMethodExceptions(vc, bean, clazz, method, bean.getRemoteInterface(), remoteMethod); } Method localMethod = ValidationRuleUtility.getMethod(method, method.getName(), getLocalComponentMethodsExtended(methodsExtendedLists)); if(localMethod != null) { validateMatchingMethodExceptions(vc, bean, clazz, method, bean.getLocalInterface(), localMethod); } } protected void validateAppendixB(IEJBValidationContext vc, EnterpriseBean bean, JavaClass thisEjbObject) { // The Java inheritance structure must match the EJB inheritance structure. // e.g. if EJB B is a child of EJB A, then class B must be a child of class A. // B could be a grandchild (or great-grandchild or ...) of A. if(bean == null) { return; } EnterpriseBean supertype = getSuperType(bean); JavaClass parentEjbObject = null; if (supertype != null) { parentEjbObject = supertype.getEjbClass(); // EJBObject a Xchild of parent EJBObject (X = child, grandchild, great-grandchild, etc.) try { ValidationRuleUtility.isValidType(thisEjbObject); ValidationRuleUtility.isValidType(parentEjbObject); if (!ValidationRuleUtility.isAssignableFrom(thisEjbObject, parentEjbObject)) { String[] msgParm = new String[] { thisEjbObject.getQualifiedName(), parentEjbObject.getQualifiedName()}; IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2103, IEJBValidationContext.ERROR, bean, thisEjbObject, msgParm, this); vc.addMessage(message); } } catch (InvalidInputException e) { String[] msgParm = { e.getJavaClass().getQualifiedName(), bean.getName()}; IMessage message = MessageUtility.getUtility().getMessage(vc, IEJBValidatorMessageConstants.CHKJ2849, IEJBValidationContext.WARNING, bean, msgParm, this); vc.addMessage(message); } } } @Override public final boolean followRemoteExceptionRules(EnterpriseBean bean, Method method) throws InvalidInputException, ValidationCancelledException { // must not throw RemoteException return ValidationRuleUtility.doesNotThrowRemoteException(bean, method); } }