blob: d9344f298205d0d132b4c80cc2245be0254667a9 [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.messages;
import flex.messaging.FlexContext;
import flex.messaging.endpoints.AbstractEndpoint;
import flex.messaging.io.amf.ActionContext;
import flex.messaging.log.Log;
import flex.messaging.log.LogCategories;
/**
* Utility class for populating MessagePerformanceInformation objects at various stages of
* server processing. A given message may have three MPI headers populated (naming convention
* of these headers is from the perspective of the server):
* <p>
* DSMPII - incoming message, message sent from client to server
* DSMPIO - outgoing message, response/acknowledgement message sent from server back to client
* DSMPIP - only populated for a pushed message, this is information for the incoming message
* that caused the pushed message
*/
public class MessagePerformanceUtils {
static final String LOG_CATEGORY = LogCategories.MESSAGE_GENERAL;
public static final String MPI_HEADER_IN = "DSMPII";
public static final String MPI_HEADER_OUT = "DSMPIO";
public static final String MPI_HEADER_PUSH = "DSMPIP";
public static int MPI_NONE = 0;
public static int MPI_TIMING = 1;
public static int MPI_TIMING_AND_SIZING = 2;
/**
* Clones the MPI object for the incoming message from client to server
* from the batch wrapper to all messages included in it, keeping track of
* overhead time spent doing this.
*
* @param message The message whose MPI should be propogated
*/
public static void propogateMPIDownBatch(Message message) {
long overhead = System.currentTimeMillis();
if (message instanceof BatchableMessage) {
BatchableMessage dm = (BatchableMessage) message;
if (dm.isBatched()) {
Object[] batchedMessages = (Object[]) message.getBody();
int batchedLength = batchedMessages.length;
for (int a = 0; a < batchedLength; a++) {
Message currentMess = (Message) batchedMessages[a];
MessagePerformanceInfo mpi = MessagePerformanceUtils.getMPII(message);
MessagePerformanceUtils.setMPII(currentMess, (MessagePerformanceInfo) mpi.clone());
propogateMPIDownBatch(currentMess);
}
}
}
overhead = System.currentTimeMillis() - overhead;
MessagePerformanceUtils.getMPII(message).addToOverhead(overhead);
}
/**
* This method finalizes the incoming MPI instance. It is necessary because the client
* send time is stored on the incoming MPII and the server receive time as well as message
* size are populated in the MPI object stored on the ActionContext, this method combines
* the information into one MPI instance.
*
* @param context - The action context used to deserialize the incoming message, it will have the
* server receive time and message size information
* @param inMessage - The incoming message, its MPI will have the client send time
*/
public static void setupMPII(ActionContext context, Message inMessage) {
try {
// the MPI from the incoming message will have the client-send timestamp
MessagePerformanceInfo mpii = MessagePerformanceUtils.getMPII(inMessage);
// this is the MPI that we want to propogate
MessagePerformanceInfo contextMPI = context.getMPII();
if (contextMPI != null && mpii != null) {
contextMPI.sendTime = mpii.sendTime;
MessagePerformanceUtils.setMPII(inMessage, (MessagePerformanceInfo) contextMPI.clone());
propogateMPIDownBatch(inMessage);
}
} catch (Exception e) {
if (Log.isDebug())
Log.getLogger(LOG_CATEGORY).error("MPI error: setting up response MPI : " +
e.toString());
}
}
/**
* This method sets up the outgoing MPI object for a response/acknowledgement message.
* The outgoing message should also have a copy of the incoming MPI so that when the client
* receives the response it has access to all information
*
* @param context - The context used to deserialize the incoming message
* @param inMessage - The incoming message
* @param outMessage - The response message
*/
public static void updateOutgoingMPI(ActionContext context, Message inMessage, Object outMessage) {
try {
MessagePerformanceInfo mpio = null;
if (context != null) {
mpio = context.getMPIO();
if (mpio == null) {
mpio = new MessagePerformanceInfo();
if (MessagePerformanceUtils.getMPII(inMessage) != null && MessagePerformanceUtils.getMPII(inMessage).sendTime != 0)
mpio.infoType = "OUT";
}
Message mess = (Message) outMessage;
if (MessagePerformanceUtils.getMPII(inMessage) != null)
MessagePerformanceUtils.setMPII(mess, (MessagePerformanceInfo) MessagePerformanceUtils.getMPII(inMessage).clone());
MessagePerformanceUtils.setMPIO(mess, mpio);
context.setMPIO(mpio);
}
if (outMessage instanceof CommandMessage &&
((CommandMessage) outMessage).getOperation() == CommandMessage.CLIENT_SYNC_OPERATION) {
// pushed message case
CommandMessage cmd = (CommandMessage) outMessage;
Object[] cmdBody = (Object[]) cmd.getBody();
// in the pushed message case we want the outgoing metrics of the message to be updated
// however, we want the incoming metrics to be that of the poll message and
// the incoming push metrics to be that of the original message
int batchedLength = cmdBody.length;
for (int i = 0; i < batchedLength; i++) {
Message currentMess = (Message) cmdBody[i];
MessagePerformanceInfo origMPII = MessagePerformanceUtils.getMPII(currentMess);
if (origMPII == null || MessagePerformanceUtils.getMPII(inMessage) == null) {
// this can happen if the server has MPI enabled but the producing client does not
// log a warning for this and break out of here as MPI requires all participating
// parties to have the same settings
if (Log.isError()) {
Log.getLogger(LOG_CATEGORY).error
(
"MPI is enabled but could not get message performance information " +
"for incoming MPI instance from client message. The client " +
"might have created a new channel not configured to send message" +
" performance after declaring a different destination which does." +
" The client channel should either be configured to add MPI " +
"properties, or a server destion which has the same MPI " +
"properties as the client channel should be used.");
}
return;
}
MessagePerformanceUtils.setMPIP(currentMess, (MessagePerformanceInfo) origMPII.clone());
MessagePerformanceInfo newMPII = (MessagePerformanceInfo) MessagePerformanceUtils.getMPII(inMessage).clone();
mpio.pushedFlag = true;
MessagePerformanceUtils.setMPII(currentMess, newMPII);
MessagePerformanceUtils.setMPIO(currentMess, mpio);
}
}
} catch (Exception e) {
if (Log.isDebug())
Log.getLogger(LOG_CATEGORY).error("MPI error: setting up response MPI : " +
e.toString());
}
}
/**
* Convenience method for setting the incoming MPI object on a message.
*
* @param message The message whose MPI header will be set
* @param mpi The incoming MPI instance
*/
public static void setMPII(Message message, MessagePerformanceInfo mpi) {
message.setHeader(MPI_HEADER_IN, mpi);
}
/**
* The server generated messages do not have MPII object (which is normally set
* by the client) which prevents the usual performance gathering. This convenience method
* can be invoked to set the MPII object (as if it's generated by the client) once the
* message is generated on the server but before it is routed to the MessageBroker
* to be delivered to the client. This way performance gathering can proceed as expected.
*
* @param message The server generated message.
*/
public static void setMPIIForServerGeneratedMessage(Message message) {
MessagePerformanceInfo mpii = new MessagePerformanceInfo();
AbstractEndpoint endpoint = (AbstractEndpoint) FlexContext.getEndpoint();
mpii.recordMessageSizes = endpoint != null ? endpoint.isRecordMessageSizes() : false;
mpii.recordMessageTimes = endpoint != null ? endpoint.isRecordMessageTimes() : false;
mpii.sendTime = System.currentTimeMillis();
MessagePerformanceUtils.setMPII(message, mpii);
}
/**
* Convenience method for setting the outgoing MPI object on a message.
*
* @param message The message whose MPI header will be set
* @param mpi The outgoing MPI instance
*/
public static void setMPIO(Message message, MessagePerformanceInfo mpi) {
message.setHeader(MPI_HEADER_OUT, mpi);
}
/**
* Convenience method for setting the pushed MPI object on a message.
*
* @param message The message whose MPI header will be set
* @param mpi The pushed MPI instance (this is the incoming MPI instance
* of the message that caused the push)
*/
public static void setMPIP(Message message, MessagePerformanceInfo mpi) {
message.setHeader(MPI_HEADER_PUSH, mpi);
}
/**
* Convenience method for retrieving the incoming MPI object from a message.
*
* @param message The message whose MPI header will be retrieved
* @return mpi Incoming MPI instance
*/
public static MessagePerformanceInfo getMPII(Message message) {
return (MessagePerformanceInfo) message.getHeader(MPI_HEADER_IN);
}
/**
* Convenience method for retrieving the outgoing MPI object from a message.
*
* @param message The message whose MPI header will be retrieved
* @return mpi Outgoing MPI instance
*/
public static MessagePerformanceInfo getMPIO(Message message) {
return (MessagePerformanceInfo) message.getHeader(MPI_HEADER_OUT);
}
/**
* Convenience method for retrieving the pushed MPI object from a message.
*
* @param message The message whose MPI header will be retrieved
* @return mpi Pushed MPI instance (this is the incoming MPI instance
* of the message that caused the push)
*/
public static MessagePerformanceInfo getMPIP(Message message) {
return (MessagePerformanceInfo) message.getHeader(MPI_HEADER_PUSH);
}
/**
* Convenience method for setting server pre-push processing time on a message.
* No-op if record-message-times is false
*
* @param message The message whose MPI header will be updated
*/
public static void markServerPrePushTime(Message message) {
// If the message does not have an MPI header then we are not recording message times
// and we have nothing to do here
if (getMPII(message) == null || getMPII(message).sendTime == 0)
return;
MessagePerformanceInfo mpi = getMPII(message);
mpi.serverPrePushTime = System.currentTimeMillis();
}
/**
* Convenience method for setting server pre-adapter timestamp on a message.
* No-op if record-message-times is false
*
* @param message The message whose MPI header will be updated
*/
public static void markServerPreAdapterTime(Message message) {
// If the message does not have an MPI header then we are not recording message times
// and we have nothing to do here
if (getMPII(message) == null || getMPII(message).sendTime == 0)
return;
MessagePerformanceInfo mpi = getMPII(message);
// it is possible that a batched message will have this called multiple times,
// do not reset stamp once it has been set
if (mpi.serverPreAdapterTime != 0)
return;
mpi.serverPreAdapterTime = System.currentTimeMillis();
}
/**
* Convenience method for setting server post-adapter timestamp on a message.
* No-op if record-message-times is false
*
* @param message The message whose MPI header will be updated
*/
public static void markServerPostAdapterTime(Message message) {
// If the message does not have an MPI header then we are not recording message times
// and we have nothing to do here
if (getMPII(message) == null || getMPII(message).sendTime == 0 || getMPII(message).serverPostAdapterTime != 0)
return;
MessagePerformanceInfo mpi = getMPII(message);
mpi.serverPostAdapterTime = System.currentTimeMillis();
}
/**
* Method may be called from a custom adapter to mark the beginning of processing that occurs
* outside of the adapter for a particular message. Use this method in conjunction with
* <code>markServerPostAdapterExternalTime</code> to mark the amount of time spent when your
* adapter must make a call to an external component. If <code>record-message-times</code> is
* <code>true</code> for the communication channel, the server processing time external to the
* adapter may be retrieved via MessagePerformanceUtils.serverAdapterExternalTime on the client
* once it receives the message.
* <p>
* If <code>record-message-times</code> is <code>false</code> for the communication channel,
* calling this method will have no effect.
*
* @param message The message being processed
*/
public static void markServerPreAdapterExternalTime(Message message) {
// If the message does not have an MPI header then we are not recording message times
// and we have nothing to do here
if (getMPII(message) == null || getMPII(message).sendTime == 0)
return;
MessagePerformanceInfo mpi = getMPII(message);
mpi.serverPreAdapterExternalTime = System.currentTimeMillis();
}
/**
* Method may be called from a custom adapter to mark the end of processing that occurs
* outside of the adapter for a particular message. Use this method in conjunction with
* <code>markServerPreAdapterExternalTime</code> to mark the amount of time spent when your
* adapter must make a call to an external component. If <code>record-message-times</code> is
* <code>true</code> for the communication channel, the server processing time external to the
* adapter may be retrieved via MessagePerformanceUtils.serverAdapterExternalTime on the client
* once it receives the message.
* <p>
* If <code>record-message-times</code> is <code>false</code> for the communication channel,
* calling this method will have no effect.
*
* @param message The message being processed
*/
public static void markServerPostAdapterExternalTime(Message message) {
// If the message does not have an MPI header then we are not recording message times
// and we have nothing to do here
if (getMPII(message) == null || getMPII(message).sendTime == 0 || getMPII(message).serverPostAdapterExternalTime != 0)
return;
MessagePerformanceInfo mpi = getMPII(message);
mpi.serverPostAdapterExternalTime = System.currentTimeMillis();
}
}