/*****************************************************************************
* Copyright [2013] [Jules White] *
* *
* 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.magnum.soda;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import net.engio.mbassy.listener.Listener;
import net.engio.mbassy.listener.Mode;
import org.magnum.soda.aop.DefaultInvocationProcessorFactory;
import org.magnum.soda.aop.InvocationProcessorFactory;
import org.magnum.soda.auth.AuthInvocationProcessor;
import org.magnum.soda.msg.LocalAddress;
import org.magnum.soda.proxy.JavaReflectionProxyCreator;
import org.magnum.soda.proxy.ObjRef;
import org.magnum.soda.proxy.ProxyCreator;
import org.magnum.soda.proxy.ProxyFactory;
import org.magnum.soda.proxy.RecordingProxy;
import org.magnum.soda.svc.AuthService;
import org.magnum.soda.svc.AuthenticationListener;
import org.magnum.soda.svc.DefaultNamingService;
import org.magnum.soda.svc.InvocationDispatcher;
import org.magnum.soda.svc.NamingService;
import org.magnum.soda.svc.ObjInvoker;
import org.magnum.soda.svc.ObtainNamingServiceMsg;
import org.magnum.soda.svc.ObtainNamingServiceRespMsg;
import org.magnum.soda.transport.Address;
import org.magnum.soda.transport.Transport;
import org.magnum.soda.transport.TransportListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.eventbus.Subscribe;
public class Soda implements TransportListener {
private static final Logger Log = LoggerFactory.getLogger(Soda.class);
private Transport transport_;
private ObjRef namingServiceRef_;
private NamingService namingService_ = new DefaultNamingService();
private LocalAddress localAddress_;
private MBassyMsgBus msgBus_ = new MBassyMsgBus();
private ObjRegistry objRegistry_;
private ProxyFactory proxyFactory_;
private ObjInvoker objInvoker_;
private ProxyCreator proxyCreator_;
// use the default classloader by default, but this allows us to
// specify a different classloader if we want proxies to be created
// for classes loaded in at runtime (rather than compiletime)
private ClassLoader classLoader_ = getClass().getClassLoader();
private AuthService authService_;
private Map<Object, SodaBinding> ctxPolyObjBinding_ = null;
public Soda() {
this(new LocalAddress());
}
public Soda(LocalAddress addr) {
localAddress_ = addr;
objRegistry_ = createObjRegistry();
proxyCreator_ = getProxyCreator();
proxyFactory_ = new ProxyFactory(objRegistry_, proxyCreator_,
localAddress_, msgBus_);
objInvoker_ = new ObjInvoker(msgBus_, objRegistry_, proxyFactory_);
objInvoker_.setProcessorFactory(getInvocationProcessorFactory());
namingServiceRef_ = objRegistry_.publish(namingService_);
msgBus_.subscribe(this);
ctxPolyObjBinding_ = new HashMap<Object, SodaBinding>();
}
public Soda(boolean becomeserver) {
this(becomeserver, null);
}
public Soda(boolean becomeserver, AuthService svc) {
this();
if (becomeserver) {
svc = (svc == null)? getAuthService() : svc;
getObjRegistry().insert(NamingService.ROOT_NAMING_SVC,
getNamingService());
getObjRegistry()
.insert(AuthService.ROOT_AUTH_SVC, svc);
}
}
public Soda(Transport t, LocalAddress addr) {
this(addr);
transport_ = t;
transport_.setListener(this);
}
protected ObjRegistry createObjRegistry(){
return new DefaultObjRegistry(getLocalAddress());
}
protected synchronized ProxyCreator getProxyCreator() {
return new JavaReflectionProxyCreator(classLoader_);
}
protected synchronized AuthService getAuthService() {
return AuthService.NO_AUTH_SVC;
}
protected synchronized InvocationProcessorFactory getInvocationProcessorFactory(){
DefaultInvocationProcessorFactory fact = new DefaultInvocationProcessorFactory();
fact.addProcessor(SodaAuth.class, AuthInvocationProcessor.class);
return fact;
}
public <T> T invoke(T obj) {
return invoke(obj, null);
}
@SuppressWarnings("unchecked")
public <T> T invoke(T obj, InvocationDispatcher dispatcher) {
RecordingProxy recorder = new RecordingProxy(obj, proxyCreator_,
dispatcher);
Class<?>[] types = { obj.getClass() };
if (!proxyCreator_.supportsNonInterfaceProxies()) {
types = obj.getClass().getInterfaces();
}
T proxy = (T) proxyCreator_.createProxy(classLoader_,
types, recorder);
return proxy;
}
public Runnable asRunnable(Object o) {
InvocationHandler hdlr = proxyCreator_.getInvocationHandler(o);
if (hdlr == null || !(hdlr instanceof RecordingProxy)) {
throw new RuntimeException(
"asRunnable can only be called on an object that is"
+ " returned from invoke(...)");
}
return (Runnable) hdlr;
}
@Subscribe
@Listener(delivery = Mode.Concurrent)
public void handleNamingServiceRequest(ObtainNamingServiceMsg msg) {
ObtainNamingServiceRespMsg resp = (ObtainNamingServiceRespMsg) msg
.createReply();
resp.setNamingService(namingServiceRef_);
msgBus_.publish(resp);
}
public void passByValue(Class<?> type) {
proxyFactory_.passByValue(type);
}
public void invokeAsync(Class<?> c){
for(Method m : c.getMethods()){
if(m.getReturnType() == void.class){
invokeAsync(m);
}
}
}
public void invokeAsync(Method m){
proxyFactory_.getInvocationSettings().invokeAsync(m);
}
public void setInvokeVoidMethodsToAsync(boolean async){
proxyFactory_.getInvocationSettings().setInvokeVoidMethodsAsync(async);
}
public void setAllowNonLocalProxyInvocations(boolean allow){
objInvoker_.setInvokeProxies(allow);
}
public LocalAddress getLocalAddress() {
return localAddress_;
}
public MBassyMsgBus getMsgBus() {
return msgBus_;
}
public ObjRegistry getObjRegistry() {
return objRegistry_;
}
@SuppressWarnings("unchecked")
public <T> T get(Class<T> type, ObjRef ref){
return (T)proxyFactory_.createProxy(new Class[]{type}, ref);
}
public <T> T get(Class<T> type, String name) {
return namingService_.get(type, name);
}
public void bind(Object o, String name) {
namingService_.bind(o, name);
}
public void connect(Address addr) {
transport_.connect(addr);
}
public void connect(Transport t, Address addr) {
setTransport(t);
connect(addr);
}
public void setTransport(Transport t) {
transport_ = t;
transport_.setListener(this);
}
protected NamingService getNamingService() {
return namingService_;
}
protected ObjInvoker getInvoker() {
return objInvoker_;
}
public SodaBinding bind(Object o) {
SodaBinding b = new SodaBinding();
this.ctxPolyObjBinding_.put(o, b);
return b;
}
@SuppressWarnings("unchecked")
public <T> SodaQuery<T> find(Class<T> type, SodaContext ctx) {
SodaQuery<Object> sq = new SodaQuery<Object>();
Iterator<Object> itrObject = this.ctxPolyObjBinding_.keySet()
.iterator();
while (itrObject.hasNext()) {
Object contextObject = itrObject.next();
Class<? extends Object> cls = contextObject.getClass();
if (!type.isAssignableFrom(cls)) {
continue;
}
SodaBinding sb = this.ctxPolyObjBinding_.get(contextObject);
Iterator<SodaContext> itrSodaBinding = sb.getContexts_().iterator();
while (itrSodaBinding.hasNext()) {
SodaContext stx = itrSodaBinding.next();
if (stx.equals(ctx)) {
sq.getList_().add(contextObject);
}
}
}
return (SodaQuery<T>) sq;
}
// return the nearest N locations of the given geohash
// public <T> SodaQuery<T> findNearest(Class<T> type, SodaContext ctx) {
//
// }
/*
* function getNearest(currentLocation, locations, maxNeighbors) { var
* matching = {}, accuracy = 12, matchCount = 0; while (matchCount <
* maxNeighbors && accuracy > 0) { var cmpHash =
* currentLocation.geoHash.substring(0,accuracy); for (var i = 0; i <
* locations.length; i++) { if (locations[i].geoHash in matching) continue;
* //don't re-check ones that have already matched if
* (locations[i].geoHash.substring(0,accuracy) === cmpHash) {
* matching[locations[i].geoHash] = locations[i]; matchCount++; if
* (matchCount === maxNeighbors) break; } } accuracy--; } var tmp = []; for
* (var geoHash in matching) { tmp.push(matching[geoHash]); } return tmp; }
*/
@Override
public void connected() {
try {
ObjRef ref = NamingService.ROOT_NAMING_SVC;
NamingService svc = (NamingService) proxyFactory_.createProxy(ref);
namingService_.setParent(svc, proxyFactory_);
} catch (Exception e) {
Log.error("Error looking up root naming service", e);
}
try {
ObjRef ref = AuthService.ROOT_AUTH_SVC;
authService_ = (AuthService) proxyFactory_.createProxy(ref);
} catch (Exception e) {
Log.error("Error looking up auth service and authenticating", e);
}
}
public void authenticate(User u, AuthenticationListener l) {
authService_.authenticate(u, l);
}
@Override
public void disconnected() {
}
public void setClassLoader(ClassLoader cl) {
classLoader_ = cl;
}
}