Package org.jboss.errai.bus.server.service.bootstrap

Source Code of org.jboss.errai.bus.server.service.bootstrap.DiscoverServices

/*
* Copyright 2009 JBoss, a divison Red Hat, Inc
*
* 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.jboss.errai.bus.server.service.bootstrap;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.jboss.errai.bus.client.api.MessageCallback;
import org.jboss.errai.bus.client.api.ResourceProvider;
import org.jboss.errai.bus.client.api.builder.AbstractRemoteCallBuilder;
import org.jboss.errai.bus.client.framework.MessageBus;
import org.jboss.errai.bus.client.framework.ProxyProvider;
import org.jboss.errai.bus.client.framework.RequestDispatcher;
import org.jboss.errai.bus.rebind.RebindUtils;
import org.jboss.errai.bus.server.ErraiBootstrapFailure;
import org.jboss.errai.bus.server.annotations.*;
import org.jboss.errai.bus.server.annotations.security.RequireAuthentication;
import org.jboss.errai.bus.server.annotations.security.RequireRoles;
import org.jboss.errai.bus.server.api.Module;
import org.jboss.errai.bus.server.io.ConversationalEndpointCallback;
import org.jboss.errai.bus.server.io.EndpointCallback;
import org.jboss.errai.bus.server.io.RemoteServiceCallback;
import org.jboss.errai.bus.server.security.auth.rules.RolesRequiredRule;
import org.jboss.errai.bus.server.service.ErraiServiceConfigurator;
import org.jboss.errai.bus.server.service.ErraiServiceConfiguratorImpl;
import org.jboss.errai.bus.server.util.ConfigUtil;
import org.jboss.errai.bus.server.util.ConfigVisitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Method;
import java.util.*;

import static org.jboss.errai.bus.server.util.ConfigUtil.visitAllTargets;

/**
* Parses the annotation meta data and configures both services and extensions.
*
* @author: Heiko Braun <hbraun@redhat.com>
* @date: May 3, 2010
*/
class DiscoverServices implements BootstrapExecution
{
  private Logger log = LoggerFactory.getLogger(DiscoverServices.class);

