| /* |
| * 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.spi.migrator; |
| |
| import org.apache.axis2.context.ConfigurationContext; |
| import org.apache.axis2.jaxws.ExceptionFactory; |
| import org.apache.axis2.jaxws.core.MessageContext; |
| import org.apache.axis2.jaxws.description.ServiceDescription; |
| import org.apache.axis2.jaxws.handler.MEPContext; |
| import org.apache.axis2.jaxws.i18n.Messages; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import javax.xml.ws.handler.MessageContext.Scope; |
| import java.util.AbstractSet; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Map; |
| import java.util.Set; |
| |
| public class ApplicationContextMigratorUtil { |
| |
| private static final Log log = LogFactory.getLog(ApplicationContextMigrator.class); |
| |
| /** |
| * Register a new ContextPropertyMigrator. |
| * |
| * @param configurationContext |
| * @param contextMigratorListID The name of the property in the ConfigurationContext that |
| * contains the list of migrators. |
| * @param migrator |
| */ |
| public static void addApplicationContextMigrator(ConfigurationContext configurationContext, |
| String contextMigratorListID, |
| ApplicationContextMigrator migrator) { |
| List<ApplicationContextMigrator> migratorList = |
| (List<ApplicationContextMigrator>)configurationContext |
| .getProperty(contextMigratorListID); |
| |
| if (migratorList == null) { |
| migratorList = new LinkedList<ApplicationContextMigrator>(); |
| configurationContext.setProperty(contextMigratorListID, migratorList); |
| } |
| |
| synchronized (migratorList) { |
| // Check to make sure we haven't already added this migrator to the |
| // list. |
| ListIterator<ApplicationContextMigrator> itr = migratorList.listIterator(); |
| while (itr.hasNext()) { |
| ApplicationContextMigrator m = itr.next(); |
| if (m.getClass().equals(migrator.getClass())) { |
| return; |
| } |
| } |
| |
| if (log.isDebugEnabled()) { |
| log.debug("Adding ApplicationContextMigrator: " + migrator.getClass().getName()); |
| } |
| migratorList.add(migrator); |
| } |
| } |
| |
| /** |
| * @param contextMigratorListID |
| * @param requestContext |
| * @param messageContext |
| */ |
| public static void performMigrationToMessageContext(String contextMigratorListID, |
| Map<String, Object> requestContext, |
| MessageContext messageContext) { |
| if (messageContext == null) { |
| |
| throw ExceptionFactory.makeWebServiceException(Messages.getMessage("nullMsgCtxErr")); |
| } |
| |
| ServiceDescription sd = messageContext.getEndpointDescription().getServiceDescription(); |
| if (sd != null) { |
| ConfigurationContext configCtx = sd.getAxisConfigContext(); |
| List<ApplicationContextMigrator> migratorList = (List<ApplicationContextMigrator>) configCtx.getProperty(contextMigratorListID); |
| if (migratorList != null) { |
| |
| // Create copy to avoid using shared list |
| List listCPM = null; |
| |
| // synchronize on non-null migratorList |
| synchronized(migratorList){ |
| listCPM = new ArrayList(migratorList); |
| } |
| |
| ListIterator<ApplicationContextMigrator> itr = listCPM.listIterator(); // Iterate over non-shared list |
| while (itr.hasNext()) { |
| ApplicationContextMigrator cpm = itr.next(); |
| if (log.isDebugEnabled()) { |
| log.debug("migrator: " + cpm.getClass().getName() + ".migratePropertiesToMessageContext"); |
| } |
| |
| // TODO: Synchronizing here is expensive too. |
| // If a cpm requires synchronization, it should provide it inside of its migratePropertiesFromMessageContext implementation. |
| |
| cpm.migratePropertiesToMessageContext(new ApplicationPropertyMapReader(requestContext, messageContext.getMEPContext()), messageContext); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @param contextMigratorListID |
| * @param responseContext |
| * @param messageContext |
| */ |
| public static void performMigrationFromMessageContext(String contextMigratorListID, |
| Map<String, Object> responseContext, |
| MessageContext messageContext) { |
| if (messageContext == null) { |
| throw ExceptionFactory.makeWebServiceException(Messages.getMessage("nullMsgCtxErr")); |
| } |
| |
| ServiceDescription sd = messageContext.getEndpointDescription().getServiceDescription(); |
| if (sd != null) { |
| ConfigurationContext configCtx = sd.getAxisConfigContext(); |
| List<ApplicationContextMigrator> migratorList = |
| (List<ApplicationContextMigrator>)configCtx.getProperty(contextMigratorListID); |
| |
| if (migratorList != null) { |
| |
| // Create copy to avoid using shared list |
| List listCPM = null; |
| |
| // synchronize on non-null migratorList |
| synchronized(migratorList){ |
| listCPM = new ArrayList(migratorList); |
| } |
| |
| ListIterator<ApplicationContextMigrator> itr = listCPM.listIterator(); // Iterate over non-shared list |
| while (itr.hasNext()) { |
| ApplicationContextMigrator cpm = itr.next(); |
| if (log.isDebugEnabled()) { |
| log.debug("migrator: " + cpm.getClass().getName() + ".migratePropertiesFromMessageContext"); |
| } |
| |
| // TODO: Synchronizing here is expensive too. |
| // If a cpm requires synchronization, it should provide it inside of its migratePropertiesFromMessageContext implementation. |
| |
| cpm.migratePropertiesFromMessageContext(new ApplicationPropertyMapWriter(responseContext, messageContext.getMEPContext()), messageContext); |
| } |
| } |
| } |
| } |
| |
| |
| /** |
| * |
| * ApplicationPropertyMapReader is a wrapper for the SOURCE property map passed to individual |
| * property migrators. When a property migrator copies properties from a request context map |
| * to a JAXWS MessageContext object, all of those properties should be marked APPLICATION |
| * scope so they can later be retrieved from the request context or response context |
| * in the client application. |
| * |
| * We override the EntrySet and Iterator to make sure the scope is properly set in the |
| * "request context to JAXWS message context" case where the property migrator uses |
| * get(String key) or putAll(Map source). This is not guaranteed to be correct, however, |
| * because a property migrator could simply be doing a get(String key) to observe properties |
| * rather than copy them. This just means we might be setting scope for a property that |
| * never actually makes its way into the JAXWS message context. If someone (a hander, |
| * perhaps) later sets a property with the same key, its scope may be "pre-set" and |
| * therefore incorrect. |
| * |
| * TODO: find solution to above problem. The MEPContext.put sets an explicit scope whenever |
| * a property is and a scope is not already present for that property. An example |
| * of where this idea would produce unexpected results is where a scope was set to APPLICATION |
| * in the property migrator for key/value pair "myKey/someValue", but myKey never actually made |
| * it into the messagecontext. Later a handler might put a "myKey/theHandlerValue". In this |
| * case the scope was already set to APPLICATION and would therefore not be set by the |
| * MEPContext.put and therefore be incorrect. |
| * |
| * ApplicationPropertyMapReader only sets the scope if a migrator calls "get" on this map or |
| * iterates over the entrySet, which may occur explicitly in the migrator, or implicitly when |
| * this map is the source for a call such as otherMap.putAll(Map source). |
| */ |
| private static class ApplicationPropertyMapReader extends HashMap<String, Object> { |
| |
| private Map<String, Object> userMap; |
| private MEPContext mepCtx; |
| |
| public ApplicationPropertyMapReader(Map<String, Object> userMap, MEPContext mepCtx) { |
| this.userMap = userMap; |
| this.mepCtx = mepCtx; |
| } |
| |
| @Override |
| public Object put(String key, Object value) { |
| //mepCtx.setScope(key, Scope.APPLICATION); |
| return userMap.put(key, value); |
| } |
| |
| @Override |
| public void putAll(Map<? extends String, ? extends Object> m) { |
| // we need to take advantage of the smarter put(String, Object) |
| for (Iterator it = m.entrySet().iterator(); it.hasNext();) { |
| Map.Entry entry = (Map.Entry)it.next(); |
| put((String)entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| @Override |
| public boolean containsKey(Object key) { |
| return userMap.containsKey(key); |
| } |
| |
| @Override |
| public boolean containsValue(Object value) { |
| return userMap.containsValue(value); |
| } |
| |
| @Override |
| public Set entrySet() { |
| return new ApplicationPropertyMapEntrySet(userMap.entrySet(), mepCtx); |
| } |
| |
| @Override |
| public Object get(Object key) { |
| // WARNING: there's no real guarantee that the reason a migrator is getting |
| // a property is due to it being put on the MessageContext. |
| // We would therefore be setting scope for a property that never actually makes |
| // its way into the messageContext. |
| Object obj = userMap.get(key); |
| if (obj != null) { |
| mepCtx.setScope((String)key, Scope.APPLICATION); |
| } |
| return obj; |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return userMap.isEmpty(); |
| } |
| |
| @Override |
| public Set keySet() { |
| return userMap.keySet(); |
| } |
| |
| @Override |
| public Object remove(Object key) { |
| return userMap.remove(key); |
| } |
| |
| @Override |
| public int size() { |
| return userMap.size(); |
| } |
| |
| @Override |
| public Collection values() { |
| return userMap.values(); |
| } |
| |
| private class ApplicationPropertyMapEntrySet extends AbstractSet { |
| |
| Set containedSet; |
| MEPContext mepCtx; |
| |
| public ApplicationPropertyMapEntrySet(Set set, MEPContext mepCtx) { |
| containedSet = set; |
| this.mepCtx = mepCtx; |
| } |
| |
| @Override |
| public EntrySetIterator iterator() { |
| return new EntrySetIterator(containedSet.iterator(), mepCtx); |
| } |
| |
| @Override |
| public int size() { |
| return containedSet.size(); |
| } |
| |
| } |
| |
| private class EntrySetIterator implements Iterator { |
| |
| private Iterator containedIterator; |
| private MEPContext mepCtx; |
| |
| private EntrySetIterator(Iterator containedIterator, MEPContext mepCtx) { |
| this.containedIterator = containedIterator; |
| this.mepCtx = mepCtx; |
| } |
| |
| // override remove() to make this Iterator class read-only |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public boolean hasNext() { |
| return containedIterator.hasNext(); |
| } |
| |
| public Object next() { |
| // WARNING: there's no real guarantee that the reason a migrator is iterating |
| // over the properties is due to this being the source object for a putAll(source) |
| // We would therefore be setting scope for a property that never actually makes |
| // its way into the messageContext |
| Map.Entry entry = (Map.Entry)containedIterator.next(); |
| mepCtx.setScope((String)entry.getKey(), Scope.APPLICATION); |
| return entry; |
| } |
| } |
| } |
| |
| /** |
| * ApplicationPropertyMapWriter is similar to the ApplicationPropertyMapReader in that it |
| * observes scope to determine what can be returned to a property migrator. Individual |
| * property migrators should only be allowed to retrieve APPLICATION-scoped properties. |
| * |
| * TODO: There's quite a bit of expensive logic that would need to go into this to be |
| * fully correct. For example, if a migrator calls size, we cannot simply return |
| * userMap.size(). Rather, we would have to count only the APPLICATION scoped properties |
| * and return those. |
| */ |
| private static class ApplicationPropertyMapWriter extends HashMap<String, Object> { |
| |
| private Map<String, Object> userMap; |
| private MEPContext mepCtx; |
| |
| public ApplicationPropertyMapWriter(Map<String, Object> userMap, MEPContext mepCtx) { |
| this.userMap = userMap; |
| this.mepCtx = mepCtx; |
| } |
| |
| @Override |
| public Object put(String key, Object value) { |
| // notice the logic here! We won't put a property on the userMap that is not APPLICATION scoped |
| if (mepCtx.getScope(key) == Scope.APPLICATION) { |
| return userMap.put(key, value); |
| } |
| return null; |
| } |
| |
| @Override |
| public void putAll(Map<? extends String, ? extends Object> m) { |
| // we need to take advantage of the smarter put(String, Object) |
| for (Iterator it = m.entrySet().iterator(); it.hasNext();) { |
| Map.Entry entry = (Map.Entry)it.next(); |
| put((String)entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| @Override |
| public boolean containsKey(Object key) { |
| if (mepCtx.getScope((String)key) == Scope.APPLICATION) { |
| return userMap.containsKey(key); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean containsValue(Object value) { |
| return userMap.containsValue(value); |
| } |
| |
| @Override |
| public Set entrySet() { |
| return userMap.entrySet(); |
| } |
| |
| @Override |
| public Object get(Object key) { |
| return userMap.get(key); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return userMap.isEmpty(); |
| } |
| |
| @Override |
| public Set keySet() { |
| return userMap.keySet(); |
| } |
| |
| @Override |
| public Object remove(Object key) { |
| return userMap.remove(key); |
| } |
| |
| @Override |
| public int size() { |
| return userMap.size(); |
| } |
| |
| @Override |
| public Collection values() { |
| return userMap.values(); |
| } |
| } |
| } |