blob: fc52755e3deeef8fcedc4bd4905d325144ae5d6b [file] [log] [blame]
/*
* 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 &quot;Envelope&quot;
* 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;
}
}
}
}
}