/*
* Copyright 2013 The Skfiy Open Association.
*
* 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 org.skfiy.typhon.net;
import com.alibaba.fastjson.JSON;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.skfiy.typhon.Component;
import org.skfiy.typhon.ComponentException;
import org.skfiy.typhon.dispatcher.DispatcherFactory;
import org.skfiy.typhon.packet.Namespaces;
import org.skfiy.typhon.packet.Packet;
import org.skfiy.typhon.session.Session;
import org.skfiy.typhon.session.SessionConstants;
import org.skfiy.typhon.session.SessionContext;
import org.skfiy.util.CustomizableThreadCreator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@code JSON } 消息协议处理器.
*
* @author Kevin Zou <kevinz@skfiy.org>
*/
@Singleton
public class JsonProtocolHandler implements Component, ProtocolHandler {
private static final Logger LOG = LoggerFactory.getLogger(JsonProtocolHandler.class);
@Inject
protected DispatcherFactory dispatcherFactory;
@Resource
protected Set<SessionErrorHandler> sessionErrorHandlers;
protected ExecutorService executorService;
protected CustomizableThreadCreator threadCreator;
protected ThreadLocal<Session> _local_session;
private boolean shutdown = false;
@PostConstruct
@Override
public void init() {
try {
Field field = SessionContext.class.getDeclaredField("LOCAL_SESSION");
field.setAccessible(true);
_local_session = (ThreadLocal<Session>) field.get(SessionContext.class);
} catch (NoSuchFieldException ex) {
throw new ComponentException("SessionContext中不存在[LOCAL_SESSION]属性", ex);
} catch (SecurityException ex) {
throw new ComponentException(
getClass() + " 缺少访问SessionContext [LOCAL_SESSION] 属性的权限", ex);
} catch (Exception ex) {
throw new ComponentException(ex);
}
threadCreator = new CustomizableThreadCreator("json-exec-");
threadCreator.setThreadGroupName("Protocol-ThreadGroup");
threadCreator.setDaemon(true);
executorService = new ThreadPoolExecutor(Integer.getInteger("protocol.corePoolSize", 4),
Integer.getInteger("protocol.maxPoolSize", 50),
Integer.getInteger("protocol.keepAliveTime", 3000),
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(Integer.getInteger("protocol.workQueueSize", 30)),
new CustomizableThreadFactory(),
new NewThreadPolicy());
}
@Override
public void reload() {
throw new UnsupportedOperationException("Not supported yet.");
}
@PreDestroy
@Override
public void destroy() {
shutdown = true;
executorService.shutdown();
}
@Override
public void handle(final Session session, final byte[] nsbs, final byte[] datas) {
if (shutdown) {
LOG.warn("shutdown......");
return;
}
final String ns = new String(nsbs, StandardCharsets.UTF_8);
Runnable impl = new Runnable() {
@Override
public void run() {
synchronized (session) {
_local_session.set(session);
try {
Packet packet = JSON.parseObject(datas,
dispatcherFactory.getPacketClass(ns));
packet.setNs(ns);
session.setAttribute(SessionConstants.ATTR_CONTEXT_PACKET, packet);
dispatcherFactory.getDispatcher().dispatch(ns, packet);
session.removeAttribute(SessionConstants.ATTR_CONTEXT_PACKET);
} catch (Throwable t) {
LOG.error("{}:{} --> {}", new String(nsbs, StandardCharsets.UTF_8),
new String(datas, StandardCharsets.UTF_8), t);
for (SessionErrorHandler seh : sessionErrorHandlers) {
if (seh.getErrorType().isAssignableFrom(t.getClass())) {
seh.handleError(session, t);
break;
}
}
}
}
}
};
if (Namespaces.LOGOUT.equals(ns)) {
impl.run();
} else {
executorService.execute(impl);
}
}
private class CustomizableThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
return threadCreator.createThread(r);
}
}
private class NewThreadPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
Thread t = threadCreator.createThread(r);
t.setDaemon(true);
t.start();
}
}
}