Package org.apache.zookeeper_voltpatches.server

Source Code of org.apache.zookeeper_voltpatches.server.ZooKeeperServer

/**
* 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.zookeeper_voltpatches.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.Semaphore;

import javax.management.InstanceAlreadyExistsException;

import org.apache.jute_voltpatches.BinaryInputArchive;
import org.apache.jute_voltpatches.Record;
import org.apache.zookeeper_voltpatches.CreateMode;
import org.apache.zookeeper_voltpatches.KeeperException;
import org.apache.zookeeper_voltpatches.KeeperException.Code;
import org.apache.zookeeper_voltpatches.KeeperException.SessionMovedException;
import org.apache.zookeeper_voltpatches.ZooDefs;
import org.apache.zookeeper_voltpatches.ZooDefs.OpCode;
import org.apache.zookeeper_voltpatches.common.PathUtils;
import org.apache.zookeeper_voltpatches.data.ACL;
import org.apache.zookeeper_voltpatches.data.Id;
import org.apache.zookeeper_voltpatches.data.Stat;
import org.apache.zookeeper_voltpatches.data.StatPersisted;
import org.apache.zookeeper_voltpatches.jmx.MBeanRegistry;
import org.apache.zookeeper_voltpatches.proto.CreateRequest;
import org.apache.zookeeper_voltpatches.proto.CreateResponse;
import org.apache.zookeeper_voltpatches.proto.DeleteRequest;
import org.apache.zookeeper_voltpatches.proto.ExistsRequest;
import org.apache.zookeeper_voltpatches.proto.ExistsResponse;
import org.apache.zookeeper_voltpatches.proto.GetACLRequest;
import org.apache.zookeeper_voltpatches.proto.GetACLResponse;
import org.apache.zookeeper_voltpatches.proto.GetChildren2Request;
import org.apache.zookeeper_voltpatches.proto.GetChildren2Response;
import org.apache.zookeeper_voltpatches.proto.GetChildrenRequest;
import org.apache.zookeeper_voltpatches.proto.GetChildrenResponse;
import org.apache.zookeeper_voltpatches.proto.GetDataRequest;
import org.apache.zookeeper_voltpatches.proto.GetDataResponse;
import org.apache.zookeeper_voltpatches.proto.ReplyHeader;
import org.apache.zookeeper_voltpatches.proto.RequestHeader;
import org.apache.zookeeper_voltpatches.proto.SetACLRequest;
import org.apache.zookeeper_voltpatches.proto.SetACLResponse;
import org.apache.zookeeper_voltpatches.proto.SetDataRequest;
import org.apache.zookeeper_voltpatches.proto.SetDataResponse;
import org.apache.zookeeper_voltpatches.proto.SetWatches;
import org.apache.zookeeper_voltpatches.proto.SyncRequest;
import org.apache.zookeeper_voltpatches.proto.SyncResponse;
import org.apache.zookeeper_voltpatches.server.DataTree.ProcessTxnResult;
import org.apache.zookeeper_voltpatches.server.NIOServerCnxn.CnxnStats;
import org.apache.zookeeper_voltpatches.server.NIOServerCnxn.Factory;
import org.apache.zookeeper_voltpatches.server.SessionTracker.SessionExpirer;
import org.apache.zookeeper_voltpatches.server.auth.AuthenticationProvider;
import org.apache.zookeeper_voltpatches.server.auth.ProviderRegistry;
import org.apache.zookeeper_voltpatches.txn.CreateSessionTxn;
import org.apache.zookeeper_voltpatches.txn.CreateTxn;
import org.apache.zookeeper_voltpatches.txn.DeleteTxn;
import org.apache.zookeeper_voltpatches.txn.ErrorTxn;
import org.apache.zookeeper_voltpatches.txn.SetACLTxn;
import org.apache.zookeeper_voltpatches.txn.SetDataTxn;
import org.apache.zookeeper_voltpatches.txn.TxnHeader;
import org.voltcore.logging.VoltLogger;

/**
* This class implements a simple standalone ZooKeeperServer. It sets up the
* following chain of RequestProcessors to process requests:
* PrepRequestProcessor -> SyncRequestProcessor -> FinalRequestProcessor
*/
public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider,
        Runnable {
    protected static final VoltLogger LOG;

    static boolean skipACL;
    static {
        LOG = new VoltLogger("ZK-SERVER");
        //Environment.logEnv("Server environment:", LOG);
        skipACL = System.getProperty("zookeeper.skipACL", "no").equals("yes");
        if (skipACL) {
            LOG.info("zookeeper.skipACL==\"yes\", ACL checks will be skipped");
        }
    }

    protected ZooKeeperServerBean jmxServerBean;
    protected DataTreeBean jmxDataTreeBean;

    /**
     * The server delegates loading of the tree to an instance of the interface
     */
    public interface DataTreeBuilder {
        public DataTree build();
    }

    static public class BasicDataTreeBuilder implements DataTreeBuilder {
        @Override
        public DataTree build() {
            return new DataTree();
        }
    }

    public interface Callout {
        public void run();

        public void request(Request r);

        public Semaphore createSession(final ServerCnxn cnxn,
                final byte[] passwd, final int timeout);
    }

    public static final int DEFAULT_TICK_TIME = 3000;
    protected int tickTime = DEFAULT_TICK_TIME;
    /** value of -1 indicates unset, use default */
    protected int minSessionTimeout = -1;
    /** value of -1 indicates unset, use default */
    protected int maxSessionTimeout = -1;
    protected SessionTracker sessionTracker;
    private ZKDatabase zkDb;
    public final static Exception ok = new Exception("No prob");
    protected volatile boolean running;
    private final Callout m_callout;

    /**
     * This is the secret that we use to generate passwords, for the moment it
     * is more of a sanity check.
     */
    public static final long superSecret = 0XB3415C00L;

    int requestsInProcess;
    final List<ChangeRecord> outstandingChanges = new ArrayList<ChangeRecord>();
    // this data structure must be accessed under the outstandingChanges lock
    final HashMap<String, ChangeRecord> outstandingChangesForPath = new HashMap<String, ChangeRecord>();

    private NIOServerCnxn.Factory serverCnxnFactory;

    private final ServerStats serverStats;

    void removeCnxn(ServerCnxn cnxn) {
        this.closeSession(cnxn.getSessionId());
    }

    /**
     * Creates a ZooKeeperServer instance. It sets everything up, but doesn't
     * actually start listening for clients until run() is invoked.
     *
     * @param dataDir
     *            the directory to put the data
     * @throws IOException
     */
    public ZooKeeperServer(Callout callout, int tickTime,
            int minSessionTimeout, int maxSessionTimeout) throws IOException {
        serverStats = new ServerStats(this);
        this.zkDb = new ZKDatabase();
        this.tickTime = tickTime;
        this.minSessionTimeout = minSessionTimeout;
        this.maxSessionTimeout = maxSessionTimeout;
        this.m_callout = callout;
        LOG.info("Created server with tickTime " + tickTime
                + " minSessionTimeout " + getMinSessionTimeout()
                + " maxSessionTimeout " + getMaxSessionTimeout());
    }

    public ServerStats serverStats() {
        return serverStats;
    }

    public void dumpConf(PrintWriter pwriter) {
        pwriter.print("clientPort=");
        pwriter.println(getClientPort());
        pwriter.print("tickTime=");
        pwriter.println(getTickTime());
        pwriter.print("maxClientCnxns=");
        pwriter.println(serverCnxnFactory.getMaxClientCnxns());
        pwriter.print("minSessionTimeout=");
        pwriter.println(getMinSessionTimeout());
        pwriter.print("maxSessionTimeout=");
        pwriter.println(getMaxSessionTimeout());
    }

    /**
     * Default constructor, relies on the config for its agrument values
     *
     * @throws IOException
     */
    public ZooKeeperServer(Callout callout) throws IOException {
        this(callout, DEFAULT_TICK_TIME, -1, -1);
        // this(callout, 999999, 999999, 999999);
    }

    /**
     * get the zookeeper database for this server
     *
     * @return the zookeeper database for this server
     */
    public ZKDatabase getZKDatabase() {
        return this.zkDb;
    }

    /**
     * set the zkdatabase for this zookeeper server
     *
     * @param zkDb
     */
    public void setZKDatabase(ZKDatabase zkDb) {
        this.zkDb = zkDb;
    }

    long getTime() {
        return System.currentTimeMillis();
    }

    private void close(long sessionId) {
        submitRequest(null, sessionId, OpCode.closeSession, 0, null, null);
    }

    public void closeSession(long sessionId) {
        LOG.debug("Closing session 0x" + Long.toHexString(sessionId));

        // we do not want to wait for a session close. send it as soon as we
        // detect it!
        close(sessionId);
    }

    protected void killSession(long sessionId, long zxid) {
        zkDb.killSession(sessionId, zxid);
        if (LOG.isTraceEnabled()) {
            ZooTrace.logTraceMessage(
                    LOG,
                    ZooTrace.SESSION_TRACE_MASK,
                    "ZooKeeperServer --- killSession: 0x"
                            + Long.toHexString(sessionId));
        }
        if (sessionTracker != null) {
            sessionTracker.removeSession(sessionId);
        }
    }

    @Override
    public void expire(long sessionId) {
        LOG.info("Initiating close of session 0x" + Long.toHexString(sessionId));
        close(sessionId);
    }

    public static class MissingSessionException extends IOException {
        private static final long serialVersionUID = 7467414635467261007L;

        public MissingSessionException(String msg) {
            super(msg);
        }
    }

    protected void registerJMX() {
        // register with JMX
        try {
            jmxServerBean = new ZooKeeperServerBean(this);
            MBeanRegistry.getInstance().register(jmxServerBean, null);

            try {
                jmxDataTreeBean = new DataTreeBean(zkDb.getDataTree());
                MBeanRegistry.getInstance().register(jmxDataTreeBean, jmxServerBean);
            }
            catch (Exception e) {
                LOG.error("Failed to register with JMX.", e);
                jmxDataTreeBean = null;
            }
        }
        catch (InstanceAlreadyExistsException e) {
            LOG.error("Failed to register ZooKeeper with JMX due to a conflict. " +
                "You are likely running two VoltDB instances on one system. " +
                "This will only affect JMX management.");
            jmxServerBean = null;
        }
        catch (Exception e) {
            LOG.error("Failed to register with JMX.", e);
            jmxServerBean = null;
        }
    }

    public Thread startup() {
        createSessionTracker();
        Thread thisThread = new Thread(this, "ZooKeeperServer");
        thisThread.start();

        registerJMX();

        synchronized (this) {
            running = true;
            notifyAll();
        }
        return thisThread;
    }

    public void createSessionTracker() {
        sessionTracker = new SessionTrackerImpl(this,
                zkDb.getSessionWithTimeOuts());
    }

    public void closeSessions(long owner) {
        sessionTracker.expireSessionsWithOwner(owner);
    }

    public boolean isRunning() {
        return running;
    }

    public void shutdown() {
        unregisterJMX();
    }

    protected void unregisterJMX() {
        // unregister from JMX
        try {
            if (jmxDataTreeBean != null) {
                MBeanRegistry.getInstance().unregister(jmxDataTreeBean);
            }
        } catch (Exception e) {
            LOG.warn("Failed to unregister with JMX", e);
        }
        try {
            if (jmxServerBean != null) {
                MBeanRegistry.getInstance().unregister(jmxServerBean);
            }
        } catch (Exception e) {
            LOG.warn("Failed to unregister with JMX", e);
        }
        jmxServerBean = null;
        jmxDataTreeBean = null;
    }

    synchronized public void incInProcess() {
        requestsInProcess++;
    }

    synchronized public void decInProcess() {
        requestsInProcess--;
    }

    public int getInProcess() {
        return requestsInProcess;
    }

    /**
     * This structure is used to facilitate information sharing between PrepRP
     * and FinalRP.
     */
    static class ChangeRecord {
        ChangeRecord(long zxid, String path, StatPersisted stat,
                int childCount, List<ACL> acl) {
            this.zxid = zxid;
            this.path = path;
            this.stat = stat;
            this.childCount = childCount;
            this.acl = acl;
        }

        long zxid;

        String path;

        StatPersisted stat; /* Make sure to create a new object when changing */

        int childCount;

        List<ACL> acl; /* Make sure to create a new object when changing */

        @SuppressWarnings("unchecked")
        ChangeRecord duplicate(long zxid) {
            StatPersisted stat = new StatPersisted();
            if (this.stat != null) {
                DataTree.copyStatPersisted(this.stat, stat);
            }
            return new ChangeRecord(zxid, path, stat, childCount,
                    acl == null ? new ArrayList<ACL>() : new ArrayList(acl));
        }
    }

    byte[] generatePasswd(long id) {
        Random r = new Random(id ^ superSecret);
        byte p[] = new byte[16];
        r.nextBytes(p);
        return p;
    }

    protected boolean checkPasswd(long sessionId, byte[] passwd) {
        return sessionId != 0
                && Arrays.equals(passwd, generatePasswd(sessionId));
    }

    void createSession(ServerCnxn cnxn, byte passwd[], int timeout)
            throws InterruptedException {
        Semaphore sessionIdCreated = m_callout.createSession(cnxn, passwd,
                timeout);
        sessionIdCreated.acquire();
    }

    public void closeSession(ServerCnxn cnxn, RequestHeader requestHeader) {
        closeSession(cnxn.getSessionId());
    }

    /**
     * @param cnxn
     * @param sessionId
     * @param xid
     * @param bb
     */
    public void submitRequest(ServerCnxn cnxn, long sessionId, int type,
            int xid, ByteBuffer bb, List<Id> authInfo) {
        Request si = new Request(cnxn, sessionId, xid, type, bb, authInfo);
        submitRequest(si);
    }

    public void submitRequest(Request si) {
        boolean validpacket = Request.isValid(si.type);
        if (validpacket) {
            m_callout.request(si);
            if (si.cnxn != null) {
                incInProcess();
            }
        } else {
            LOG.warn("Dropping packet at server of type " + si.type);
            // if invalid packet drop the packet.
        }
    }

    static public void byteBuffer2Record(ByteBuffer bb, Record record)
            throws IOException {
        BinaryInputArchive ia;
        ia = BinaryInputArchive.getArchive(new ByteBufferInputStream(bb));
        record.deserialize(ia, "request");
    }

    public static int getSnapCount() {
        String sc = System.getProperty("zookeeper.snapCount");
        try {
            return Integer.parseInt(sc);
        } catch (Exception e) {
            return 100000;
        }
    }

    public int getGlobalOutstandingLimit() {
        String sc = System.getProperty("zookeeper.globalOutstandingLimit");
        int limit;
        try {
            limit = Integer.parseInt(sc);
        } catch (Exception e) {
            limit = 1000;
        }
        return limit;
    }

    public void setServerCnxnFactory(NIOServerCnxn.Factory factory) {
        serverCnxnFactory = factory;
    }

    public NIOServerCnxn.Factory getServerCnxnFactory() {
        return serverCnxnFactory;
    }

    /**
     * return the last proceesed id from the datatree
     */
    @Override
    public long getLastProcessedZxid() {
        return zkDb.getDataTreeLastProcessedZxid();
    }

    /**
     * return the outstanding requests in the queue, which havent been processed
     * yet
     */
    @Override
    public long getOutstandingRequests() {
        return getInProcess();
    }

    public int getTickTime() {
        return tickTime;
    }

    public void setTickTime(int tickTime) {
        LOG.info("tickTime set to " + tickTime);
        this.tickTime = tickTime;
    }

    public int getMinSessionTimeout() {
        return minSessionTimeout == -1 ? tickTime * 2 : minSessionTimeout;
    }

    public void setMinSessionTimeout(int min) {
        LOG.info("minSessionTimeout set to " + min);
        this.minSessionTimeout = min;
    }

    public int getMaxSessionTimeout() {
        return maxSessionTimeout == -1 ? tickTime * 20 : maxSessionTimeout;
    }

    public void setMaxSessionTimeout(int max) {
        LOG.info("maxSessionTimeout set to " + max);
        this.maxSessionTimeout = max;
    }

    public int getClientPort() {
        return serverCnxnFactory != null ? serverCnxnFactory.ss.socket()
                .getLocalPort() : -1;
    }

    @Override
    public String getState() {
        return "standalone";
    }

    public void dumpEphemerals(PrintWriter pwriter) {
        zkDb.dumpEphemerals(pwriter);
    }

    void addChangeRecord(ChangeRecord c) {
        synchronized (outstandingChanges) {
            outstandingChanges.add(c);
            outstandingChangesForPath.put(c.path, c);
        }
    }

    ChangeRecord getRecordForPath(String path)
            throws KeeperException.NoNodeException {
        ChangeRecord lastChange = null;
        synchronized (outstandingChanges) {
            lastChange = outstandingChangesForPath.get(path);
            /*
             * for (int i = 0; i < zks.outstandingChanges.size(); i++) {
             * ChangeRecord c = zks.outstandingChanges.get(i); if
             * (c.path.equals(path)) { lastChange = c; } }
             */
            if (lastChange == null) {
                DataNode n = getZKDatabase().getNode(path);
                if (n != null) {
                    Long acl;
                    Set<String> children;
                    synchronized (n) {
                        acl = n.acl;
                        children = n.getChildren();
                    }
                    lastChange = new ChangeRecord(-1, path, n.stat,
                            children != null ? children.size() : 0,
                            getZKDatabase().convertLong(acl));
                }
            }
        }
        if (lastChange == null || lastChange.stat == null) {
            throw new KeeperException.NoNodeException(path);
        }
        return lastChange;
    }

    static void checkACL(List<ACL> acl, int perm, List<Id> ids)
            throws KeeperException.NoAuthException {
        if (skipACL) {
            return;
        }
        if (acl == null || acl.size() == 0) {
            return;
        }
        for (Id authId : ids) {
            if (authId.getScheme().equals("super")) {
                return;
            }
        }
        for (ACL a : acl) {
            Id id = a.getId();
            if ((a.getPerms() & perm) != 0) {
                if (id.getScheme().equals("world")
                        && id.getId().equals("anyone")) {
                    return;
                }
                AuthenticationProvider ap = ProviderRegistry.getProvider(id
                        .getScheme());
                if (ap != null) {
                    for (Id authId : ids) {
                        if (authId.getScheme().equals(id.getScheme())
                                && ap.matches(authId.getId(), id.getId())) {
                            return;
                        }
                    }
                }
            }
        }
        throw new KeeperException.NoAuthException();
    }

    /**
     * This method will be called inside the ProcessRequestThread, which is a
     * singleton, so there will be a single thread calling this code.
     *
     * @param request
     */
    public void prepRequest(Request request, long txnId) {
        // LOG.info("Prep>>> cxid = " + request.cxid + " type = " +
        // request.type + " id = 0x" + Long.toHexString(request.sessionId));
        TxnHeader txnHeader = null;
        Record txn = null;
        try {
            switch (request.type) {
            case OpCode.create:
                txnHeader = new TxnHeader(request.sessionId, request.cxid,
                        txnId, getTime(), OpCode.create);
                CreateRequest createRequest = new CreateRequest();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        createRequest);
                String path = createRequest.getPath();
                int lastSlash = path.lastIndexOf('/');
                if (lastSlash == -1 || path.indexOf('\0') != -1) {
                    LOG.info("Invalid path " + path + " with session 0x"
                            + Long.toHexString(request.sessionId));
                    throw new KeeperException.BadArgumentsException(path);
                }
                if (!fixupACL(request.authInfo, createRequest.getAcl())) {
                    throw new KeeperException.InvalidACLException(path);
                }
                String parentPath = path.substring(0, lastSlash);
                ChangeRecord parentRecord = getRecordForPath(parentPath);

                checkACL(parentRecord.acl, ZooDefs.Perms.CREATE,
                        request.authInfo);
                int parentCVersion = parentRecord.stat.getCversion();
                CreateMode createMode = CreateMode.fromFlag(createRequest
                        .getFlags());
                if (createMode.isSequential()) {
                    path = path + String.format("%010d", parentCVersion);
                }
                try {
                    PathUtils.validatePath(path);
                } catch (IllegalArgumentException ie) {
                    LOG.info("Invalid path " + path + " with session 0x"
                            + Long.toHexString(request.sessionId));
                    throw new KeeperException.BadArgumentsException(path);
                }
                try {
                    if (getRecordForPath(path) != null) {
                        throw new KeeperException.NodeExistsException(path);
                    }
                } catch (KeeperException.NoNodeException e) {
                    // ignore this one
                }
                boolean ephemeralParent = parentRecord.stat.getEphemeralOwner() != 0;
                if (ephemeralParent) {
                    throw new KeeperException.NoChildrenForEphemeralsException(
                            path);
                }
                txn = new CreateTxn(path, createRequest.getData(),
                        createRequest.getAcl(), createMode.isEphemeral());
                StatPersisted s = new StatPersisted();
                if (createMode.isEphemeral()) {
                    s.setEphemeralOwner(request.sessionId);
                }
                parentRecord = parentRecord.duplicate(txnHeader.getZxid());
                parentRecord.childCount++;
                parentRecord.stat
                        .setCversion(parentRecord.stat.getCversion() + 1);
                addChangeRecord(parentRecord);
                addChangeRecord(new ChangeRecord(txnHeader.getZxid(), path, s,
                        0, createRequest.getAcl()));

                break;
            case OpCode.delete:
                txnHeader = new TxnHeader(request.sessionId, request.cxid,
                        txnId, getTime(), OpCode.delete);
                DeleteRequest deleteRequest = new DeleteRequest();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        deleteRequest);
                path = deleteRequest.getPath();
                lastSlash = path.lastIndexOf('/');
                if (lastSlash == -1 || path.indexOf('\0') != -1
                        || getZKDatabase().isSpecialPath(path)) {
                    throw new KeeperException.BadArgumentsException(path);
                }
                parentPath = path.substring(0, lastSlash);
                parentRecord = getRecordForPath(parentPath);
                ChangeRecord nodeRecord = getRecordForPath(path);
                checkACL(parentRecord.acl, ZooDefs.Perms.DELETE,
                        request.authInfo);
                int version = deleteRequest.getVersion();
                if (version != -1 && nodeRecord.stat.getVersion() != version) {
                    throw new KeeperException.BadVersionException(path);
                }
                if (nodeRecord.childCount > 0) {
                    throw new KeeperException.NotEmptyException(path);
                }
                txn = new DeleteTxn(path);
                parentRecord = parentRecord.duplicate(txnHeader.getZxid());
                parentRecord.childCount--;
                parentRecord.stat
                        .setCversion(parentRecord.stat.getCversion() + 1);
                addChangeRecord(parentRecord);
                addChangeRecord(new ChangeRecord(txnHeader.getZxid(), path,
                        null, -1, null));
                break;
            case OpCode.setData:
                txnHeader = new TxnHeader(request.sessionId, request.cxid,
                        txnId, getTime(), OpCode.setData);
                SetDataRequest setDataRequest = new SetDataRequest();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        setDataRequest);
                path = setDataRequest.getPath();
                nodeRecord = getRecordForPath(path);
                checkACL(nodeRecord.acl, ZooDefs.Perms.WRITE, request.authInfo);
                version = setDataRequest.getVersion();
                int currentVersion = nodeRecord.stat.getVersion();
                if (version != -1 && version != currentVersion) {
                    throw new KeeperException.BadVersionException(path);
                }
                version = currentVersion + 1;
                txn = new SetDataTxn(path, setDataRequest.getData(), version);
                nodeRecord = nodeRecord.duplicate(txnHeader.getZxid());
                nodeRecord.stat.setVersion(version);
                addChangeRecord(nodeRecord);
                break;
            case OpCode.setACL:
                txnHeader = new TxnHeader(request.sessionId, request.cxid,
                        txnId, getTime(), OpCode.setACL);
                SetACLRequest setAclRequest = new SetACLRequest();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        setAclRequest);
                path = setAclRequest.getPath();
                if (!fixupACL(request.authInfo, setAclRequest.getAcl())) {
                    throw new KeeperException.InvalidACLException(path);
                }
                nodeRecord = getRecordForPath(path);
                checkACL(nodeRecord.acl, ZooDefs.Perms.ADMIN, request.authInfo);
                version = setAclRequest.getVersion();
                currentVersion = nodeRecord.stat.getAversion();
                if (version != -1 && version != currentVersion) {
                    throw new KeeperException.BadVersionException(path);
                }
                version = currentVersion + 1;
                txn = new SetACLTxn(path, setAclRequest.getAcl(), version);
                nodeRecord = nodeRecord.duplicate(txnHeader.getZxid());
                nodeRecord.stat.setAversion(version);
                addChangeRecord(nodeRecord);
                break;
            case OpCode.createSession:
                txnHeader = new TxnHeader(request.sessionId, request.cxid,
                        txnId, getTime(), OpCode.createSession);
                request.request.rewind();
                int to = request.request.getInt();
                txn = new CreateSessionTxn((Long)request.getOwner());
                request.request.rewind();
                sessionTracker.addSession(request.sessionId,
                        (Long) request.getOwner());
                break;
            case OpCode.closeSession:
                txnHeader = new TxnHeader(request.sessionId, request.cxid,
                        txnId, getTime(), OpCode.closeSession);
                // We don't want to do this check since the session expiration
                // thread
                // queues up this operation without being the session owner.
                // this request is the last of the session so it should be ok
                // zks.sessionTracker.checkSession(request.sessionId,
                // request.getOwner());
                HashSet<String> es = getZKDatabase().getEphemerals(
                        request.sessionId);
                synchronized (outstandingChanges) {
                    for (ChangeRecord c : outstandingChanges) {
                        if (c.stat == null) {
                            // Doing a delete
                            es.remove(c.path);
                        } else if (c.stat.getEphemeralOwner() == request.sessionId) {
                            es.add(c.path);
                        }
                    }
                    for (String path2Delete : es) {
                        addChangeRecord(new ChangeRecord(txnHeader.getZxid(),
                                path2Delete, null, 0, null));
                    }
                }
                sessionTracker.removeSession(request.sessionId);
                LOG.info("Processed session termination for sessionid: 0x"
                        + Long.toHexString(request.sessionId));
                break;
            case OpCode.sync:
            case OpCode.exists:
            case OpCode.getData:
            case OpCode.getACL:
            case OpCode.getChildren:
            case OpCode.getChildren2:
            case OpCode.ping:
            case OpCode.setWatches:
            default:
            }
        } catch (KeeperException e) {
            if (txnHeader != null) {
                txnHeader.setType(OpCode.error);
                txn = new ErrorTxn(e.code().intValue());
            }
            LOG.debug("Got user-level KeeperException when processing "
                    + request.toString() + " Error Path:" + e.getPath()
                    + " Error:" + e.getMessage());
            request.setException(e);
        } catch (Exception e) {
            // log at error level as we are returning a marshalling
            // error to the user
            LOG.error("Failed to process " + request, e);

            StringBuilder sb = new StringBuilder();
            ByteBuffer bb = request.request;
            if (bb != null) {
                bb.rewind();
                while (bb.hasRemaining()) {
                    sb.append(Integer.toHexString(bb.get() & 0xff));
                }
            } else {
                sb.append("request buffer is null");
            }

            LOG.error("Dumping request buffer: 0x" + sb.toString());
            if (txnHeader != null) {
                txnHeader.setType(OpCode.error);
                txn = new ErrorTxn(Code.MARSHALLINGERROR.intValue());
            }
        }
        request.hdr = txnHeader;
        request.txn = txn;
        request.zxid = txnId;
        executeRequest(request);
    }

    private void executeRequest(Request request) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Processing request:: " + request);
        }
        // request.addRQRec(">final");
        long traceMask = ZooTrace.CLIENT_REQUEST_TRACE_MASK;
        if (request.type == OpCode.ping) {
            traceMask = ZooTrace.SERVER_PING_TRACE_MASK;
        }
        if (LOG.isTraceEnabled()) {
            ZooTrace.logRequest(LOG, traceMask, 'E', request, "");
        }
        ProcessTxnResult rc = null;
        synchronized (outstandingChanges) {
            while (!outstandingChanges.isEmpty()
                    && outstandingChanges.get(0).zxid <= request.zxid) {
                ChangeRecord cr = outstandingChanges.remove(0);
                if (cr.zxid < request.zxid) {
                    LOG.warn("Zxid outstanding " + cr.zxid
                            + " is less than current " + request.zxid);
                }
                if (outstandingChangesForPath.get(cr.path) == cr) {
                    outstandingChangesForPath.remove(cr.path);
                }
            }
            if (request.hdr != null) {
                rc = getZKDatabase().processTxn(request.hdr, request.txn);
            }
        }

        if (request.hdr != null && request.hdr.getType() == OpCode.closeSession) {
            Factory scxn = getServerCnxnFactory();
            // this might be possible since
            // we might just be playing diffs from the leader
            if (scxn != null && request.cnxn == null) {
                // calling this if we have the cnxn results in the client's
                // close session response being lost - we've already closed
                // the session/socket here before we can send the closeSession
                // in the switch block below
                scxn.closeSession(request.sessionId);
                return;
            }
        }

        if (request.cnxn == null) {
            return;
        }
        ServerCnxn cnxn = request.cnxn;

        String lastOp = "NA";
        decInProcess();
        Code err = Code.OK;
        Record rsp = null;
        boolean closeSession = false;
        try {
            if (request.hdr != null && request.hdr.getType() == OpCode.error) {
                throw KeeperException.create(KeeperException.Code
                        .get(((ErrorTxn) request.txn).getErr()));
            }

            KeeperException ke = request.getException();
            if (ke != null) {
                throw ke;
            }

            if (LOG.isDebugEnabled()) {
                LOG.debug(request);
            }
            switch (request.type) {
            case OpCode.ping: {
                serverStats().updateLatency(request.createTime);

                lastOp = "PING";
                ((CnxnStats) cnxn.getStats()).updateForResponse(request.cxid,
                        request.zxid, lastOp, request.createTime,
                        System.currentTimeMillis());

                cnxn.sendResponse(new ReplyHeader(-2, getZKDatabase()
                        .getDataTreeLastProcessedZxid(), 0), null, "response");
                return;
            }
            case OpCode.createSession: {
                serverStats().updateLatency(request.createTime);

                lastOp = "SESS";
                ((CnxnStats) cnxn.getStats()).updateForResponse(request.cxid,
                        request.zxid, lastOp, request.createTime,
                        System.currentTimeMillis());

                cnxn.finishSessionInit(true);
                return;
            }
            case OpCode.create: {
                lastOp = "CREA";
                rsp = new CreateResponse(rc.path);
                err = Code.get(rc.err);
                break;
            }
            case OpCode.delete: {
                lastOp = "DELE";
                err = Code.get(rc.err);
                break;
            }
            case OpCode.setData: {
                lastOp = "SETD";
                rsp = new SetDataResponse(rc.stat);
                err = Code.get(rc.err);
                break;
            }
            case OpCode.setACL: {
                lastOp = "SETA";
                rsp = new SetACLResponse(rc.stat);
                err = Code.get(rc.err);
                break;
            }
            case OpCode.closeSession: {
                lastOp = "CLOS";
                closeSession = true;
                err = Code.get(rc.err);
                break;
            }
            case OpCode.sync: {
                lastOp = "SYNC";
                SyncRequest syncRequest = new SyncRequest();
                ZooKeeperServer.byteBuffer2Record(request.request, syncRequest);
                rsp = new SyncResponse(syncRequest.getPath());
                break;
            }
            case OpCode.exists: {
                lastOp = "EXIS";
                // TODO we need to figure out the security requirement for this!
                ExistsRequest existsRequest = new ExistsRequest();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        existsRequest);
                String path = existsRequest.getPath();
                if (path.indexOf('\0') != -1) {
                    throw new KeeperException.BadArgumentsException();
                }
                Stat stat = getZKDatabase().statNode(path,
                        existsRequest.getWatch() ? cnxn : null);
                rsp = new ExistsResponse(stat);
                break;
            }
            case OpCode.getData: {
                lastOp = "GETD";
                GetDataRequest getDataRequest = new GetDataRequest();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        getDataRequest);
                DataNode n = getZKDatabase().getNode(getDataRequest.getPath());
                if (n == null) {
                    throw new KeeperException.NoNodeException();
                }
                Long aclL;
                synchronized (n) {
                    aclL = n.acl;
                }
                checkACL(getZKDatabase().convertLong(aclL), ZooDefs.Perms.READ,
                        request.authInfo);
                Stat stat = new Stat();
                byte b[] = getZKDatabase().getData(getDataRequest.getPath(),
                        stat, getDataRequest.getWatch() ? cnxn : null);
                rsp = new GetDataResponse(b, stat);
                break;
            }
            case OpCode.setWatches: {
                lastOp = "SETW";
                SetWatches setWatches = new SetWatches();
                // XXX We really should NOT need this!!!!
                request.request.rewind();
                ZooKeeperServer.byteBuffer2Record(request.request, setWatches);
                long relativeZxid = setWatches.getRelativeZxid();
                getZKDatabase().setWatches(relativeZxid,
                        setWatches.getDataWatches(),
                        setWatches.getExistWatches(),
                        setWatches.getChildWatches(), cnxn);
                break;
            }
            case OpCode.getACL: {
                lastOp = "GETA";
                GetACLRequest getACLRequest = new GetACLRequest();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        getACLRequest);
                Stat stat = new Stat();
                List<ACL> acl = getZKDatabase().getACL(getACLRequest.getPath(),
                        stat);
                rsp = new GetACLResponse(acl, stat);
                break;
            }
            case OpCode.getChildren: {
                lastOp = "GETC";
                GetChildrenRequest getChildrenRequest = new GetChildrenRequest();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        getChildrenRequest);
                DataNode n = getZKDatabase().getNode(
                        getChildrenRequest.getPath());
                if (n == null) {
                    throw new KeeperException.NoNodeException();
                }
                Long aclG;
                synchronized (n) {
                    aclG = n.acl;

                }
                checkACL(getZKDatabase().convertLong(aclG), ZooDefs.Perms.READ,
                        request.authInfo);
                List<String> children = getZKDatabase().getChildren(
                        getChildrenRequest.getPath(), null,
                        getChildrenRequest.getWatch() ? cnxn : null);
                rsp = new GetChildrenResponse(children);
                break;
            }
            case OpCode.getChildren2: {
                lastOp = "GETC";
                GetChildren2Request getChildren2Request = new GetChildren2Request();
                ZooKeeperServer.byteBuffer2Record(request.request,
                        getChildren2Request);
                Stat stat = new Stat();
                DataNode n = getZKDatabase().getNode(
                        getChildren2Request.getPath());
                if (n == null) {
                    throw new KeeperException.NoNodeException();
                }
                Long aclG;
                synchronized (n) {
                    aclG = n.acl;
                }
                checkACL(getZKDatabase().convertLong(aclG), ZooDefs.Perms.READ,
                        request.authInfo);
                List<String> children = getZKDatabase().getChildren(
                        getChildren2Request.getPath(), stat,
                        getChildren2Request.getWatch() ? cnxn : null);
                rsp = new GetChildren2Response(children, stat);
                break;
            }
            }
        } catch (SessionMovedException e) {
            // session moved is a connection level error, we need to tear
            // down the connection otw ZOOKEEPER-710 might happen
            // ie client on slow follower starts to renew session, fails
            // before this completes, then tries the fast follower (leader)
            // and is successful, however the initial renew is then
            // successfully fwd/processed by the leader and as a result
            // the client and leader disagree on where the client is most
            // recently attached (and therefore invalid SESSION MOVED generated)
            cnxn.sendCloseSession();
            return;
        } catch (KeeperException e) {
            err = e.code();
        } catch (Exception e) {
            // log at error level as we are returning a marshalling
            // error to the user
            LOG.error("Failed to process " + request, e);
            StringBuilder sb = new StringBuilder();
            ByteBuffer bb = request.request;
            bb.rewind();
            while (bb.hasRemaining()) {
                sb.append(Integer.toHexString(bb.get() & 0xff));
            }
            LOG.error("Dumping request buffer: 0x" + sb.toString());
            err = Code.MARSHALLINGERROR;
        }

        ReplyHeader hdr = new ReplyHeader(request.cxid, request.zxid,
                err.intValue());

        serverStats().updateLatency(request.createTime);
        ((CnxnStats) cnxn.getStats()).updateForResponse(request.cxid,
                request.zxid, lastOp, request.createTime,
                System.currentTimeMillis());

        try {
            cnxn.sendResponse(hdr, rsp, "response");
            if (closeSession) {
                cnxn.sendCloseSession();
            }
        } catch (IOException e) {
            LOG.error("FIXMSG", e);
        }
    }

    /**
     * This method checks out the acl making sure it isn't null or empty, it has
     * valid schemes and ids, and expanding any relative ids that depend on the
     * requestor's authentication information.
     *
     * @param authInfo
     *            list of ACL IDs associated with the client connection
     * @param acl
     *            list of ACLs being assigned to the node (create or setACL
     *            operation)
     * @return
     */
    private boolean fixupACL(List<Id> authInfo, List<ACL> acl) {
        if (skipACL) {
            return true;
        }
        if (acl == null || acl.size() == 0) {
            return false;
        }
        Iterator<ACL> it = acl.iterator();
        LinkedList<ACL> toAdd = null;
        while (it.hasNext()) {
            ACL a = it.next();
            Id id = a.getId();
            if (id.getScheme().equals("world") && id.getId().equals("anyone")) {
                // wide open
            } else if (id.getScheme().equals("auth")) {
                // This is the "auth" id, so we have to expand it to the
                // authenticated ids of the requestor
                it.remove();
                if (toAdd == null) {
                    toAdd = new LinkedList<ACL>();
                }
                boolean authIdValid = false;
                for (Id cid : authInfo) {
                    AuthenticationProvider ap = ProviderRegistry
                            .getProvider(cid.getScheme());
                    if (ap == null) {
                        LOG.error("Missing AuthenticationProvider for "
                                + cid.getScheme());
                    } else if (ap.isAuthenticated()) {
                        authIdValid = true;
                        toAdd.add(new ACL(a.getPerms(), cid));
                    }
                }
                if (!authIdValid) {
                    return false;
                }
            } else {
                AuthenticationProvider ap = ProviderRegistry.getProvider(id
                        .getScheme());
                if (ap == null) {
                    return false;
                }
                if (!ap.isValid(id.getId())) {
                    return false;
                }
            }
        }
        if (toAdd != null) {
            for (ACL a : toAdd) {
                acl.add(a);
            }
        }
        return acl.size() > 0;
    }

    @Override
    public void run() {
        try {
            m_callout.run();
        } finally {
            running = false;
        }
    }

}
TOP

Related Classes of org.apache.zookeeper_voltpatches.server.ZooKeeperServer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.