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 */
018package org.apache.hadoop.metrics.spi;
019
020import java.io.IOException;
021import java.lang.reflect.InvocationHandler;
022import java.lang.reflect.Method;
023import java.lang.reflect.Proxy;
024import java.util.ArrayList;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028
029import org.apache.hadoop.classification.InterfaceAudience;
030import org.apache.hadoop.classification.InterfaceStability;
031import org.apache.hadoop.metrics.ContextFactory;
032import org.apache.hadoop.metrics.MetricsContext;
033import org.apache.hadoop.metrics.MetricsException;
034import org.apache.hadoop.metrics.MetricsRecord;
035import org.apache.hadoop.metrics.MetricsUtil;
036import org.apache.hadoop.metrics.Updater;
037
038@InterfaceAudience.Public
039@InterfaceStability.Evolving
040public 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}