/**
* 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.server.quorum;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.zookeeper.KeeperException.SessionExpiredException;
import org.apache.zookeeper.KeeperException.SessionMovedException;
import org.apache.zookeeper.KeeperException.UnknownSessionException;
import org.apache.zookeeper.server.SessionTrackerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The leader session tracker tracks local and global sessions on the leader.
*/
public class LeaderSessionTracker extends UpgradeableSessionTracker {
private static final Logger LOG = LoggerFactory.getLogger(LeaderSessionTracker.class);
private final boolean localSessionsEnabled;
private final SessionTrackerImpl globalSessionTracker;
/**
* Server id of the leader
*/
private final long serverId;
public LeaderSessionTracker(SessionExpirer expirer,
ConcurrentMap<Long, Integer> sessionsWithTimeouts,
int tickTime, long id, boolean localSessionsEnabled) {
this.globalSessionTracker = new SessionTrackerImpl(
expirer, sessionsWithTimeouts, tickTime, id);
this.localSessionsEnabled = localSessionsEnabled;
if (this.localSessionsEnabled) {
createLocalSessionTracker(expirer, tickTime, id);
}
serverId = id;
}
public void removeSession(long sessionId) {
if (localSessionTracker != null) {
localSessionTracker.removeSession(sessionId);
}
globalSessionTracker.removeSession(sessionId);
}
public void start() {
globalSessionTracker.start();
if (localSessionTracker != null) {
localSessionTracker.start();
}
}
public void shutdown() {
if (localSessionTracker != null) {
localSessionTracker.shutdown();
}
globalSessionTracker.shutdown();
}
public boolean isGlobalSession(long sessionId) {
return globalSessionTracker.isTrackingSession(sessionId);
}
public boolean addGlobalSession(long sessionId, int sessionTimeout) {
boolean added =
globalSessionTracker.addSession(sessionId, sessionTimeout);
if (localSessionsEnabled && added) {
// Only do extra logging so we know what kind of session this is
// if we're supporting both kinds of sessions
LOG.info("Adding global session 0x" + Long.toHexString(sessionId));
}
return added;
}
public boolean addSession(long sessionId, int sessionTimeout) {
boolean added;
if (localSessionsEnabled && !isGlobalSession(sessionId)) {
added = localSessionTracker.addSession(sessionId, sessionTimeout);
// Check for race condition with session upgrading
if (isGlobalSession(sessionId)) {
added = false;
localSessionTracker.removeSession(sessionId);
} else if (added) {
LOG.info("Adding local session 0x" + Long.toHexString(sessionId));
}
} else {
added = addGlobalSession(sessionId, sessionTimeout);
}
return added;
}
public boolean touchSession(long sessionId, int sessionTimeout) {
if (localSessionTracker != null &&
localSessionTracker.touchSession(sessionId, sessionTimeout)) {
return true;
}
return globalSessionTracker.touchSession(sessionId, sessionTimeout);
}
public long createSession(int sessionTimeout) {
if (localSessionsEnabled) {
return localSessionTracker.createSession(sessionTimeout);
}
return globalSessionTracker.createSession(sessionTimeout);
}
// Returns the serverId from the sessionId (the high order byte)
public static long getServerIdFromSessionId(long sessionId) {
return sessionId >> 56;
}
public void checkSession(long sessionId, Object owner)
throws SessionExpiredException, SessionMovedException,
UnknownSessionException {
if (localSessionTracker != null) {
try {
localSessionTracker.checkSession(sessionId, owner);
// A session can both be a local and global session during
// upgrade
if (!isGlobalSession(sessionId)) {
return;
}
} catch(UnknownSessionException e) {
// Ignore. We'll check instead whether it's a global session
}
}
try {
globalSessionTracker.checkSession(sessionId, owner);
// if we can get here, it is a valid global session
return;
} catch (UnknownSessionException e) {
// Ignore. This may be local session from other servers.
}
/*
* if local session is not enabled or it used to be our local session
* throw sessions expires
*/
if (!localSessionsEnabled
|| (getServerIdFromSessionId(sessionId) == serverId)) {
throw new SessionExpiredException();
}
}
public void checkGlobalSession(long sessionId, Object owner)
throws SessionExpiredException, SessionMovedException {
try {
globalSessionTracker.checkSession(sessionId, owner);
} catch (UnknownSessionException e) {
// For global session, if we don't know it, it is already expired
throw new SessionExpiredException();
}
}
public void setOwner(long sessionId, Object owner)
throws SessionExpiredException {
if (localSessionTracker != null) {
try {
localSessionTracker.setOwner(sessionId, owner);
return;
} catch(SessionExpiredException e) {
// Ignore. We'll check instead whether it's a global session
}
}
globalSessionTracker.setOwner(sessionId, owner);
}
public void dumpSessions(PrintWriter pwriter) {
if (localSessionTracker != null) {
pwriter.print("Local ");
localSessionTracker.dumpSessions(pwriter);
pwriter.print("Global ");
}
globalSessionTracker.dumpSessions(pwriter);
}
public void setSessionClosing(long sessionId) {
// call is no-op if session isn't tracked so safe to call both
if (localSessionTracker != null) {
localSessionTracker.setSessionClosing(sessionId);
}
globalSessionTracker.setSessionClosing(sessionId);
}
public Map<Long, Set<Long>> getSessionExpiryMap() {
Map<Long, Set<Long>> sessionExpiryMap;
// combine local and global sessions, getting local first so upgrades
// to global are caught
if (localSessionTracker != null) {
sessionExpiryMap = localSessionTracker.getSessionExpiryMap();
} else {
sessionExpiryMap = new TreeMap<Long, Set<Long>>();
}
sessionExpiryMap.putAll(globalSessionTracker.getSessionExpiryMap());
return sessionExpiryMap;
}
}