/** * 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 org.apache.hadoop.ipc; import java.util.ArrayList; import java.util.Collection; import com.google.common.base.Joiner; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceStability; /** * Used to registry custom methods to refresh at runtime. * Each identifier maps to one or more RefreshHandlers. */ @InterfaceStability.Unstable public class RefreshRegistry { public static final Log LOG = LogFactory.getLog(RefreshRegistry.class); // Used to hold singleton instance private static class RegistryHolder { @SuppressWarnings("All") public static RefreshRegistry registry = new RefreshRegistry(); } // Singleton access public static RefreshRegistry defaultRegistry() { return RegistryHolder.registry; } private final Multimap<String, RefreshHandler> handlerTable; public RefreshRegistry() { handlerTable = HashMultimap.create(); } /** * Registers an object as a handler for a given identity. * Note: will prevent handler from being GC'd, object should unregister itself * when done * @param identifier a unique identifier for this resource, * such as org.apache.hadoop.blacklist * @param handler the object to register */ public synchronized void register(String identifier, RefreshHandler handler) { if (identifier == null) { throw new NullPointerException("Identifier cannot be null"); } handlerTable.put(identifier, handler); } /** * Remove the registered object for a given identity. * @param identifier the resource to unregister * @return the true if removed */ public synchronized boolean unregister(String identifier, RefreshHandler handler) { return handlerTable.remove(identifier, handler); } public synchronized void unregisterAll(String identifier) { handlerTable.removeAll(identifier); } /** * Lookup the responsible handler and return its result. * This should be called by the RPC server when it gets a refresh request. * @param identifier the resource to refresh * @param args the arguments to pass on, not including the program name * @throws IllegalArgumentException on invalid identifier * @return the response from the appropriate handler */ public synchronized Collection<RefreshResponse> dispatch(String identifier, String[] args) { Collection<RefreshHandler> handlers = handlerTable.get(identifier); if (handlers.size() == 0) { String msg = "Identifier '" + identifier + "' does not exist in RefreshRegistry. Valid options are: " + Joiner.on(", ").join(handlerTable.keySet()); throw new IllegalArgumentException(msg); } ArrayList<RefreshResponse> responses = new ArrayList<RefreshResponse>(handlers.size()); // Dispatch to each handler and store response for(RefreshHandler handler : handlers) { RefreshResponse response; // Run the handler try { response = handler.handleRefresh(identifier, args); if (response == null) { throw new NullPointerException("Handler returned null."); } LOG.info(handlerName(handler) + " responds to '" + identifier + "', says: '" + response.getMessage() + "', returns " + response.getReturnCode()); } catch (Exception e) { response = new RefreshResponse(-1, e.getLocalizedMessage()); } response.setSenderName(handlerName(handler)); responses.add(response); } return responses; } private String handlerName(RefreshHandler h) { return h.getClass().getName() + '@' + Integer.toHexString(h.hashCode()); } }