/* * 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.axis2.jaxws.description; import org.apache.axis2.jaxws.description.builder.DescriptionBuilderComposite; import org.apache.axis2.jaxws.description.builder.MethodDescriptionComposite; import org.apache.axis2.jaxws.description.impl.DescriptionUtils; import org.apache.axis2.jaxws.description.impl.EndpointInterfaceDescriptionImpl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * A MethodRetriever is an abstract class which is meant to be sub-classed for each type of * method retrieval behavior. The catalyst for this was the introduction of new spec. * interpretation by SUN RI. * * Please refer to the following links: * * * https://jax-ws.dev.java.net/issues/show_bug.cgi?id=577 * http://forums.java.net/jive/thread.jspa?threadID=61630 * http://forums.java.net/jive/thread.jspa?threadID=55078 * * This base is being used to allow for a cleaner componentization of the old/new and potential * future behavior changes. * * The sub-class is required to implement only the abstract 'retrieveMethods' * */ public abstract class MethodRetriever { //Logging setup private static final Log log = LogFactory.getLog(MethodRetriever.class); private String legacyWebMethod = null; public String getLegacyWebMethod() { return legacyWebMethod; } public void setLegacyWebMethod(String legacyWebMethod) { this.legacyWebMethod = legacyWebMethod; } protected MethodRetriever() {} /* * Returns a non-null (possibly empty) list of MethodDescriptionComposites */ public abstract Iterator<MethodDescriptionComposite> retrieveMethods(); /** * A recursive method which peruses and retrieves methods in the super class hierarchy * @param tmpDBC * @param eid * @return */ protected ArrayList<MethodDescriptionComposite> retrieveSEIMethodsChain( DescriptionBuilderComposite tmpDBC, EndpointInterfaceDescriptionImpl eid) { DescriptionBuilderComposite dbc = tmpDBC; ArrayList<MethodDescriptionComposite> retrieveList = new ArrayList<MethodDescriptionComposite>(); retrieveList = retrieveSEIMethods(dbc); //Since this is an interface, anything that is in the extends clause will actually appear // in the interfaces list instead. Iterator<String> iter = null; List<String> interfacesList = dbc.getInterfacesList(); if (interfacesList != null) { iter = dbc.getInterfacesList().iterator(); while (iter.hasNext()) { String interfaceName = iter.next(); DescriptionBuilderComposite superInterface = eid.getEndpointDescriptionImpl().getServiceDescriptionImpl().getDBCMap().get(interfaceName); retrieveList.addAll(retrieveSEIMethodsChain(superInterface, eid)); } } return retrieveList; } /** * This method will loop through each method that was previously determined as being relevant to * the current composite. It will then drive the call to determine if this represents a method * that has been overridden. If it represents an overriding method declaration it will remove * the inherited methods from the list leaving only the most basic method declaration. * * @param methodList - <code>ArrayList</code> list of relevant methods * @param dbc - <code>DescriptionBuilderComposite</code> current composite * @return - <code>ArrayList</code> */ protected ArrayList<MethodDescriptionComposite> removeOverriddenMethods( ArrayList<MethodDescriptionComposite> methodList, DescriptionBuilderComposite dbc, EndpointInterfaceDescriptionImpl eid) { Map<String, Integer> hierarchyMap = dbc.isInterface() ? getInterfaceHierarchy(dbc, eid) : getClassHierarchy(dbc, eid); ArrayList<MethodDescriptionComposite> returnMethods = new ArrayList<MethodDescriptionComposite>(); for (int i = 0; i < methodList.size(); i++) { if (notFound(returnMethods, methodList.get(i))) { returnMethods.add(getBaseMethod(methodList.get(i), i, methodList, hierarchyMap)); } } return returnMethods; } /** * This method drives the establishment of the hierarchy of interfaces for an SEI. */ private Map<String, Integer> getInterfaceHierarchy(DescriptionBuilderComposite dbc, EndpointInterfaceDescriptionImpl eid) { if (log.isDebugEnabled()) { log.debug("Getting interface hierarchy for: " + dbc.getClassName()); } Map<String, Integer> hierarchyMap = new HashMap<String, Integer>(); hierarchyMap.put(dbc.getClassName(), 0); return getInterfaceHierarchy(dbc.getInterfacesList(), hierarchyMap, 1, eid); } protected ArrayList<MethodDescriptionComposite> retrieveSEIMethods(DescriptionBuilderComposite dbc) { //Rules for retrieving Methods on an SEI (or a superclass of an SEI) are simple //Just retrieve all methods regardless of WebMethod annotations ArrayList<MethodDescriptionComposite> retrieveList = new ArrayList<MethodDescriptionComposite>(); Iterator<MethodDescriptionComposite> iter = null; List<MethodDescriptionComposite> mdcList = dbc.getMethodDescriptionsList(); if (mdcList != null) { iter = dbc.getMethodDescriptionsList().iterator(); while (iter.hasNext()) { MethodDescriptionComposite mdc = iter.next(); mdc.setDeclaringClass(dbc.getClassName()); retrieveList.add(mdc); } } return retrieveList; } /** * This method will establish a <code>HashMap</code> that represents a class name of a composite * and an integer value for the entry. The integer represents the classes level in the Java * hierarchy. 0 represents the most basic class with n representing the highest level class. * * @param dbc - <code>DescriptionBuilderComposite</code> * @return - <code>HashMap</code> */ private HashMap<String, Integer> getClassHierarchy(DescriptionBuilderComposite dbc, EndpointInterfaceDescriptionImpl eid) { HashMap<String, DescriptionBuilderComposite> dbcMap = eid.getEndpointDescriptionImpl() .getServiceDescriptionImpl().getDBCMap(); HashMap<String, Integer> hierarchyMap = new HashMap<String, Integer>(); if (log.isDebugEnabled()) { log.debug("Putting class at base level: " + dbc.getClassName()); } hierarchyMap.put(dbc.getClassName(), Integer.valueOf(0)); DescriptionBuilderComposite superDBC = dbcMap.get((dbc.getSuperClassName())); int i = 1; while (superDBC != null && !superDBC.getClassName().equals("java.lang.Object")) { hierarchyMap.put(superDBC.getClassName(), Integer.valueOf(i)); if (log.isDebugEnabled()) { log.debug("Putting class: " + superDBC.getClassName() + " at hierarchy rank: " + i); } i++; superDBC = dbcMap.get(superDBC.getSuperClassName()); } return hierarchyMap; } /** * This method will loop through each method we have already identified as a base method and * compare the current method. * * @param mdcList - <code>ArrayList</code> identified base methods * @param mdc - <code>MethodDescriptionComposite</code> current method * @return - boolean */ private boolean notFound(ArrayList<MethodDescriptionComposite> mdcList, MethodDescriptionComposite mdc) { for (MethodDescriptionComposite method : mdcList) { if (mdc.compare(method)) { return false; } } return true; } /** * Recursive method that builds the hierarchy of interfaces. This begins with an * SEI and walks all of its super interfaces. */ private Map<String, Integer> getInterfaceHierarchy(List<String> interfaces, Map<String, Integer> hierarchyMap, int level, EndpointInterfaceDescriptionImpl eid) { HashMap<String, DescriptionBuilderComposite> dbcMap = eid.getEndpointDescriptionImpl() .getServiceDescriptionImpl().getDBCMap(); // walk through all of the interfaces if (interfaces != null && !interfaces.isEmpty()) { for (String interfaze : interfaces) { DescriptionBuilderComposite interDBC = dbcMap.get(interfaze); if (interDBC != null) { if (log.isDebugEnabled()) { log.debug("Inserting super interface " + interDBC.getClassName() + " at level " + level); } hierarchyMap.put(interDBC.getClassName(), level); return getInterfaceHierarchy(interDBC.getInterfacesList(), hierarchyMap, level++, eid); } } } return hierarchyMap; } /** * This method is responsible for determining the most basic level of a method declaration in * the <code>DescriptionBuilderComposite</code> hierarchy. * * @param mdc - <code>MethodDescriptionComposite</code> current method * @param index - <code>int</code> current location in method list * @param methodList - <code>List</code> list of methods available on this composite * @param hierarchyMap - <code>HashMap</code> map that represents the hierarchy of the current * <code>DescriptionBuilderComposite</code> * @return - <code>MethodDescriptionComposite</code> most basic method declaration */ private static MethodDescriptionComposite getBaseMethod(MethodDescriptionComposite mdc, int index, ArrayList<MethodDescriptionComposite> methodList, Map<String, Integer> hierarchyMap) { int baseLevel = hierarchyMap.get(mdc.getDeclaringClass()); if (log.isDebugEnabled()) { log.debug("Base method: " + mdc.getMethodName() + " initial level: " + baseLevel); } for (; index < methodList.size(); index++) { MethodDescriptionComposite compareMDC = methodList.get(index); // If the two methods are the same method that means we have found an inherited // overridden case if (mdc.equals(compareMDC)) { if (log.isDebugEnabled()) { log.debug("Found equivalent methods: " + mdc.getMethodName()); } // get the declaration level of the method we are comparing to int compareLevel = hierarchyMap.get(compareMDC.getDeclaringClass()); // if the method was declared by a class in a lower level of the hierarchy it // becomes the method that we will compare other methods to if (compareLevel < baseLevel) { if (log.isDebugEnabled()) { log.debug("Found method lower in hierarchy chain: " + compareMDC.getMethodName() + " of class: " + compareMDC.getMethodName()); } mdc = compareMDC; baseLevel = compareLevel; } } } return mdc; } }