/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.util; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.ejb.Local; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; import org.rhq.enterprise.server.alert.AlertDefinitionManagerBean; import org.rhq.enterprise.server.alert.AlertManagerBean; import org.rhq.enterprise.server.auth.SubjectManagerBean; import org.rhq.enterprise.server.authz.RoleManagerBean; import org.rhq.enterprise.server.configuration.ConfigurationManagerBean; import org.rhq.enterprise.server.content.AdvisoryManagerBean; import org.rhq.enterprise.server.content.ContentManagerBean; import org.rhq.enterprise.server.content.DistributionManagerBean; import org.rhq.enterprise.server.content.RepoManagerBean; import org.rhq.enterprise.server.event.EventManagerBean; import org.rhq.enterprise.server.measurement.AvailabilityManagerBean; import org.rhq.enterprise.server.measurement.CallTimeDataManagerBean; import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerBean; import org.rhq.enterprise.server.measurement.MeasurementDataManagerBean; import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerBean; import org.rhq.enterprise.server.measurement.MeasurementProblemManagerBean; import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerBean; import org.rhq.enterprise.server.operation.OperationManagerBean; import org.rhq.enterprise.server.report.DataAccessManagerBean; import org.rhq.enterprise.server.resource.ResourceFactoryManagerBean; import org.rhq.enterprise.server.resource.ResourceManagerBean; import org.rhq.enterprise.server.resource.ResourceTypeManagerBean; import org.rhq.enterprise.server.resource.group.ResourceGroupManagerBean; import org.rhq.enterprise.server.support.SupportManagerBean; import org.rhq.enterprise.server.system.SystemManagerBean; /** * @author Joseph Marques */ public class RemoteAPIValidator { private static Class<?>[] beans = new Class[] { // AdvisoryManagerBean.class, // AlertDefinitionManagerBean.class, // AlertManagerBean.class,// AvailabilityManagerBean.class,// CallTimeDataManagerBean.class, // RepoManagerBean.class,// ConfigurationManagerBean.class, // ContentManagerBean.class, // //ContentSourceManagerBean.class, // DataAccessManagerBean.class, // DistributionManagerBean.class,// EventManagerBean.class, // MeasurementBaselineManagerBean.class,// MeasurementDataManagerBean.class,// MeasurementDefinitionManagerBean.class, // MeasurementProblemManagerBean.class,// MeasurementScheduleManagerBean.class, // OperationManagerBean.class, // //PerspectiveManagerBean.class, // ResourceFactoryManagerBean.class, // ResourceGroupManagerBean.class, // ResourceManagerBean.class, // ResourceTypeManagerBean.class, // RoleManagerBean.class, // SubjectManagerBean.class, // SupportManagerBean.class, // SystemManagerBean.class }; private static Set<String> finderMethodExceptions = new HashSet<String>(); static { /* * unless otherwise noted, all of the below methods return data considered to * be single, logical objects even though they are implemented as list structures */ finderMethodExceptions.add("translateInstallationSteps"); finderMethodExceptions.add("getResourceNameOptionItems"); finderMethodExceptions.add("getResourceLineage"); finderMethodExceptions.add("getResourceIdLineage"); finderMethodExceptions.add("deleteResources"); // action method, just happens to returned deleted resourceIds finderMethodExceptions.add("deleteResource"); // action method, just happens to returned deleted resourceIds finderMethodExceptions.add("executeQuery"); finderMethodExceptions.add("executeQueryWithPageControl"); } public static void validateAll() { int classesInError = 0; int totalErrors = 0; for (Class<?> managerBean : beans) { List<String> errors = validate(managerBean); if (errors.isEmpty() == false) { classesInError++; totalErrors += errors.size(); for (String error : errors) { System.out.println(error); } System.out.println(); } } System.out.println(String.valueOf(beans.length) + " classes checked, " + classesInError + " in error"); System.out.println(String.valueOf(totalErrors) + " total errors"); } public static List<String> validate(Class<?> managerBean) { List<String> errors = new ArrayList<String>(); Class<?>[] interfaces = managerBean.getInterfaces(); if (interfaces.length != 2) { errors.add(managerBean.getSimpleName() + " had " + interfaces.length + " interfaces, was expecting 2"); } Class<?> localInterface = null; Class<?> remoteInterface = null; if (interfaces[0].getAnnotation(Local.class) != null) { localInterface = interfaces[0]; remoteInterface = interfaces[1]; } else { remoteInterface = interfaces[0]; localInterface = interfaces[1]; } Method[] remoteMethods = remoteInterface.getMethods(); for (Method remoteMethod : remoteMethods) { String methodName = remoteMethod.getName(); Class<?>[] parameterTypes = remoteMethod.getParameterTypes(); try { Method localMethod = localInterface.getMethod(methodName, parameterTypes); Method managerBeanMethod = managerBean.getMethod(methodName, parameterTypes); Class<?>[] localExceptions = localMethod.getExceptionTypes(); Class<?>[] managerBeanExceptions = managerBeanMethod.getExceptionTypes(); Class<?>[] remoteExceptions = remoteMethod.getExceptionTypes(); for (Class<?> remoteException : remoteExceptions) { // make sure local mirrors remote throws signature boolean found = false; for (Class<?> localException : localExceptions) { if (remoteException.equals(localException)) { found = true; break; } } if (found == false) { errors.add(format(remoteMethod) + ", local method does not throw " + remoteException.getSimpleName()); } // make sure managerBean mirrors remote throws signature for (Class<?> managerBeanException : managerBeanExceptions) { if (remoteException.equals(managerBeanException)) { found = true; break; } } if (found == false) { errors.add(format(remoteMethod) + ", manager bean method does not throw " + remoteException.getSimpleName()); } } } catch (NoSuchMethodException nsme) { errors.add(format(remoteMethod) + ", method not found in the local interface"); } validateTypeParameter(remoteMethod, 0, Subject.class, "subject", errors); validateTypeParameter(remoteMethod, parameterTypes.length - 1, PageControl.class, "pageControl", errors); } Method[] managerBeanMethods = managerBean.getMethods(); for (Method managerBeanMethod : managerBeanMethods) { String methodName = managerBeanMethod.getName(); Class<?> returnType = managerBeanMethod.getReturnType(); if (returnType.isAssignableFrom(List.class) || returnType.isAssignableFrom(PageList.class)) { if (methodName.startsWith("find") == false && finderMethodExceptions.contains(methodName) == false) { errors.add(format(managerBeanMethod) + ", bean method returning a List must begin with 'find'"); } } } return errors; } private static void validateTypeParameter(Method remoteMethod, int parameterIndex, Class<?> expectedParameterType, String namingConvention, List<String> errors) { int parameterCount = remoteMethod.getParameterTypes().length; if (parameterCount == 0) { return; } if (parameterIndex < 0 || parameterIndex > parameterCount - 1) { errors.add(format(remoteMethod) + ", parameterIndex was " + parameterIndex + " but only had " + parameterCount + " arguments"); } Class<?> parameterType = remoteMethod.getParameterTypes()[parameterIndex]; if (parameterType.equals(expectedParameterType) == false) { return; } Annotation[] parameterAnnotations = remoteMethod.getParameterAnnotations()[parameterIndex]; } private static String format(Method method) { StringBuilder builder = new StringBuilder(); builder.append(method.getDeclaringClass().getSimpleName()).append('.'); builder.append(method.getName()).append("("); boolean first = true; for (Class<?> parameterType : method.getParameterTypes()) { if (first) { first = false; } else { builder.append(","); } builder.append(parameterType.getSimpleName()); } builder.append(")"); return builder.toString(); } public static void main(String[] args) { RemoteAPIValidator.validateAll(); } }