  public void execute(final BootstrapContext context)
  {
    final ErraiServiceConfiguratorImpl config = (ErraiServiceConfiguratorImpl)context.getConfig();

    boolean autoScanModules = true;

    final Set<String> loadedComponents = new HashSet<String>();

    /*** Extensions  ***/
    if (config.hasProperty("errai.auto_discover_services")) {
      autoScanModules = Boolean.parseBoolean(config.getProperty("errai.auto_discover_services"));
    }
    if (autoScanModules) {

      log.info("beging searching for services ...");

      List<File> configRootTargets = ConfigUtil.findAllConfigTargets();

      visitAllTargets(configRootTargets,
          new ConfigVisitor() {
            public void visit(final Class<?> loadClass) {
              if (loadedComponents.contains(loadClass.getName())) return;


              if (Module.class.isAssignableFrom(loadClass)) {
                final Class<? extends Module> clazz = loadClass.asSubclass(Module.class);

                loadedComponents.add(loadClass.getName());

                if (clazz.isAnnotationPresent(LoadModule.class)) {
                  log.info("discovered module : " + clazz.getName() + " -- don't use Modules! Use @Service and MessageCallback!");
                  Guice.createInjector(new AbstractModule() {
                    @Override
                    protected void configure() {
                      bind(Module.class).to(clazz);
                      bind(MessageBus.class).toInstance(context.getBus());
                    }
                  }).getInstance(Module.class).init();
                }

              } else if (loadClass.isAnnotationPresent(Service.class)) {
                Object svc = null;

                Class remoteImpl = getRemoteImplementation(loadClass);
                if (remoteImpl != null) {
                  createRPCScaffolding(remoteImpl, loadClass, context);
                } else if (MessageCallback.class.isAssignableFrom(loadClass)) {
                  final Class<? extends MessageCallback> clazz = loadClass.asSubclass(MessageCallback.class);

                  loadedComponents.add(loadClass.getName());

                  log.info("discovered service: " + clazz.getName());
                  try {
                    svc = Guice.createInjector(new AbstractModule() {
                      @Override
                      protected void configure() {
                        bind(MessageCallback.class).to(clazz);
                        bind(MessageBus.class).toInstance(context.getBus());
                        bind(RequestDispatcher.class).toInstance(context.getService().getDispatcher());

                        // Add any extension bindings.
                        for (Map.Entry<Class<?>, ResourceProvider> entry : config.getExtensionBindings().entrySet()) {
                          bind(entry.getKey()).toProvider(new GuiceProviderProxy(entry.getValue()));
                        }
                      }
                    }).getInstance(MessageCallback.class);
                  }
                  catch (Throwable t) {
                    t.printStackTrace();
                  }


                  String svcName = clazz.getAnnotation(Service.class).value();

                  // If no name is specified, just use the class name as the service
                  // by default.
                  if ("".equals(svcName)) {
                    svcName = clazz.getSimpleName();
                  }

                  // Subscribe the service to the bus.
                  context.getBus().subscribe(svcName, (MessageCallback) svc);

                  RolesRequiredRule rule = null;
                  if (clazz.isAnnotationPresent(RequireRoles.class)) {
                    rule = new RolesRequiredRule(clazz.getAnnotation(RequireRoles.class).value(), context.getBus());
                  } else if (clazz.isAnnotationPresent(RequireAuthentication.class)) {
                    rule = new RolesRequiredRule(new HashSet<Object>(), context.getBus());
                  }
                  if (rule != null) {
                    context.getBus().addRule(svcName, rule);
                  }
                }

                if (svc == null) {
                  svc = Guice.createInjector(new AbstractModule() {
                    @Override
                    protected void configure() {
                      bind(MessageBus.class).toInstance(context.getBus());
                      bind(RequestDispatcher.class).toInstance(context.getService().getDispatcher());

                      // Add any extension bindings.
                      for (Map.Entry<Class<?>, ResourceProvider> entry : config.getExtensionBindings().entrySet()) {
                        bind(entry.getKey()).toProvider(new GuiceProviderProxy(entry.getValue()));
                      }
                    }
                  }).getInstance(loadClass);
                }

                Map<String, MessageCallback> epts = new HashMap<String, MessageCallback>();


                // we scan for endpoints
                for (final Method method : loadClass.getDeclaredMethods()) {
                  if (method.isAnnotationPresent(Endpoint.class)) {
                    epts.put(method.getName(), method.getReturnType() == Void.class ?
                        new EndpointCallback(svc, method) :
                        new ConversationalEndpointCallback(svc, method, context.getBus()));
                  }
                }

                if (!epts.isEmpty()) {
                  context.getBus().subscribe(loadClass.getSimpleName() + ":RPC", new RemoteServiceCallback(epts));
                }

              } else if (loadClass.isAnnotationPresent(ExposeEntity.class)) {
                log.info("Marked " + loadClass + " as serializable.");
                loadedComponents.add(loadClass.getName());
                config.getSerializableTypes().add(loadClass);
              }
            }
          }
      );
    } else {
      log.info("auto-discovery of services disabled.");
    }

    try {
      ResourceBundle bundle = ResourceBundle.getBundle("ErraiApp");
      if (bundle != null) {
        log.info("checking ErraiApp.properties for configured types ...");

        Enumeration<String> keys = bundle.getKeys();
        while(keys.hasMoreElements())
        {
          String key = keys.nextElement();
          if(key.equals(ErraiServiceConfigurator.CONFIG_ERRAI_SERIALIZABLE_TYPE))
          {
            for (String s : key.split(" ")) {
              try {
                Class<?> cls = Class.forName(s.trim());
                log.info("Marked " + cls + " as serializable.");
                loadedComponents.add(cls.getName());
                config.getSerializableTypes().add(cls);
              }
              catch (Exception e) {
                throw new ErraiBootstrapFailure(e);
              }

            }

            break;
          }
        }
      }
    }
    catch (MissingResourceException e) {
      throw new ErraiBootstrapFailure("unable to find ErraiApp.properties in the classpath.");
    }
  }

  private static Class getRemoteImplementation(Class type) {
    for (Class iface : type.getInterfaces()) {
      if (iface.isAnnotationPresent(Remote.class)) {
        return iface;
      } else if (iface.getInterfaces().length != 0 && ((iface = getRemoteImplementation(iface)) != null)) {
        return iface;
      }
    }
    return null;
  }

  private void createRPCScaffolding(final Class remoteIface, final Class<?> type, final BootstrapContext context) {

    final ErraiServiceConfiguratorImpl config = (ErraiServiceConfiguratorImpl)context.getConfig();
    final Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        bind(MessageBus.class).toInstance(context.getBus());
        bind(RequestDispatcher.class).toInstance(context.getService().getDispatcher());

        // Add any extension bindings.
        for (Map.Entry<Class<?>, ResourceProvider> entry : config.getExtensionBindings().entrySet()) {
          bind(entry.getKey()).toProvider(new GuiceProviderProxy(entry.getValue()));
        }
      }
    });

    Object svc = injector.getInstance(type);

    Map<String, MessageCallback> epts = new HashMap<String, MessageCallback>();

    // beware of classloading issues. better reflect on the actual instance
    for (Class<?> intf : svc.getClass().getInterfaces()) {
      for (final Method method : intf.getDeclaredMethods()) {
        if (RebindUtils.isMethodInInterface(remoteIface, method)) {
          epts.put(RebindUtils.createCallSignature(method), new ConversationalEndpointCallback(svc, method, context.getBus()));
        }
      }
    }

    context.getBus().subscribe(remoteIface.getName() + ":RPC", new RemoteServiceCallback(epts));

    new ProxyProvider() {
      {
        AbstractRemoteCallBuilder.setProxyFactory(this);
      }

      public <T> T getRemoteProxy(Class<T> proxyType) {
        throw new RuntimeException("This API is not supported in the server-side environment.");
      }
    };

  }

}
TOP

Related Classes of org.jboss.errai.bus.server.service.bootstrap.DiscoverServices

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.