/* * 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.handler; import org.apache.axis2.jaxws.core.MessageContext; import org.apache.axis2.jaxws.description.EndpointDescription; import org.apache.axis2.jaxws.message.Message; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * The <tt>MEPContext</tt> is the version of the MessageContext * that will be given to application handlers as the handler list * is traversed. It is only to be used by application handlers. * * The MEPContext object is constructed using a non-null request * context. Once the request has been fully processed in the JAX-WS engine, * the response context should be set on this. Since the response context * is always last, it takes priority in all MEPContext methods. * */ public class MEPContext implements javax.xml.ws.handler.MessageContext { // If this a request flow, then the MEP contains the request MC. // If this a response flow, then the MEP contains both the request MC and the response MC. // (Note that access to the requestMC properties is sometimes synchronized in the // response flow.) protected MessageContext requestMC; protected MessageContext responseMC; private Map<String, Scope> scopes; // APPLICATION or HANDLER scope for properties /* * Flag to indicate whether we're being called from a handler or an application * (endpoint or client). Users of MEPContext should use the 'is' and * 'set' appropriately for this flag. The most likely scenario is to set the * flag to true after the server-side inbound handlers are complete. * * TODO, all methods should use this flag to check for access rights */ private boolean ApplicationAccessLocked = false; /* * Ideally this would be "protected", but we want the junit tests to see it. */ public MEPContext(MessageContext requestMsgCtx) { this.requestMC = requestMsgCtx; scopes = new HashMap<String, Scope>(); // make sure the MessageContext points back to this requestMsgCtx.setMEPContext(this); } public EndpointDescription getEndpointDesc() { if (responseMC != null) { return responseMC.getEndpointDescription(); } return requestMC.getEndpointDescription(); } public MessageContext getRequestMessageContext() { return requestMC; } public MessageContext getResponseMessageContext() { return responseMC; } public MessageContext getMessageContext() { if (responseMC != null) { return responseMC; } return requestMC; } public void setResponseMessageContext(MessageContext responseMC) { if(this.responseMC != null) { responseMC.setProperty(javax.xml.ws.handler.MessageContext.OUTBOUND_MESSAGE_ATTACHMENTS, this.responseMC.getProperty(javax.xml.ws.handler.MessageContext.OUTBOUND_MESSAGE_ATTACHMENTS)); } // TODO does ApplicationAccessLocked mean anything here? -- method is protected, so probably not this.responseMC = responseMC; // if callers are being careful, the responseMC should not be set // until the engine is done invoking the endpoint, on both server and // client side. At that point, we can start allowing callers access // to HANDLER scoped properties again. Set the flag: ApplicationAccessLocked = false; } public void setMessage(Message msg) { if (responseMC != null) { responseMC.setMessage(msg); } else { requestMC.setMessage(msg); } } public Scope getScope(String s) { if (scopes.get(s) == null) { // JAX-WS default 9.4.1. However, we try to set the scope for // every incoming property to HANDLER. If a property is coming from // the axis2 AbstractContext properties bag, we want those to be // APPLICATION scoped. Those properties may have been set by an // axis application handler, and may need to be accessible by // a client app or endpoint. return Scope.APPLICATION; } return scopes.get(s); } public void setScope(String s, Scope scope) { // TODO review next two lines if (isApplicationAccessLocked()) { // endpoints are not allowed to change property scope. They should all be APPLICATION scoped anyway return; } scopes.put(s, scope); } //-------------------------------------------------- // java.util.Map methods //-------------------------------------------------- public void clear() { // TODO review if (isApplicationAccessLocked()) { // endpoints are allowed to clear APPLICATION scoped properties only Map<String, Object> appScopedProps = getApplicationScopedProperties(); for(Iterator it = appScopedProps.keySet().iterator(); it.hasNext();) { String key = (String)it.next(); remove(key); // TODO also remove Scope setting for "key"? How? } return; } // TODO: REVIEW // I don't think this will work if the message contexts have a copy // of the map if (responseMC != null) { responseMC.getProperties().clear(); } synchronized (requestMC) { requestMC.getProperties().clear(); } } public boolean containsKey(Object key) { if (isApplicationAccessLocked()) { return getApplicationScopedProperties().containsKey(key); } synchronized (requestMC) { if (responseMC != null) { boolean containsKey = responseMC.containsKey(key) || requestMC.containsKey(key); if ((getScope((String)key) == Scope.APPLICATION) || (!isApplicationAccessLocked())) { return containsKey; } } if ((getScope((String)key) == Scope.APPLICATION) || (!isApplicationAccessLocked())) { return requestMC.containsKey(key); } } return false; } public boolean containsValue(Object value) { if (isApplicationAccessLocked()) { return getApplicationScopedProperties().containsValue(value); } if (responseMC != null) { if (responseMC.getProperties().containsValue(value)) { return true; } synchronized (requestMC) { return requestMC.getProperties().containsValue(value); } } return requestMC.getProperties().containsValue(value); } public Set entrySet() { // TODO should check ApplicationAccessLocked flag // and return only APPLICATION scoped properties if true if (isApplicationAccessLocked()) { return getApplicationScopedProperties().entrySet(); } HashMap tempProps = new HashMap(); synchronized (requestMC) { tempProps.putAll(requestMC.getProperties()); } if (responseMC != null) { tempProps.putAll(responseMC.getProperties()); } return tempProps.entrySet(); } public Object get(Object keyObject) { String key = (String) keyObject; if (responseMC != null) { if (responseMC.getProperty(key) != null) { if ((getScope((String)key) == Scope.APPLICATION) || (!isApplicationAccessLocked())) { return responseMC.getProperty(key); } } } synchronized (requestMC) { if ((getScope((String)key) == Scope.APPLICATION) || (!isApplicationAccessLocked())) { return requestMC.getProperty(key); } } return null; } public boolean isEmpty() { if (isApplicationAccessLocked()) { return getApplicationScopedProperties().isEmpty(); } synchronized (requestMC) { if (responseMC != null) { return requestMC.getProperties().isEmpty() && requestMC.getProperties().isEmpty(); } return requestMC.getProperties().isEmpty(); } } public Set keySet() { if (isApplicationAccessLocked()) { return getApplicationScopedProperties().keySet(); } HashMap tempProps = new HashMap(); synchronized (requestMC) { tempProps.putAll(requestMC.getProperties()); } if (responseMC != null) { tempProps.putAll(responseMC.getProperties()); } return tempProps.keySet(); } public Object put(String key, Object value) { // TODO careful: endpoints may overwrite pre-existing key/value pairs. // Those key/value pairs may already have a scope attached to them, which // means an endpoint could "put" a property that is wrongly scoped if (scopes.get(key) == null) { // check the scopes object directly, not through getScope()!! setScope(key, Scope.HANDLER); } synchronized (requestMC) { if (requestMC.containsKey(key)) { return requestMC.setProperty(key, value); } if (responseMC != null) { return responseMC.setProperty(key, value); } return requestMC.setProperty(key, value); } } public void putAll(Map t) { // TODO similar problem as "put" for(Iterator it = t.entrySet().iterator(); it.hasNext();) { Entry<String, Object> entry = (Entry)it.next(); if (getScope(entry.getKey()) == null) { setScope(entry.getKey(), Scope.HANDLER); } } if (responseMC != null) { responseMC.setProperties(t); } else { synchronized (requestMC) { requestMC.setProperties(t); } } } public Object remove(Object key) { // check ApplicationAccessLocked flag and prevent removal of HANDLER scoped props if (isApplicationAccessLocked()) { if (getScope((String)key).equals(Scope.HANDLER)) { return null; } } // yes, remove from both and return the right object // TODO This won't work because getProperties returns a temporary map Object retVal = null; if (responseMC != null) { retVal = responseMC.getProperties().remove(key); } synchronized (requestMC) { if (retVal == null) { return requestMC.getProperties().remove(key); } else { requestMC.getProperties().remove(key); } } return retVal; } public int size() { if (isApplicationAccessLocked()) { return getApplicationScopedProperties().size(); } // The properties must be combined together because some // keys may be the same on the request and the response. HashMap tempProps = new HashMap(); synchronized (requestMC) { tempProps.putAll(requestMC.getProperties()); } if (responseMC != null) { tempProps.putAll(responseMC.getProperties()); } return tempProps.size(); } public Collection values() { if (isApplicationAccessLocked()) { return getApplicationScopedProperties().values(); } HashMap tempProps = new HashMap(); synchronized (requestMC) { tempProps.putAll(requestMC.getProperties()); } if (responseMC != null) { tempProps.putAll(responseMC.getProperties()); } return tempProps.values(); } public Message getMessageObject() { // TODO does ApplicationAccessLocked apply here? if (responseMC != null) { return responseMC.getMessage(); } return requestMC.getMessage(); } public boolean isApplicationAccessLocked() { // since MEPContext is both a wrapper and a subclass, we need to be careful to set it only on the wrapper object: if (this == requestMC.getMEPContext()) { // object compare, I am the wrapper object return ApplicationAccessLocked; } if (responseMC == null) { return requestMC.getMEPContext().isApplicationAccessLocked(); } else { return responseMC.getMEPContext().isApplicationAccessLocked() || requestMC.getMEPContext().isApplicationAccessLocked(); } } public void setApplicationAccessLocked(boolean applicationAccessLocked) { // since MEPContext is both a wrapper and a subclass, we need to be careful to set it only on the wrapper object: if (this == requestMC.getMEPContext()) { // object compare, I am the wrapper object ApplicationAccessLocked = applicationAccessLocked; } else { requestMC.getMEPContext().setApplicationAccessLocked(applicationAccessLocked); } } /** * The returned tempMap should be used as a read-only map as changes to it will * not propogate into the requestMC or responseMC * * Watch out for infinite loop if you call another method in this class that uses this method. * * @return */ public Map<String, Object> getApplicationScopedProperties() { Map<String, Object> tempMap = new HashMap<String, Object>(); if (!scopes.containsValue(Scope.APPLICATION)) { return tempMap; } synchronized (requestMC) { for(Iterator it = requestMC.getProperties().entrySet().iterator(); it.hasNext();) { Entry entry = (Entry)it.next(); if (getScope((String)entry.getKey()).equals(Scope.APPLICATION)) { tempMap.put((String)entry.getKey(), entry.getValue()); } } } if (responseMC != null) { for(Iterator it = responseMC.getProperties().entrySet().iterator(); it.hasNext();) { Entry entry = (Entry)it.next(); if (getScope((String)entry.getKey()).equals(Scope.APPLICATION)) { tempMap.put((String)entry.getKey(), entry.getValue()); } } } return tempMap; } }