| /* |
| * 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 flex.messaging.endpoints.amf; |
| |
| import flex.messaging.endpoints.BaseHTTPEndpoint; |
| import flex.messaging.io.amf.ASObject; |
| import flex.messaging.io.amf.ActionContext; |
| import flex.messaging.io.amf.MessageBody; |
| import flex.messaging.io.amf.MessageHeader; |
| import flex.messaging.messages.Message; |
| import flex.messaging.messages.RemotingMessage; |
| import flex.messaging.messages.ErrorMessage; |
| |
| import java.io.IOException; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.lang.reflect.Array; |
| |
| /** |
| * AMF Headers are of limited use because the apply to the entire AMF packet, which |
| * may contain a batch of several requests. |
| * <p> |
| * Rather than relying on the Flash Player team to change the AMF specification, |
| * Flex 1.5 introduced the concept of a Message Envelope that allowed them to provide |
| * message level headers that apply to a single request body. |
| * </p> |
| * <p> |
| * Essentially they introduced one more layer of indirection with an ASObject of type "Envelope" |
| * that had two properties:<br /> |
| * - <i>headers</i>, which was an array of Header structures<br /> |
| * - <i>body</i>, which was the actual data of the request (typically an array of arguments) |
| * </p> |
| * <p> |
| * To save space on the wire, a Header structure was simply an array. The first element was |
| * the header name as a String, and was the only required field. The second element, a boolean, |
| * indicated whether the header must be understood. The third element, any Object, represented |
| * the header value, if required. |
| * </p> |
| */ |
| public class LegacyFilter extends AMFFilter { |
| public static final String LEGACY_ENVELOPE_FLAG_KEY = "_flag"; |
| public static final String LEGACY_ENVELOPE_FLAG_VALUE = "Envelope"; |
| public static final String LEGACY_SECURITY_HEADER_NAME = "Credentials"; |
| public static final String LEGACY_SECURITY_PRINCIPAL = "userid"; |
| public static final String LEGACY_SECURITY_CREDENTIALS = "password"; |
| |
| private BaseHTTPEndpoint endpoint; |
| |
| public LegacyFilter(BaseHTTPEndpoint endpoint) { |
| this.endpoint = endpoint; |
| } |
| |
| public void invoke(final ActionContext context) throws IOException { |
| MessageBody requestBody = context.getRequestMessageBody(); |
| context.setLegacy(true); |
| |
| // Parameters are usually sent as an AMF Array |
| Object data = requestBody.getData(); |
| List newParams = null; |
| |
| // Check whether we're a new Flex 2.0 Messaging request |
| if (data != null) { |
| if (data.getClass().isArray()) { |
| int paramLength = Array.getLength(data); |
| if (paramLength == 1) { |
| Object obj = Array.get(data, 0); |
| if (obj != null && obj instanceof Message) { |
| context.setLegacy(false); |
| newParams = new ArrayList(); |
| newParams.add(obj); |
| } |
| } |
| |
| // It was not a Flex 2.0 Message, but we have an array, use its contents as our params |
| if (newParams == null) { |
| newParams = new ArrayList(); |
| for (int i = 0; i < paramLength; i++) { |
| try { |
| newParams.add(Array.get(data, i)); |
| } catch (Throwable t) { |
| } |
| } |
| } |
| } else if (data instanceof List) { |
| List paramList = (List) data; |
| if (paramList.size() == 1) { |
| Object obj = paramList.get(0); |
| if (obj != null && obj instanceof Message) { |
| context.setLegacy(false); |
| newParams = new ArrayList(); |
| newParams.add(obj); |
| } |
| } |
| |
| // It was not a Flex 2.0 Message, but we have a list, so use it as our params |
| if (newParams == null) { |
| newParams = (List) data; |
| } |
| } |
| } |
| |
| // We still haven't found any lists of params, so create one with |
| // whatever data we have. |
| if (newParams == null) { |
| newParams = new ArrayList(); |
| newParams.add(data); |
| |
| } |
| |
| if (context.isLegacy()) { |
| newParams = legacyRequest(context, newParams); |
| } |
| |
| requestBody.setData(newParams); |
| |
| |
| next.invoke(context); |
| |
| |
| if (context.isLegacy()) { |
| MessageBody responseBody = context.getResponseMessageBody(); |
| Object response = responseBody.getData(); |
| |
| if (response instanceof ErrorMessage) { |
| ErrorMessage error = (ErrorMessage) response; |
| ASObject aso = new ASObject(); |
| aso.put("message", error.faultString); |
| aso.put("code", error.faultCode); |
| aso.put("details", error.faultDetail); |
| aso.put("rootCause", error.rootCause); |
| response = aso; |
| } else if (response instanceof Message) { |
| response = ((Message) response).getBody(); |
| } |
| responseBody.setData(response); |
| } |
| } |
| |
| private List legacyRequest(ActionContext context, List oldParams) { |
| List newParams = new ArrayList(1); |
| Map headerMap = new HashMap(); |
| Object body = oldParams; |
| Message message = null; |
| MessageBody requestBody = context.getRequestMessageBody(); |
| |
| // Legacy Packet Security |
| List packetHeaders = context.getRequestMessage().getHeaders(); |
| packetCredentials(packetHeaders, headerMap); |
| |
| |
| // Legacy Body |
| if (oldParams.size() == 1) { |
| Object obj = oldParams.get(0); |
| |
| if (obj != null && obj instanceof ASObject) { |
| ASObject aso = (ASObject) obj; |
| |
| // Unwrap legacy Flex 1.5 Envelope type |
| if (isEnvelope(aso)) { |
| body = aso.get("data"); |
| |
| // Envelope level headers |
| Object h = aso.get("headers"); |
| if (h != null && h instanceof List) { |
| readEnvelopeHeaders((List) h, headerMap); |
| envelopeCredentials(headerMap); |
| } |
| } |
| } |
| } |
| |
| // Convert legacy body into a RemotingMessage |
| message = createMessage(requestBody, body, headerMap); |
| newParams.add(message); |
| return newParams; |
| } |
| |
| private boolean isEnvelope(ASObject aso) { |
| String flag = null; |
| Object f = aso.get(LEGACY_ENVELOPE_FLAG_KEY); |
| if (f != null && f instanceof String) |
| flag = (String) f; |
| |
| if (flag != null && flag.equalsIgnoreCase(LEGACY_ENVELOPE_FLAG_VALUE)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| |
| private RemotingMessage createMessage(MessageBody messageBody, Object body, Map headerMap) { |
| RemotingMessage remotingMessage = new RemotingMessage(); |
| // Assigning an empty String, MessageBroker expects non-null messageId. |
| remotingMessage.setMessageId(""); |
| remotingMessage.setBody(body); |
| remotingMessage.setHeaders(headerMap); |
| |
| // Decode legacy target URI into destination.operation |
| String targetURI = messageBody.getTargetURI(); |
| |
| int dotIndex = targetURI.lastIndexOf("."); |
| if (dotIndex > 0) { |
| String destination = targetURI.substring(0, dotIndex); |
| remotingMessage.setDestination(destination); |
| } |
| |
| if (targetURI.length() > dotIndex) { |
| String operation = targetURI.substring(dotIndex + 1); |
| remotingMessage.setOperation(operation); |
| } |
| |
| return remotingMessage; |
| } |
| |
| |
| private Map readEnvelopeHeaders(List headers, Map headerMap) { |
| int count = headers.size(); |
| |
| for (int i = 0; i < count; i++) { |
| Object obj = headers.get(i); |
| |
| //We currently expect a plain old AS Array |
| if (obj != null && obj instanceof List) { |
| List h = (List) obj; |
| |
| Object name = null; |
| //Object mustUnderstand = null; |
| Object data = null; |
| |
| int numFields = h.size(); |
| |
| //The array must have exactly three (3) fields |
| if (numFields == 3) { |
| name = h.get(0); |
| |
| if (name != null && name instanceof String) { |
| //mustUnderstand = h.get(1); |
| data = h.get(2); |
| headerMap.put(name, data); |
| } |
| } |
| } |
| } |
| |
| return headerMap; |
| } |
| |
| private void envelopeCredentials(Map headers) { |
| // Process Legacy Security Credentials |
| Object obj = headers.get(LEGACY_SECURITY_HEADER_NAME); |
| if (obj != null && obj instanceof ASObject) { |
| ASObject header = (ASObject) obj; |
| String principal = (String) header.get(LEGACY_SECURITY_PRINCIPAL); |
| Object credentials = header.get(LEGACY_SECURITY_CREDENTIALS); |
| endpoint.getMessageBroker().getLoginManager().login(principal, credentials.toString()); |
| } |
| headers.remove(LEGACY_SECURITY_HEADER_NAME); |
| } |
| |
| private void packetCredentials(List packetHeaders, Map headers) { |
| if (packetHeaders.size() > 0) { |
| for (Iterator iter = packetHeaders.iterator(); iter.hasNext(); ) { |
| MessageHeader header = (MessageHeader) iter.next(); |
| if (header.getName().equals(LEGACY_SECURITY_HEADER_NAME)) { |
| Map loginInfo = (Map) header.getData(); |
| String principal = loginInfo.get(LEGACY_SECURITY_PRINCIPAL).toString(); |
| Object credentials = loginInfo.get(LEGACY_SECURITY_CREDENTIALS); |
| endpoint.getMessageBroker().getLoginManager().login(principal, credentials.toString()); |
| break; |
| } |
| } |
| } |
| } |
| } |