All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.jiac.micro.ext.service.impl.ServiceEngine Maven / Gradle / Ivy

The newest version!
/*
 * MicroJIAC - A Lightweight Agent Framework
 * This file is part of MicroJIAC Emulated-Service-Engine.
 *
 * Copyright (c) 2007-2011 DAI-Labor, Technische Universität Berlin
 *
 * This library includes software developed at DAI-Labor, Technische
 * Universität Berlin (http://www.dai-labor.de)
 *
 * This library is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see .
 */

package de.jiac.micro.ext.service.impl;

import java.io.IOException;
import java.util.Vector;

import org.slf4j.Logger;

import com.github.libxjava.lang.IClassLoader;

import de.jiac.micro.agent.handle.ICommunicationHandle;
import de.jiac.micro.core.handle.IReflector;
import de.jiac.micro.core.io.IAddress;
import de.jiac.micro.core.scope.Scope;
import de.jiac.micro.ext.service.IService;
import de.jiac.micro.interaction.rr.IRequestHandler;
import de.jiac.micro.interaction.rr.RequestResponseProtocol;

/**
 * @author Marcel Patzlaff
 */
public final class ServiceEngine implements IRequestHandler {
    static final String SEARCH= "mjiac-search";
    static final String INVOKE= "mjiac-invoke";
    static final String SEARCH_GROUP= "mjiacservicegroup";
    
    private static class Synchroniser {
        protected Synchroniser() {}
        public String source= null;
        public Object content= null; 
    }
    
    private static Object getDefaultValue(String mDescriptor) {
        char type= mDescriptor.charAt(mDescriptor.indexOf(')') + 1);
        Object defaultValue= null;
        switch(type) {
            case 'B': {
                defaultValue= new Byte(Byte.MIN_VALUE);
                break;
            }
            
            case 'C': {
                defaultValue= new Character(Character.MIN_VALUE);
                break;
            }
            
            case 'D': {
                defaultValue= new Double(Double.NaN);
                break;
            }
            
            case 'F': {
                defaultValue= new Float(Float.NaN);
                break;
            }
            
            case 'I': {
                defaultValue= new Integer(Integer.MIN_VALUE);
                break;
            }
            
            case 'J': {
                defaultValue= new Long(Long.MIN_VALUE);
                break;
            }
            
            case 'S': {
                defaultValue= new Short(Short.MIN_VALUE);
                break;
            }
            
            case 'Z': {
                defaultValue= Boolean.FALSE;
                break;
            }
        }
        
        return defaultValue;
    }
    
    private static IAddress getAddress(String addressStr) {
        ICommunicationHandle ch= (ICommunicationHandle) Scope.getContainer().getHandle(ICommunicationHandle.class);
        
        if(ch != null) {
            return ch.getAddressForString(addressStr);
        }
        
        return null;
    }
    
    private class AsyncRunner implements Runnable {
        protected boolean searchOnly;
        protected AbstractServiceContext context;
        protected String mName;
        protected String mDescriptor;
        protected Object[] arguments;
        
        protected AsyncRunner() {}
        
        public void run() {
            try {
                if(searchOnly) {
                    // FIXME: multiple providers
                    try {
                        internalSearchWithBlocking(context, 1);
                    } catch (Exception e) {
                        logger.warn("could not lookup providers", e);
                    }
                } else {
                    try {
                        synchronized (context) {
                            context.asyncMethodName= mName;
                            context.asyncMethodDescriptor= mDescriptor;
                        }
                        
                        Object result= internalSearchAndInvokeWithBlocking(context, mName, mDescriptor, arguments);
                        
                        synchronized (context) {
                            context.asyncResult= result;
                        }
                    } catch (Exception e) {
                        synchronized(context) {
                            context.asyncResult= e;
                        }
                    }
                }
            } finally {
                context.lock(false);
            }
        }
    }
    
    protected final Logger logger;
    private final RequestResponseProtocol _requestProtocol;
    private Vector _services;
    
    public ServiceEngine(RequestResponseProtocol requestProtocol) {
        _requestProtocol= requestProtocol;
        _requestProtocol.setRequestHandler(this);
        _services= new Vector();
        logger= Scope.getScope().getContainerReference().getLogger("ServiceEngine");
    }
    
    public void onRequest(String requestId, String key, Object content, String source) {
        if(SEARCH.equals(key)) {
            String serviceName= (String) content;
            if(getServiceInstance(serviceName) != null) {
                try {
                    _requestProtocol.respond(requestId, key, serviceName, source);
                } catch (IOException e) {
                    logger.error("could not respond to search request", e);
                }
            }
        } else if(INVOKE.equals(key)) {
            try {
                ServiceInvocation request= (ServiceInvocation) content;
                IService service= getServiceInstance(request.serviceName);
                if(service != null) {
                    IReflector reflector= (IReflector) Scope.getContainer().getHandle(IReflector.class);
                    
                    // reflector does not want an array if the method only has one argument!
                    Object args= request.arguments.length == 1 ? request.arguments[0] : request.arguments;
                    request.setResult(reflector.invokeMethodWithDescriptor(service, request.serviceName, request.mName, request.mDescriptor, args));
                    _requestProtocol.respond(requestId, key, request, source);
                }
            } catch (Exception e) {
                logger.error("could not execute service", e);
            }
        }
    }

