/** * Copyright (c) 2009-2011 VMware, Inc. All Rights Reserved. * * Licensed 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 com.springsource.insight.plugin.springtx; import java.sql.Connection; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import com.springsource.insight.intercept.operation.Operation; import com.springsource.insight.intercept.operation.OperationFinalizer; import com.springsource.insight.util.StringUtil; public class TransactionOperationFinalizer implements OperationFinalizer { private static final TransactionOperationFinalizer INSTANCE = new TransactionOperationFinalizer(); static final int MAX_TX_NAME_LEN = 30; static final List<String> propagationNames = Collections.unmodifiableList( Arrays.asList("REQUIRED", "SUPPORTS", "MANDATORY", "REQUIRES_NEW", "NOT_SUPPORTED", "NEVER", "NESTED")); static final String DEFAULT_ISOLATION_LEVEL = "DEFAULT"; static final Map<Integer, String> isolationLevels = Collections.unmodifiableMap(new HashMap<Integer, String>() { private static final long serialVersionUID = 1L; { put(Integer.valueOf(Connection.TRANSACTION_NONE), DEFAULT_ISOLATION_LEVEL); put(Integer.valueOf(Connection.TRANSACTION_READ_COMMITTED), "READ_COMMITTED"); put(Integer.valueOf(Connection.TRANSACTION_READ_UNCOMMITTED), "READ_UNCOMMITTED"); put(Integer.valueOf(Connection.TRANSACTION_REPEATABLE_READ), "REPEATABLE_READ"); put(Integer.valueOf(Connection.TRANSACTION_SERIALIZABLE), "SERIALIZABLE"); } }); public static void register(Operation operation) { operation.addFinalizer(INSTANCE); } // convert values to strings public void finalize(Operation operation, Map<String, Object> richObjects) { String level = normalizeIsolation(operation); if (level != null) { operation.put("isolation", level); } String propagation = normalizePropagation(operation); if (propagation != null) { operation.put("propagation", propagation); } operation.label(buildLabel(operation)); } static String normalizeIsolation(Operation operation) { int isolation = operation.getInt("isolation", (-1)); if (isolation < 0) { return DEFAULT_ISOLATION_LEVEL; // debug breakpoint } String level = isolationLevels.get(Integer.valueOf(isolation)); if (StringUtil.isEmpty(level)) { return DEFAULT_ISOLATION_LEVEL; } else { return level; } } static String normalizePropagation(Operation operation) { int propagation = operation.getInt("propagation", (-1)); if ((propagation >= 0) && (propagation < propagationNames.size())) { return propagationNames.get(propagation); } else { return null; } } static String buildLabel(Operation operation) { StringBuilder label = new StringBuilder("Transaction"); String name = operation.get("name", String.class); if (name != null) { label.append(": ") .append(truncateTxName(name, MAX_TX_NAME_LEN)); } Boolean readOnly = operation.get("readOnly", Boolean.class, Boolean.FALSE); if (readOnly.booleanValue()) { label.append(" (Read-only)"); } else if (TransactionOperationStatus.RolledBack.toString().equals(operation.get("status", String.class))) { label.append(" (Rolled Back)"); } return label.toString(); } static String truncateTxName(String txName, int maxChars) { int packageIdx = StringUtil.indexOfNthCharFromTail(txName, '.', 2); if (packageIdx == -1) { // Not a Spring formatted transaction name. This can apparently happen with WebLogic return StringUtil.chopTailAndEllipsify(txName, maxChars); } String chopped = txName.substring(packageIdx + 1); return StringUtil.chopTailAndEllipsify(chopped, maxChars); } }