/**
*Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)]
*Licensed 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 net.rubyeye.xmemcached.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import net.rubyeye.xmemcached.HashAlgorithm;
import net.rubyeye.xmemcached.networking.MemcachedSession;
import com.google.code.yanf4j.core.Session;
/**
* Session locator base on hash(key) mod sessions.size().Standard hash strategy
*
* @author dennis
*
*/
public class ArrayMemcachedSessionLocator extends
AbstractMemcachedSessionLocator {
private HashAlgorithm hashAlgorighm;
private transient volatile List<List<Session>> sessions;
public ArrayMemcachedSessionLocator() {
this.hashAlgorighm = HashAlgorithm.NATIVE_HASH;
}
public ArrayMemcachedSessionLocator(HashAlgorithm hashAlgorighm) {
this.hashAlgorighm = hashAlgorighm;
}
public final void setHashAlgorighm(HashAlgorithm hashAlgorighm) {
this.hashAlgorighm = hashAlgorighm;
}
public final long getHash(int size, String key) {
long hash = this.hashAlgorighm.hash(key);
return hash % size;
}
final Random rand = new Random();
public final Session getSessionByKey(final String key) {
if (this.sessions == null || this.sessions.size() == 0) {
return null;
}
// Copy on read
List<List<Session>> sessionList = this.sessions;
int size = sessionList.size();
if (size == 0) {
return null;
}
long start = this.getHash(size, key);
List<Session> sessions = sessionList.get((int) start);
Session session = getRandomSession(sessions);
// If it is not failure mode,get next available session
if (!this.failureMode && (session == null || session.isClosed())) {
long next = this.getNext(size, start);
while ((session == null || session.isClosed()) && next != start) {
sessions = sessionList.get((int) next);
next = this.getNext(size, next);
session = getRandomSession(sessions);
}
}
return session;
}
private Session getRandomSession(List<Session> sessions) {
if (sessions == null || sessions.isEmpty())
return null;
return sessions.get(rand.nextInt(sessions.size()));
}
public final long getNext(int size, long start) {
if (start == size - 1) {
return 0;
} else {
return start + 1;
}
}
public final void updateSessions(final Collection<Session> list) {
if (list == null || list.isEmpty()) {
this.sessions = Collections.emptyList();
return;
}
Collection<Session> copySessions = list;
List<List<Session>> tmpList = new ArrayList<List<Session>>();
Session target = null;
List<Session> subList = null;
for (Session session : copySessions) {
if (target == null) {
target = session;
subList = new ArrayList<Session>();
subList.add(target);
} else {
if (session.getRemoteSocketAddress().equals(
target.getRemoteSocketAddress())) {
subList.add(session);
} else {
tmpList.add(subList);
target = session;
subList = new ArrayList<Session>();
subList.add(target);
}
}
}
// The last one
if (subList != null) {
tmpList.add(subList);
}
List<List<Session>> newSessions = new ArrayList<List<Session>>(
tmpList.size() * 2);
for (List<Session> sessions : tmpList) {
if (sessions != null && !sessions.isEmpty()) {
Session session = sessions.get(0);
if (session instanceof MemcachedTCPSession) {
int weight = ((MemcachedSession) session).getWeight();
for (int i = 0; i < weight; i++) {
newSessions.add(sessions);
}
} else {
newSessions.add(sessions);
}
}
}
this.sessions = newSessions;
}
}