001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.metrics.spi;
019    
020    import java.io.IOException;
021    import java.lang.reflect.InvocationHandler;
022    import java.lang.reflect.Method;
023    import java.lang.reflect.Proxy;
024    import java.util.ArrayList;
025    
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    
029    import org.apache.hadoop.classification.InterfaceAudience;
030    import org.apache.hadoop.classification.InterfaceStability;
031    import org.apache.hadoop.metrics.ContextFactory;
032    import org.apache.hadoop.metrics.MetricsContext;
033    import org.apache.hadoop.metrics.MetricsException;
034    import org.apache.hadoop.metrics.MetricsRecord;
035    import org.apache.hadoop.metrics.MetricsUtil;
036    import org.apache.hadoop.metrics.Updater;
037    
038    @InterfaceAudience.Public
039    @InterfaceStability.Evolving
040    public class CompositeContext extends AbstractMetricsContext {
041    
042      private static final Log LOG = LogFactory.getLog(CompositeContext.class);
043      private static final String ARITY_LABEL = "arity";
044      private static final String SUB_FMT = "%s.sub%d";
045      private final ArrayList<MetricsContext> subctxt =
046        new ArrayList<MetricsContext>();
047    
048      @InterfaceAudience.Private
049      public CompositeContext() {
050      }
051    
052      @InterfaceAudience.Private
053      public void init(String contextName, ContextFactory factory) {
054        super.init(contextName, factory);
055        int nKids;
056        try {
057          String sKids = getAttribute(ARITY_LABEL);
058          nKids = Integer.valueOf(sKids);
059        } catch (Exception e) {
060          LOG.error("Unable to initialize composite metric " + contextName +
061                    ": could not init arity", e);
062          return;
063        }
064        for (int i = 0; i < nKids; ++i) {
065          MetricsContext ctxt = MetricsUtil.getContext(
066              String.format(SUB_FMT, contextName, i), contextName);
067          if (null != ctxt) {
068            subctxt.add(ctxt);
069          }
070        }
071      }
072    
073      @InterfaceAudience.Private
074      @Override
075      public MetricsRecord newRecord(String recordName) {
076        return (MetricsRecord) Proxy.newProxyInstance(
077            MetricsRecord.class.getClassLoader(),
078            new Class[] { MetricsRecord.class },
079            new MetricsRecordDelegator(recordName, subctxt));
080      }
081    
082      @InterfaceAudience.Private
083      @Override
084      protected void emitRecord(String contextName, String recordName,
085          OutputRecord outRec) throws IOException {
086        for (MetricsContext ctxt : subctxt) {
087          try {
088            ((AbstractMetricsContext)ctxt).emitRecord(
089              contextName, recordName, outRec);
090            if (contextName == null || recordName == null || outRec == null) {
091              throw new IOException(contextName + ":" + recordName + ":" + outRec);
092            }
093          } catch (IOException e) {
094            LOG.warn("emitRecord failed: " + ctxt.getContextName(), e);
095          }
096        }
097      }
098    
099      @InterfaceAudience.Private
100      @Override
101      protected void flush() throws IOException {
102        for (MetricsContext ctxt : subctxt) {
103          try {
104            ((AbstractMetricsContext)ctxt).flush();
105          } catch (IOException e) {
106            LOG.warn("flush failed: " + ctxt.getContextName(), e);
107          }
108        }
109      }
110    
111      @InterfaceAudience.Private
112      @Override
113      public void startMonitoring() throws IOException {
114        for (MetricsContext ctxt : subctxt) {
115          try {
116            ctxt.startMonitoring();
117          } catch (IOException e) {
118            LOG.warn("startMonitoring failed: " + ctxt.getContextName(), e);
119          }
120        }
121      }
122    
123      @InterfaceAudience.Private
124      @Override
125      public void stopMonitoring() {
126        for (MetricsContext ctxt : subctxt) {
127          ctxt.stopMonitoring();
128        }
129      }
130    
131      /**
132       * Return true if all subcontexts are monitoring.
133       */
134      @InterfaceAudience.Private
135      @Override
136      public boolean isMonitoring() {
137        boolean ret = true;
138        for (MetricsContext ctxt : subctxt) {
139          ret &= ctxt.isMonitoring();
140        }
141        return ret;
142      }
143    
144      @InterfaceAudience.Private
145      @Override
146      public void close() {
147        for (MetricsContext ctxt : subctxt) {
148          ctxt.close();
149        }
150      }
151    
152      @InterfaceAudience.Private
153      @Override
154      public void registerUpdater(Updater updater) {
155        for (MetricsContext ctxt : subctxt) {
156          ctxt.registerUpdater(updater);
157        }
158      }
159    
160      @InterfaceAudience.Private
161      @Override
162      public void unregisterUpdater(Updater updater) {
163        for (MetricsContext ctxt : subctxt) {
164          ctxt.unregisterUpdater(updater);
165        }
166      }
167    
168      private static class MetricsRecordDelegator implements InvocationHandler {
169        private static final Method m_getRecordName = initMethod();
170        private static Method initMethod() {
171          try {
172            return MetricsRecord.class.getMethod("getRecordName", new Class[0]);
173          } catch (Exception e) {
174            throw new RuntimeException("Internal error", e);
175          }
176        }
177    
178        private final String recordName;
179        private final ArrayList<MetricsRecord> subrecs;
180    
181        MetricsRecordDelegator(String recordName, ArrayList<MetricsContext> ctxts) {
182          this.recordName = recordName;
183          this.subrecs = new ArrayList<MetricsRecord>(ctxts.size());
184          for (MetricsContext ctxt : ctxts) {
185            subrecs.add(ctxt.createRecord(recordName));
186          }
187        }
188    
189        public Object invoke(Object p, Method m, Object[] args) throws Throwable {
190          if (m_getRecordName.equals(m)) {
191            return recordName;
192          }
193          assert Void.TYPE.equals(m.getReturnType());
194          for (MetricsRecord rec : subrecs) {
195            m.invoke(rec, args);
196          }
197          return null;
198        }
199      }
200    
201    }