| /* |
| * 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(); |
| } |
| |
| } |