org.cloudata.core.common.testhelper.FaultManager.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudata.core.common.testhelper.FaultManager.java

Source

/**
 * 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.cloudata.core.common.testhelper;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.cloudata.core.common.conf.CloudataConf;
import org.cloudata.core.common.lock.LockUtil;
import org.cloudata.core.common.testhelper.FaultManager.FaultHelper.InjectionInfo;

public class FaultManager {
    private static final Log LOG = LogFactory.getLog(FaultManager.class);
    private final static String LOCK_PATH = "/fault_injection";

    private HashMap<Class<?>, HashMap<String, Fault>> classFaultMap = new HashMap<Class<?>, HashMap<String, Fault>>();
    ZooKeeper zk;
    CloudataConf conf;

    FaultManager(CloudataConf conf) throws IOException {
        this.conf = conf;
        initLockClient(conf);
    }

    public static FaultManager create(CloudataConf conf) throws IOException {
        FaultManager fm = new FaultManager(conf);
        fm.load();
        return fm;
    }

    public void close() {
    }

    public Fault getFault(Class<?> c, String methodName) throws IOException {
        if (zk == null) {
            throw new IOException("FaultManager is not initialized");
        }

        HashMap<String, Fault> faultMap = null;
        synchronized (classFaultMap) {
            faultMap = findFaultMap(c);
        }

        if (faultMap == null) {
            return null;
        }

        return faultMap.get(methodName);
    }

    public void clearAllFaults() throws IOException, InterruptedException {
        LockUtil.delete(zk, LockUtil.getZKPath(conf, LOCK_PATH), true);

        Condition cond = new Condition() {
            boolean isSatisfied() {
                return classFaultMap.isEmpty();
            }
        };

        waitUntil(cond, 10000);
    }

    public void clear(final Class<?> c, final String methodName) throws IOException, InterruptedException {
        if (zk == null) {
            throw new IOException("FaultManager is not initialized");
        }

        String faultLockPath = FaultHelper.createLockPath(c, methodName);
        try {
            zk.setData(LockUtil.getZKPath(conf, LOCK_PATH + "/" + faultLockPath), "".getBytes(), -1);
        } catch (Exception e) {
            throw new IOException(e);
        }

        Condition cond = new Condition() {
            boolean isSatisfied() throws IOException {
                return getFault(c, methodName) == null;
            }
        };

        waitUntil(cond, 5000);
    }

    /**
     * Pleiades? fault  . 
     * ? ? ? fault  FaultManager?  fault ?? ? 
     * blocking. , ? blocking?   ? fault   
     *  FaultManager   fault ?  ? . 
     * ?,  FaultManager fault   ? sleep? . 
     * @param c
     * @param methodName
     * @param faultClass
     * @throws IOException
     * @throws InterruptedException 
     */
    public void inject(final Class<?> c, final String methodName, final Class<? extends Fault> faultClass)
            throws IOException, InterruptedException {
        if (zk == null) {
            throw new IOException("FaultManager is not initialized");
        }

        String faultLockPath = FaultHelper.createLockPath(c, methodName);

        try {
            zk.setData(LockUtil.getZKPath(conf, LOCK_PATH + "/" + faultLockPath),
                    faultClass.getName().replaceAll("\\.", "_").getBytes(), -1);
        } catch (Exception e) {
            throw new IOException(e);
        }

        Condition cond = new Condition() {
            boolean isSatisfied() throws IOException {
                Fault fault = null;
                return (fault = getFault(c, methodName)) != null && fault.getClass().equals(faultClass);
            }
        };

        waitUntil(cond, 5000);
    }

    private void load() throws IOException {
        List<String> dirs = null;
        try {
            dirs = zk.getChildren(LockUtil.getZKPath(conf, LOCK_PATH), false);
        } catch (NoNodeException e) {
        } catch (Exception e) {
            throw new IOException(e);
        }

        if (dirs == null || dirs.size() == 0) {
            return;
        }

        for (String eachPath : dirs) {
            String fullPath = LOCK_PATH + "/" + eachPath;
            String classAndMethodName = extractClassAndMethodName(fullPath);
            String[] pathElements = classAndMethodName.split("/");

            if (pathElements.length != 2) {
                continue;
            }

            InjectionInfo info;
            try {
                info = FaultHelper.convertAsInfo(fullPath,
                        new String(zk.getData(LockUtil.getZKPath(conf, fullPath), false, null)));
            } catch (Exception e) {
                throw new IOException(e);
            }

            if (info.fault != null) {
                LOG.debug("load fault : " + info);
                internalInject(info.c, info.methodName, info.fault);
            }
        }
    }

    private String extractClassAndMethodName(String fullPath) {
        return fullPath.substring(fullPath.indexOf(LOCK_PATH) + LOCK_PATH.length() + 1);
    }

    private void initLockClient(CloudataConf conf) throws IOException {
        zk = LockUtil.getZooKeeper(conf, "FaultManager", null);
        try {
            if (zk.exists(LockUtil.getZKPath(conf, LOCK_PATH), false) == null) {
                LockUtil.createNodes(zk, LockUtil.getZKPath(conf, LOCK_PATH), null, CreateMode.PERSISTENT);
            }

            List<String> paths = zk.getChildren(LockUtil.getZKPath(conf, LOCK_PATH), new LockWatcher());
            for (String eachPath : paths) {
                zk.exists(LockUtil.getZKPath(conf, LOCK_PATH + "/" + eachPath), new LockWatcher());
            }
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    class LockWatcher implements Watcher {
        @Override
        public void process(WatchedEvent event) {
            if (event.getType() == Event.EventType.NodeChildrenChanged
                    || event.getType() == Event.EventType.NodeDataChanged) {
                String path = event.getPath();
                if (path.equals(LOCK_PATH)) {
                    // clear all
                    synchronized (classFaultMap) {
                        classFaultMap.clear();
                    }
                } else {
                    InjectionInfo info = null;
                    try {
                        info = FaultHelper.convertAsInfo(path, new String(zk.getData(path, false, null)));
                    } catch (NoNodeException e) {
                    } catch (Exception e) {
                        LOG.error(e);
                        return;
                    }

                    if (info.fault == null) {
                        LOG.debug("clear fault event [" + path + "]");
                        internalClear(info.c, info.methodName);
                    } else {
                        LOG.debug("inject fault event [" + path + "], fault class :"
                                + info.fault.getClass().getName());
                        internalInject(info.c, info.methodName, info.fault);
                    }
                }
            }
        }
    }

    //      public LockEventFilter getFilter() {
    //        LockEventFilter filter = new LockEventFilter();
    //        filter.addConstraint(LockEventFilter.Operator.PF, LOCK_PATH);
    //
    //        List<LockService.Events> lockEvents = new ArrayList<LockService.Events>();
    //        lockEvents.add(LockService.Events.NODE_REMOVED);
    //        lockEvents.add(LockService.Events.CONTENTS_CHANGED);
    //        filter.setLockEventList(lockEvents);
    //
    //        return filter;
    //      }
    //      
    //      public void notifyEvent(LockService.Events event, LockEventData eventData) {
    //        try {
    //          String path = eventData.getLockId();
    //          if (path.equals(LOCK_PATH)) {
    //            // clear all
    //            synchronized(classFaultMap) {
    //              classFaultMap.clear();
    //            }
    //          } else {
    //            InjectionInfo info = FaultHelper.convertAsInfo(path, lockService.getContents(path));
    //
    //            if (info.fault == null) {
    //              LOG.debug("clear fault event [" + path + "]");
    //              internalClear(info.c, info.methodName);
    //            } else {
    //              LOG.debug("inject fault event [" + path + "], fault class :"
    //                  + info.fault.getClass().getName());
    //              internalInject(info.c, info.methodName, info.fault);
    //            }
    //          }
    //        } catch (IOException e) {
    //          LOG.warn("Fail to process event", e);
    //        }
    //      }
    //    };
    //  }

    private void internalClear(Class<?> c, String methodName) {
        synchronized (classFaultMap) {
            HashMap<String, Fault> faultMap = findFaultMap(c);

            if (faultMap != null) {
                faultMap.remove(methodName);

                if (faultMap.isEmpty()) {
                    classFaultMap.remove(c);
                }
            }

            classFaultMap.notifyAll();
        }
    }

    private void internalInject(Class<?> c, String methodName, Fault fault) {
        synchronized (classFaultMap) {
            HashMap<String, Fault> faultMap = classFaultMap.get(c);
            if (faultMap == null) {
                faultMap = new HashMap<String, Fault>();
                classFaultMap.put(c, faultMap);
            }
            faultMap.put(methodName, fault);
            classFaultMap.notifyAll();
        }
    }

    private HashMap<String, Fault> findFaultMap(Class<?> c) {
        HashMap<String, Fault> ret = classFaultMap.get(c);

        if (ret == null) {
            Class<?>[] interfaces = c.getInterfaces();
            for (Class<?> ci : interfaces) {
                if ((ret = findFaultMap(ci)) != null) {
                    return ret;
                }
            }

            return null;
        }

        return ret;
    }

    void waitUntil(Condition cond, long waitTime) throws IOException, InterruptedException {
        synchronized (classFaultMap) {
            long begin = System.currentTimeMillis();

            while (!cond.isSatisfied()) {
                classFaultMap.wait(waitTime);
                waitTime -= (System.currentTimeMillis() - begin);
                if (!cond.isSatisfied() && waitTime <= 0) {
                    throw new IOException("fail to operation until timeout");
                }
            }
        }
    }

    static abstract class Condition {
        abstract boolean isSatisfied() throws IOException;
    }

    static class FaultHelper {
        static final String DELIMINATOR = "||";

        static class InjectionInfo {
            Class<?> c;
            String methodName;
            Fault fault;

            public String toString() {
                return "fault info, class : " + c.getName() + ", method : " + methodName + ", fault : "
                        + ((fault == null) ? null : fault.getClass().getName());
            }
        }

        public static InjectionInfo convertAsInfo(String path, String contents) throws IOException {
            InjectionInfo info = new InjectionInfo();
            String[] pathElements = path.split("/");
            String className = null;

            try {
                String unConvertedClassName = pathElements[pathElements.length - 2];
                className = returnToClassName(unConvertedClassName);
                info.c = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new IOException("class name is invalid [" + className + "]");
            }

            info.methodName = pathElements[pathElements.length - 1];

            if (contents.equals("")) {
                info.fault = null;
            } else {
                String faultName = null;
                try {
                    faultName = returnToClassName(contents);
                    info.fault = (Fault) Class.forName(faultName).newInstance();
                } catch (ClassNotFoundException e) {
                    throw new IOException("class name is invalid [" + faultName + "]");
                } catch (Exception e) {
                    throw new IOException("fail to instantiate fault", e);
                }
            }

            return info;
        }

        public static String convertAsContent(Class<?> c, String methodName, Fault fault) {
            return c.getName() + DELIMINATOR + methodName + DELIMINATOR + fault.getClass().getName();
        }

        public static String createLockPath(Class<?> c, String methodName) {
            return c.getName().replaceAll("\\.", "_") + "/" + methodName;
        }

        public static String returnToClassName(String convertedClassName) {
            return convertedClassName.replaceAll("_", "\\.");
        }
    }
}