/* * Copyright 2010 NCHOVY * * 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.krakenapps.rpc.impl; import java.util.Map; import java.util.Properties; import java.util.UUID; import org.krakenapps.rpc.RpcConnection; import org.krakenapps.rpc.RpcContext; import org.krakenapps.rpc.RpcException; import org.krakenapps.rpc.RpcMethod; import org.krakenapps.rpc.RpcPeer; import org.krakenapps.rpc.RpcPeerRegistry; import org.krakenapps.rpc.RpcService; import org.krakenapps.rpc.RpcServiceBinding; import org.krakenapps.rpc.RpcSession; import org.krakenapps.rpc.RpcSessionEvent; import org.krakenapps.rpc.RpcSessionEventCallback; import org.krakenapps.rpc.RpcTrustLevel; import org.krakenapps.rpc.SimpleRpcService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * all connections shares one control service. * * @author xeraph * */ public class RpcControlService extends SimpleRpcService { private final Logger logger = LoggerFactory.getLogger(RpcControlService.class.getName()); private String guid; private RpcPeerRegistry peerRegistry; public RpcControlService(String guid, RpcPeerRegistry peerRegistry) { this.guid = guid; this.peerRegistry = peerRegistry; } @Override public void sessionRequested(RpcSessionEvent e) { logger.debug("kraken-rpc: rpc-control session requested"); } @RpcMethod(name = "ping") public int ping(int value) { return value; } @RpcMethod(name = "peering-request") public Object[] handlePeeringRequest(String peerGuid) { RpcConnection conn = RpcContext.getConnection(); conn.setPeerGuid(peerGuid); logger.trace("rpc-control: peering request received from {}", peerGuid); if (conn.getPeerCertificate() == null) { String nonce = UUID.randomUUID().toString(); conn.setNonce(nonce); return new Object[] { "challenge", guid, nonce }; } RpcPeer peer = peerRegistry.findPeer(peerGuid); RpcTrustLevel trustLevel = RpcTrustLevel.Untrusted; if (peer != null) trustLevel = peer.getTrustLevel(); else trustLevel = RpcTrustLevel.Low; conn.setTrustLevel(trustLevel); return new Object[] { "success", guid, trustLevel.getCode() }; } @RpcMethod(name = "authenticate") public Object[] handleAuthenticate(String guid, String hash) { RpcConnection conn = RpcContext.getConnection(); if (conn.getNonce() == null) { logger.warn("kraken-rpc: peer request first"); throw new RpcException("peer request first"); } RpcPeer peer = null; boolean result = false; try { peer = peerRegistry.authenticate(conn.getPeerGuid(), conn.getNonce(), hash); conn.setTrustLevel(peer.getTrustLevel()); result = true; } catch (IllegalStateException e) { logger.warn("kraken-rpc: auth failed, peer=" + conn.getPeerGuid(), e); } int trustLevel = peer != null ? peer.getTrustLevel().getCode() : RpcTrustLevel.Low.getCode(); return new Object[] { result, trustLevel }; } @RpcMethod(name = "session-request") public Integer handleSessionRequest(String serviceName, Map<String, Object> params) { RpcConnectionImpl conn = (RpcConnectionImpl) RpcContext.getConnection(); RpcServiceBinding binding = conn.findServiceBinding(serviceName); if (binding == null) throw new IllegalStateException("service binding not found: " + serviceName); if (conn.getTrustLevel() == null || conn.getTrustLevel() == RpcTrustLevel.Untrusted) throw new IllegalStateException("[" + serviceName + "] session request not allowed, establish peering first"); RpcService service = binding.getService(); RpcSessionEvent event = new RpcSessionEventImpl(RpcSessionEvent.Requested, null, toProperties(params)); // may throw exception if session request is not accepted service.sessionRequested(event); int newSessionId = conn.nextSessionId(); RpcSession session = conn.openSession(newSessionId, serviceName); session.addListener(new RpcServiceSessionClosedCallback(service)); if (logger.isDebugEnabled()) logger.debug("kraken-rpc: new [{}] service session [{}] created by session request", serviceName, newSessionId); return newSessionId; } private Properties toProperties(Map<String, Object> map) { Properties props = new Properties(); if (map == null) return props; for (String key : map.keySet()) { props.put(key, map.get(key)); } return props; } /** * It will invoke RpcService.sessionClosed callback when session is closed. * This callback is registered to session when new session request is * accepted at server-side. * * @author xeraph * */ private static class RpcServiceSessionClosedCallback implements RpcSessionEventCallback { private RpcService service; public RpcServiceSessionClosedCallback(RpcService service) { this.service = service; } @Override public void sessionClosed(RpcSession session) { RpcSessionEvent event = new RpcSessionEventImpl(RpcSessionEvent.Closed, session); service.sessionClosed(event); } } }