    public void onResponse(String requestId, String key, Object content, String source, Object ctx) {
        Synchroniser sync= (Synchroniser) ctx;
        synchronized (sync) {
            sync.source= source;
            sync.content= content;
            sync.notify();
        }
    }

    public void onTimeout(String requestId, Object ctx) {
        Synchroniser sync= (Synchroniser) ctx;
        synchronized (sync) {
            sync.notify();
        }
    }

    void deployService(IService service) {
        synchronized (_services) {
            if(!_services.contains(service)) {
                _services.addElement(service);
            }
        }
    }
    
    void undeployService(IService service) {
        _services.removeElement(service);
    }
    
    void search(final AbstractServiceContext context, int max) {
        context.lock(true);
        
        if(context.isBlocking()) {
            try {
                internalSearchWithBlocking(context, max);
            } catch (Exception e) {
                logger.warn("could not lookup providers", e);
            } finally {
                context.lock(false);
            }
        } else {
            // TODO we should cache the AsyncRunners!
            
            AsyncRunner runner= new AsyncRunner();
            runner.context= context;
            runner.searchOnly= true;
            
            // TODO thread pooling?
            Scope.createScopeAwareThread(runner, "worker").start();
        }
    }

    Object searchAndInvoke(final AbstractServiceContext context, final String mName, final String mDescriptor, final Object[] arguments) throws Exception {
        context.lock(true);
        
        if(context.isBlocking()) {
            try {
                return internalSearchAndInvokeWithBlocking(context, mName, mDescriptor, arguments);
            } finally {
                context.lock(false);
            }
        } else {
            // TODO if services are used excessively than try to cache the AsyncRunners
            AsyncRunner runner= new AsyncRunner();
            runner.context= context;
            runner.mName= mName;
            runner.mDescriptor= mDescriptor;
            runner.arguments= arguments;
            
            // TODO thread pooling?
            Scope.createScopeAwareThread(runner, mName).start();
            return getDefaultValue(mDescriptor);
        }
    }
    
    protected void internalSearchWithBlocking(final AbstractServiceContext context, final int max) throws IOException, InterruptedException {
        long timeout= context.getTimeout();
        Synchroniser sync= new Synchroniser();
        final String serviceName= context.serviceClass.getName();
        
        synchronized (sync) {
            // FIXME: wait for max providers
            _requestProtocol.broadcastRequest(SEARCH, serviceName, SEARCH_GROUP, timeout, sync);
            sync.wait(timeout);
        }
        
        if(sync.source == null || sync.content == null || !serviceName.equals(sync.content)) {
            throw new RuntimeException("service unavailable: '" + serviceName + "'");
        }
        
        IAddress providerAddress= getAddress(sync.source);
        
        if(providerAddress != null && !(context.providers.contains(providerAddress))) {
            context.providers.addElement(providerAddress);
        }
    }
    
    /**
     * Note: The caller must lock the specified ServiceContext!
     */
    protected Object internalSearchAndInvokeWithBlocking(AbstractServiceContext context, String mName, String mDescriptor, Object[] arguments) throws Exception {
        // FIXME: reuse providers already looked-up!
        
        long timeout= context.getTimeout();
        long searchStart= System.currentTimeMillis();
        Synchroniser sync= new Synchroniser();
        final String serviceName= context.serviceClass.getName();
        
        synchronized (sync) {
            // wait at most one third of the timeout for search responses
            _requestProtocol.broadcastRequest(SEARCH, serviceName, SEARCH_GROUP, timeout / 3, sync);
            sync.wait(timeout);
        }
        
        if(sync.source == null || sync.content == null || !serviceName.equals(sync.content)) {
            throw new RuntimeException("service unavailable: '" + serviceName + "'");
        }
        
        String providerAddress= sync.source;
        sync.source= null;
        sync.content= null;
        timeout-= (System.currentTimeMillis() - searchStart);
        
        if(timeout <= 0) {
            throw new RuntimeException("no time to invoke service: '" + serviceName + "'");
        }
        
        synchronized (sync) {
            ServiceInvocation invocation= new ServiceInvocation(serviceName, mName, mDescriptor, arguments);
            _requestProtocol.request(INVOKE, invocation, providerAddress, timeout, sync);
            sync.wait(timeout);
        }
        
        if(sync.content == null) {
            throw new RuntimeException("service invocation timed out: " + serviceName + "'");
        }
        
        return ((ServiceInvocation) sync.content).result;
    }
    
    private IService getServiceInstance(String serviceName) {
        IClassLoader classLoader= Scope.getContainer().getClassLoader();
        try {
            Class serviceInterface= classLoader.loadClass(serviceName);
            
            for(int i= 0; i < _services.size(); ++i) {
                IService current= (IService) _services.elementAt(i);
                if(serviceInterface.isInstance(current)) {
                    return current;
                }
            }
        } catch (ClassNotFoundException e) {
            logger.warn("service not found: '" + serviceName + "'", e);
        }
        
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy