001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.management; 018 019import javax.management.Descriptor; 020import javax.management.MBeanException; 021import javax.management.MBeanOperationInfo; 022import javax.management.ReflectionException; 023import javax.management.RuntimeOperationsException; 024import javax.management.modelmbean.ModelMBeanInfo; 025import javax.management.modelmbean.RequiredModelMBean; 026 027import org.apache.camel.util.ObjectHelper; 028import org.apache.camel.util.URISupport; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032/** 033 * A {@link RequiredModelMBean} which allows us to intercept invoking operations on the MBean. 034 * <p/> 035 * For example if mask has been enabled on JMX, then we use this implementation 036 * to hide sensitive information from the returned JMX attributes / operations. 037 */ 038public class MaskRequiredModelMBean extends RequiredModelMBean { 039 040 private static final Logger LOG = LoggerFactory.getLogger(MaskRequiredModelMBean.class); 041 private boolean mask; 042 043 public MaskRequiredModelMBean() throws MBeanException, RuntimeOperationsException { 044 // must have default no-arg constructor 045 } 046 047 public MaskRequiredModelMBean(ModelMBeanInfo mbi, boolean mask) throws MBeanException, RuntimeOperationsException { 048 super(mbi); 049 this.mask = mask; 050 } 051 052 public boolean isMask() { 053 return mask; 054 } 055 056 @Override 057 public Object invoke(String opName, Object[] opArgs, String[] sig) throws MBeanException, ReflectionException { 058 Object answer = super.invoke(opName, opArgs, sig); 059 // mask the answer if enabled and it was a String type (we cannot mask other types) 060 if (mask && answer instanceof String && ObjectHelper.isNotEmpty(answer) && isMaskOperation(opName)) { 061 answer = mask(opName, (String) answer); 062 } 063 return answer; 064 } 065 066 protected boolean isMaskOperation(String opName) { 067 for (MBeanOperationInfo info : getMBeanInfo().getOperations()) { 068 if (info.getName().equals(opName)) { 069 Descriptor desc = info.getDescriptor(); 070 if (desc != null) { 071 Object val = desc.getFieldValue("mask"); 072 return val != null && "true".equals(val); 073 } 074 } 075 } 076 return false; 077 } 078 079 /** 080 * Masks the returned value from invoking the operation 081 * 082 * @param opName the operation name invoked 083 * @param value the current value 084 * @return the masked value 085 */ 086 protected String mask(String opName, String value) { 087 // use sanitize uri which will mask sensitive information 088 String answer = URISupport.sanitizeUri(value); 089 if (LOG.isTraceEnabled()) { 090 LOG.trace("Masking JMX operation: {}.{} value: {} -> {}", 091 new Object[]{getMBeanInfo().getClassName(), opName, value, answer}); 092 } 093 return answer; 094 } 095}