zoukankan      html  css  js  c++  java
  • 企业搜索引擎开发之连接器connector(三)

    由于一直忙于公司产品的开发工作,企业搜索引擎的连接器分析及constellio源码分析因此长时间没有更新,本次继续这项未竟事业

    连接器管理都是通过servlet对外提供通信接口,由此从连接器管理提供的servlet着手,寻求分析连接器的入口

    该系统提供的servlet的uml模型图如下

     

    这些servlet主要提供连接器的管理,诸如连接器的配置、修改、移除、重启、设置定时调度等功能;其中StartUp跟随应用同时启动,负责初始化一些bean及相关参数等,其源码如下:

    /**
     * The main purpose of this servlet is to have its "init" method called when the
     * container starts up. This is by done by means of the web.xml file. But I also
     * gave it a get and post that do the same thing.
     *
     */
    public class StartUp extends HttpServlet {
      private static final Logger LOGGER =
          Logger.getLogger(StartUp.class.getName());
    
      @Override
      public void init() {
        NDC.push("Init");
        try {
          LOGGER.info("init");
          ServletContext servletContext = this.getServletContext();
          doConnectorManagerStartup(servletContext);
          LOGGER.info("init done");
        } finally {
          NDC.remove();
        }
      }
    
      @Override
      public void destroy() {
        NDC.push("Shutdown");
        try {
          LOGGER.info("destroy");
          Context.getInstance().shutdown(true);
        } finally {
          NDC.remove();
        }
      }
    
      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse res)
          throws IOException {
        doPost(req, res);
      }
    
      @Override
      protected void doPost(HttpServletRequest req, HttpServletResponse res)
          throws IOException {
        ServletContext servletContext = this.getServletContext();
        doConnectorManagerStartup(servletContext);
        res.setContentType(ServletUtil.MIMETYPE_HTML);
        PrintWriter out = res.getWriter();
        out.println("<HTML><HEAD><TITLE>Connector Manager Started</TITLE></HEAD>"
            + "<BODY>Connector manager has been successfully started.</BODY>"
            + "</HTML>");
        out.close();
        LOGGER.info("Connector Manager started.");
      }
    
      private void doConnectorManagerStartup(ServletContext servletContext) {
        LOGGER.info(ServletUtil.getManagerSplash());
    
        // read in and set initialization parameters
        String kp = servletContext.getInitParameter("keystore_passwd_file");
        EncryptedPropertyPlaceholderConfigurer.setKeyStorePasswdPath(kp);
    
        String ks = servletContext.getInitParameter("keystore_file");
        String realks = servletContext.getRealPath("/WEB-INF/" + ks);
        if (null == realks) {
          // Servlet container cannot translated the virtual path to a
          // real path, so use the given path.
          EncryptedPropertyPlaceholderConfigurer.setKeyStorePath(ks);
        } else {
          EncryptedPropertyPlaceholderConfigurer.setKeyStorePath(realks);
        }
    
        String kt = servletContext.getInitParameter("keystore_type");
        EncryptedPropertyPlaceholderConfigurer.setKeyStoreType(kt);
    
        String ka = servletContext.getInitParameter("keystore_crypto_algo");
        EncryptedPropertyPlaceholderConfigurer.setKeyStoreCryptoAlgo(ka);
    
        // Note: default context location is /WEB-INF/applicationContext.xml
        LOGGER.info("Making an XmlWebApplicationContext");
        XmlWebApplicationContext ac = new XmlWebApplicationContext();
        ac.setServletContext(servletContext);
        ac.refresh();
    
        Context context = Context.getInstance();
        context.setServletContext(ac, servletContext.getRealPath("/WEB-INF"));
        context.start();
      }
    }

     这里的关键方法是void doConnectorManagerStartup(ServletContext servletContext)

    前面部分用于证书生成,后面部分是获取spring容器并初始化Context上下文对象

    接下来分析Context的源码:

    /**
     * Static services for establishing the application context. This consists of
     * configuration, instantiating singletons, start up, etc.
     * This code supports two context types: servlet (as a web application within
     * an application server) and standalone.
     * When we run junit tests, we use a standalone context.
     * Use the methods setStandaloneContext and setServletContext to select the
     * context type.
     * <p>
     * Also the interface used for event publishing.  Wraps the event publishing
     * functionality of the established context.
     */
    public class Context {
    
      public static final String GSA_FEED_HOST_PROPERTY_KEY = "gsa.feed.host";
      public static final String GSA_FEED_PORT_PROPERTY_KEY = "gsa.feed.port";
      public static final String GSA_FEED_PORT_DEFAULT = "19900";
    
      public static final String GSA_ADMIN_REQUIRES_PREFIX_KEY =
          "gsa.admin.requiresPrefix";
      public static final String TEED_FEED_FILE_PROPERTY_KEY = "teedFeedFile";
      public static final String MANAGER_LOCKED_PROPERTY_KEY = "manager.locked";
    
      public static final Boolean GSA_ADMIN_REQUIRES_PREFIX_DEFAULT =
          Boolean.FALSE;
    
      public static final String DEFAULT_JUNIT_CONTEXT_LOCATION =
          "testdata/mocktestdata/applicationContext.xml";
      public static final String DEFAULT_JUNIT_COMMON_DIR_PATH =
          "testdata/mocktestdata/";
    
      /**
       * Id of the Spring Bean used to declare the order services are to be loaded.
       */
      public static final String ORDERED_SERVICES_BEAN_NAME = "OrderedServices";
      private static final String APPLICATION_CONTEXT_PROPERTIES_BEAN_NAME =
          "ApplicationContextProperties";
    
      // This is the comment written to the ApplicationContextProperties file.
      private static final String CONNECTOR_MANGER_CONFIG_HEADER =
          " Google Search Appliance Connector Manager Configuration\n"
          + "\n"
          + " The 'gsa.feed.host' property specifies the host name or IP address\n"
          + " for the feed host on the GSA.\n"
          + " For example:\n"
          + "   gsa.feed.host=172.24.2.0\n"
          + "\n"
          + " The 'gsa.feed.port' property specifies the host port for the feed\n"
          + " host on the GSA.\n"
          + " For example:\n"
          + "   gsa.feed.port=19900\n"
          + "\n"
          + " The 'manager.locked' property is used to lock out the Admin Servlet\n"
          + " and prevent it from making changes to this configuration file.\n"
          + " Specifically, the ability to set the FeedConnection properties will\n"
          + " be locked out.  If it is set to 'true' or missing the Servlet will\n"
          + " not be allowed to update this file.\n"
          + " NOTE: This property will automatically be changed to 'true' upon\n"
          + " successful update of the file by the Servlet.  Therefore, once the\n"
          + " FeedConnection properties are successfully updated by the Servlet\n"
          + " subsequent updates will be locked out until the flag is manually\n"
          + " reset to 'false'.\n"
          + " For example:\n"
          + "   manager.locked=false\n"
          + "\n"
          + " The 'feedLoggingLevel' property controls the logging of the feed\n"
          + " record to a log file.  The log record will contain the feed XML\n"
          + " without the content data.  Set this property to 'ALL' to enable feed\n"
          + " logging, 'OFF' to disable.  Customers and developers can use this\n"
          + " functionality to observe the feed record and metadata information\n"
          + " the connector manager sends to the GSA.\n"
          + " For example:\n"
          + "   feedLoggingLevel=OFF\n"
          + "\n"
          + " If you set the 'teedFeedFile' property to the name of an existing\n"
          + " file, whenever the connector manager feeds content to the GSA, it\n"
          + " will write a duplicate copy of the feed XML to the file specified by\n"
          + " the teedFeedFile property.  GSA customers and third-party developers\n"
          + " can use this functionality to observe the content the connector\n"
          + " manager sends to the GSA and reproduce any issue which may arise.\n"
          + " NOTE: The teedFeedFile will contain all feed data sent to the GSA,\n"
          + " including document content and metadata.  The teedFeedFile can\n"
          + " therefore grow quite large very quickly.\n"
          + " For example:\n"
          + "   teedFeedFile=/tmp/CMTeedFeedFile"
          + "\n"
          + " The 'feed.timezone' property defines the default time zone used\n"
          + " for Date metadata values for Documents.  A null or empty string\n"
          + " indicates that the system timezone of the machine running the\n"
          + " Connector Manager should be used.  Standard TimeZone identifiers\n"
          + " may be used.  For example:\n"
          + "   feed.timezone=America/Los_Angeles\n"
          + " If a standard TimeZone identifier is unavailable, then a custom\n"
          + " TimeZone identifier can be constructed as +/-hours[minutes] offset\n"
          + " from GMT.  For example:\n"
          + "   feed.timezone=GMT+10    # GMT + 10 hours\n"
          + "   feed.timezone=GMT+0630  # GMT + 6 hours, 30 minutes\n"
          + "   feed.timezone=GMT-0800  # GMT - 8 hours, 0 minutes\n"
          + "\n"
          + " The 'feed.file.size' property sets the target size, in bytes, of\n"
          + " an accumulated feed file. The Connector Manager tries to collect\n"
          + " many feed Documents into a single feed file to improve the\n"
          + " efficiency of sending feed data to the GSA.  Specifying too small\n"
          + " a value may result in many small feeds which might overrun the\n"
          + " GSA's feed processor.  However, specifying too large a feed size\n"
          + " reduces concurrency and may result in OutOfMemory errors in the\n"
          + " Java VM, especially if using multiple Connector Instances.\n"
          + " The default target feed size is 10MB.\n"
          + " For example:\n"
          + "   feed.file.size=10485760\n"
          + "\n"
          + " The 'feed.document.size.limit' property defines the maximum\n"
          + " allowed size, in bytes, of a Document's content.  Documents whose\n"
          + " content exceeds this size will still have metadata indexed,\n"
          + " however the content itself will not be fed.  The default value\n"
          + " is 30MB, the maximum file size accepted by the GSA.\n"
          + " For example:\n"
          + "   feed.document.size.limit=31457280\n"
          + "\n"
          + " The 'feed.backlog.*' properties are used to throttle back the\n"
          + " document feed if the GSA has fallen behind processing outstanding\n"
          + " feed items.  The Connector Manager periodically polls the GSA,\n"
          + " fetching the count of unprocessed feed items (the backlog count).\n"
          + " If the backlog count exceeds the ceiling value, feeding is paused.\n"
          + " Once the backlog count drops down below the floor value, feeding\n"
          + " resumes.\n  For example:\n"
          + " Stop feeding the GSA if its backlog exceeds this value.\n"
          + "   feed.backlog.ceiling=10000\n"
          + " Resume feeding the GSA if its backlog falls below this value.\n"
          + "   feed.backlog.floor=1000\n"
          + " How often to check for feed backlog (in seconds).\n"
          + "   feed.backlog.interval=900\n"
          + "\n"
          + " The 'traversal.batch.size' property defines the optimal number\n"
          + " of items to return in each repository traversal batch.  The batch\n"
          + " size represents the size of the roll-back that occurs during a\n"
          + " failure condition.  Batch sizes that are too small may incur\n"
          + " excessive processing overhead.  Batch sizes that are too large\n"
          + " may produce OutOfMemory conditions within a Connector or result\n"
          + " in early termination of the batch if processing time exceeds the\n"
          + " travesal.time.limit.   For example:\n"
          + "    traversal.batch.size=500\n"
          + "\n"
          + " The 'traversal.poll.interval' property defines the number of\n"
          + " seconds to wait after a traversal of the repository finds no new\n"
          + " content before looking again.  Short intervals allow new content\n"
          + " to be readily available for search, at the cost of increased\n"
          + " repository access.  Long intervals add latency before new\n"
          + " content becomes available for search.  By default, the Connector\n"
          + " Manager waits 5 minutes (300 seconds) before retraversing the\n"
          + " repository if no new content was found on the last traversal.\n"
          + " For example:\n"
          + "   traversal.poll.interval=300\n"
          + "\n"
          + " The 'traversal.time.limit' property defines the number of\n"
          + " seconds a traversal batch should run before gracefully exiting.\n"
          + " Traversals that exceed this time period risk cancelation.\n"
          + " The default time limit is 30 minutes (1800 seconds).\n"
          + " For example:\n"
          + "   traversal.time.limit=1800\n"
          + "\n"
          + " The 'traversal.enabled' property is used to enable or disable\n"
          + " Traversals and Feeds for all connector instances in this\n"
          + " Connector Manager.  Disabling Traversal would be desirable if\n"
          + " configuring a Connector Manager deployment that only authorizes\n"
          + " search results.  Traversals are enabled by default.\n"
          + " traversal.enabled=false\n"
          + "\n";
    
      private static final Logger LOGGER =
          Logger.getLogger(Context.class.getName());
    
      private static final GenericApplicationContext genericApplicationContext =
          new GenericApplicationContext();
    
      private static Context INSTANCE = new Context();
    
      private boolean started = false;
    
      private boolean isServletContext = false;
    
      private boolean isFeeding = true;
    
      private String commonDirPath = null;
    
      // singletons
      private Manager manager = null;
      private TraversalScheduler traversalScheduler = null;
      private TraversalContext traversalContext = null;
      private SpringInstantiator instantiator = null;
    
      // control variables for turning off normal functionality - testing only
      private String standaloneContextLocation;
    
    
      private Boolean gsaAdminRequiresPrefix = null;
    
      private boolean isTeedFeedFileInitialized = false;
      private String teedFeedFile = null;
    
      private boolean isGsaFeedHostInitialized = false;
      private String gsaFeedHost = null;
    
      private int propertiesVersion = 0;
    
      /**
       * @param feeding to feed or not to feed
       */
      public void setFeeding(boolean feeding) {
        LOGGER.config("Traversal and Feeds are "
            + ((feeding) ? "enabled." : "disabled."));
        this.isFeeding = feeding;
      }
    
      public static Context getInstance() {
        return INSTANCE;
      }
    
      ApplicationContext applicationContext = null;
    
      private Context() {
        // Private to ensure singleton.
      }
    
      private void initializeStandaloneApplicationContext() {
        if (applicationContext != null) {
          // too late - someone else already established a context. this might
          // happen with multiple junit tests that each want to establish a context.
          // so long as they use the same context location, it's ok. if they want a
          // different context location, they should refresh() - see below
          return;
        }
    
        applicationContext = genericApplicationContext; // avoid recursion
    
        if (standaloneContextLocation == null) {
          standaloneContextLocation = DEFAULT_JUNIT_CONTEXT_LOCATION;
        }
    
        if (commonDirPath == null) {
          commonDirPath = DEFAULT_JUNIT_COMMON_DIR_PATH;
        }
        LOGGER.info("context file: " + standaloneContextLocation);
        LOGGER.info("common dir path: " + commonDirPath);
    
        applicationContext =
            new FileSystemXmlApplicationContext(standaloneContextLocation);
      }
    
      /**
       * Establishes that we are operating within the standalone context. In
       * this case, we use a FileSystemApplicationContext.
       * @param contextLocation the name of the context XML file used for
       * instantiation.
       * @param commonDirPath the location of the common directory which contains
       * ConnectorType and Connector instantiation configuration data.
       */
      public void setStandaloneContext(String contextLocation,
                                       String commonDirPath) {
        this.standaloneContextLocation = contextLocation;
        this.commonDirPath = commonDirPath;
        initializeStandaloneApplicationContext();
      }
    
      /**
       * Establishes that we are operating from a servlet context. In this case, we
       * use an XmlWebApplicationContext, which finds its config from the servlet
       * context - WEB-INF/applicationContext.xml.
       */
      public void setServletContext(ApplicationContext servletApplicationContext,
                                    String commonDirPath) {
        this.applicationContext = servletApplicationContext;
        this.commonDirPath = commonDirPath;
        isServletContext = true;
      }
    
      /*
       * Choose a default context, if it wasn't specified in any other way. For now,
       * we choose servlet context by default.
       */
      private void initApplicationContext() {
        if (applicationContext == null) {
          initializeStandaloneApplicationContext();
        }
        if (applicationContext == null) {
          throw new IllegalStateException("Spring failure - no application context");
        }
      }
    
      /**
       * Start up the Scheduler.
       */
      private void startScheduler() {
        traversalScheduler =
            (TraversalScheduler) getRequiredBean("TraversalScheduler",
                TraversalScheduler.class);
        if (traversalScheduler != null) {
          traversalScheduler.init();
        }
      }
    
      /**
       * Start up the Instantiator.
       */
      private void startInstantiator() {
        instantiator =
            (SpringInstantiator) getBean("Instantiator", SpringInstantiator.class);
        if (instantiator != null) {
          instantiator.init();
        }
      }
    
      /**
       * Do everything necessary to start up the application.
       */
      public void start() {
        if (started) {
          return;
        }
        initApplicationContext();
        startInstantiator();
        if (isFeeding) {
          startScheduler();
        }
        startServices();
        started = true;
      }
    
      /**
       * Starts any services declared as part of the application.
       */
      private void startServices() {
        for (ContextService service : getServices()) {
          service.start();
        }
      }
    
      /**
       * Gets a service by name.  Returns a matching bean if found or null
       * otherwise.
       *
       * @param serviceName the name of the service to find.
       * @return if there is a single bean with the given name it will be returned.
       *         If there are multiple beans with the same name, the first one found
       *         will be returned.  If there are no beans with the given name, null
       *         will be returned.
       */
      public ContextService findService(String serviceName) {
        return (ContextService) getBean(serviceName, null);
      }
    
      /**
       * Returns an ordered list of services attached to the context.  Collection is
       * ordered according to the startup order of the services.
       * <p>
       * To get the list in reverse order use {@link Collections#reverse(List)}.
       *
       * @return an ordered list of ContextService objects.  If no services are
       *         registered an empty list will be returned.
       */
      public List<ContextService> getServices() {
        // TODO: Investigate the use of the GenericBeanFactoryAccessor here.
        Map<?, ?> orderedServices = (Map<?, ?>)
            getBean(ORDERED_SERVICES_BEAN_NAME, null);
        Map<?, ?> services = applicationContext.getBeansOfType(ContextService.class);
        List<ContextService> result = new ArrayList<ContextService>();
    
        if (orderedServices != null) {
          for (Iterator<?> iter = orderedServices.keySet().iterator();
              iter.hasNext(); ) {
            ContextService service =
                (ContextService) orderedServices.get(iter.next());
            result.add(service);
          }
        }
        for (Iterator<?> iter = services.values().iterator(); iter.hasNext(); ) {
            ContextService service = (ContextService) iter.next();
          if (!result.contains(service)) {
            result.add(service);
          }
        }
    
        return result;
      }
    
      /**
       * Get a bean from the application context that we MUST have to operate.
       *
       * @param beanName the name of the bean we're looking for. Typically, the same
       *        as its most general interface.
       * @param clazz the class of the bean we're looking for.
       * @return if there is a single bean of the required type, we return it,
       *         regardless of name. If there are multiple beans of the required
       *         type, we return the one with the required name, if present, or the
       *         first one we find, if there is none of the right name.
       * @throws IllegalStateException if there are no beans of the right type, or
       *         if there is an instantiation problem.
       */
      public Object getRequiredBean(String beanName, Class<?> clazz) {
        try {
          Object object = getBean(beanName, clazz);
          if (object != null) {
            return object;
          }
          throw new IllegalStateException("The context has no " + beanName);
        } catch (BeansException e) {
          throw new IllegalStateException("Spring failure - can't instantiate "
              + beanName + ": (" + e.toString() + ")");
        }
      }
    
      /**
       * Get an optional bean from the application context.
       *
       * @param beanName the name of the bean we're looking for. Typically,
       *        the same as its most general interface.
       * @param clazz the class of the bean we're looking for.
       * @return if there is a single bean of the required type, we return it,
       *         regardless of name. If there are multiple beans of the required
       *         type, we return the one with the required name, if present, or the
       *         first one we find, if there is none of the right name.  Returns
       *         null if no bean of the appropriate name or type is found.
       * @throws BeansException if there is an instantiation problem.
       */
      public Object getBean(String beanName, Class<?> clazz)
          throws BeansException {
        initApplicationContext();
        return getBean(applicationContext, beanName, clazz);
      }
    
      /**
       * Get a bean from the supplied BeanFactory.  First look for a bean with
       * the given name and type.  If none is found, look for the first bean
       * of the specified type.
       *
       * @param factory a ListableBeanFactory
       * @param beanName the name of the bean we're looking for. Typically, the same
       *        as its most general interface.  If null, return the first bean
       *        of the requested type.
       * @param clazz the class of the bean we're looking for.  If null, return
       *        any bean of the specified name.
       * @return if there is a single bean of the required type, we return it,
       *         regardless of name. If there are multiple beans of the required
       *         type, we return the one with the required name, if present, or the
       *         first one we find, if there is none of the right name.  Returns
       *         null if no bean of the appropriate name or type is found.
       * @throws BeansException if there is an instantiation problem.
       */
      public Object getBean(ListableBeanFactory factory, String beanName,
          Class<?> clazz) throws BeansException {
        Object result = null;
    
        // First, look for a bean with the specified name and type.
        try {
          if (beanName != null && beanName.length() > 0) {
            result = factory.getBean(beanName, clazz);
            if (result != null) {
              return result;
            }
          }
        } catch (NoSuchBeanDefinitionException e) {
          // Not a problem yet.  Look for any bean of the appropriate type.
        }
    
        // If no bean type was specified, we are done.
        if (clazz == null) {
          return null;
        }
    
        // Get the list of beans defined in the bean factory of the required type.
        String[] beanList = factory.getBeanNamesForType(clazz);
    
        // Make sure there is at least one
        if (beanList.length < 1) {
          return null;
        }
    
        // If more beans were found issue a warning.
        if (beanList.length > 1) {
          StringBuilder buf = new StringBuilder();
          for (int i = 1; i < beanList.length; i++) {
            buf.append(" ");
            buf.append(beanList[i]);
          }
          LOGGER.warning("Resource contains multiple " + clazz.getName() +
              " definitions. Using the first: " + beanList[0] +
              ". Skipping: " + buf);
        }
    
        return factory.getBean(beanList[0]);
      }
    
      /**
       * Gets the singleton Manager.
       *
       * @return the Manager
       */
      public Manager getManager() {
        if (manager != null) {
          return manager;
        }
        manager = (Manager) getRequiredBean("Manager", Manager.class);
        return manager;
      }
    
      /**
       * Gets the singleton TraversalContext.
       *
       * @return the TraversalContext
       */
      public TraversalContext getTraversalContext() {
        if (traversalContext != null) {
          return traversalContext;
        }
        try {
          traversalContext = (TraversalContext) getRequiredBean("TraversalContext",
              TraversalContext.class);
        } catch (IllegalStateException e) {
          LOGGER.warning("Can't find suitable " + TraversalContext.class.getName()
              + " bean in context, using default.");
          traversalContext = new ProductionTraversalContext();
        }
        return traversalContext;
      }
    
      /**
       * Throws out the current context instance and gets another one. For testing
       * only. This could really boolux things up if it were used in production!
       *
       */
      public static void refresh() {
        INSTANCE = new Context();
      }
    
      /**
       * Gets the applicationContext. For testing only.
       *
       * @return the applicationContext
       */
      public ApplicationContext getApplicationContext() {
        initApplicationContext();
        return applicationContext;
      }
    
      public synchronized void shutdown(boolean force) {
        LOGGER.info("Shutdown initiated...");
        stopServices(force);
        if (null != traversalScheduler) {
          traversalScheduler.shutdown();
          traversalScheduler = null;
        }
        if (null != instantiator) {
          instantiator.shutdown(force,
              ThreadPool.DEFAULT_SHUTDOWN_TIMEOUT_MILLIS);
          instantiator = null;
        }
        started = false;
      }
    
      /**
       * Stops any services declared as part of the application.
       */
      private void stopServices(boolean force) {
        initApplicationContext();
        List<ContextService> services = getServices();
        Collections.reverse(services);
        for (ContextService service : services) {
          service.stop(force);
        }
      }
    
      /**
       * Retrieves the prefix for the Common directory file depending on whether
       * it is a standalone context or servlet context.
       *
       * @return prefix for the Repository file.
       */
      public String getCommonDirPath() {
        initApplicationContext();
        return commonDirPath;
      }
    
      private String getPropFileName() throws InstantiatorException {
        String propFileName = null;
        try {
          propFileName =
              (String) applicationContext.getBean(
                  APPLICATION_CONTEXT_PROPERTIES_BEAN_NAME, java.lang.String.class);
        } catch (BeansException e) {
          throw new InstantiatorException("Spring exception while getting "
              + APPLICATION_CONTEXT_PROPERTIES_BEAN_NAME + " bean", e);
        }
        if (propFileName == null || propFileName.length() < 1) {
          throw new InstantiatorException("Null or empty file name returned from "
              + "Spring while getting " + APPLICATION_CONTEXT_PROPERTIES_BEAN_NAME
              + " bean");
        }
        return propFileName;
      }
    
      private File getPropFile(String propFileName) throws InstantiatorException {
        Resource propResource = applicationContext.getResource(propFileName);
        File propFile;
        try {
          propFile = propResource.getFile();
        } catch (IOException e) {
          throw new InstantiatorException(e);
        }
        return propFile;
      }
    
      public Properties getConnectorManagerConfig() throws InstantiatorException {
        initApplicationContext();
        Properties result = new Properties();
        // Get the properties out of the CM properties file if present.
        String propFileName = getPropFileName();
        File propFile = getPropFile(propFileName);
        try {
          Properties props = PropertiesUtils.loadFromFile(propFile);
          result.setProperty(GSA_FEED_HOST_PROPERTY_KEY,
              props.getProperty(GSA_FEED_HOST_PROPERTY_KEY));
          result.setProperty(GSA_FEED_PORT_PROPERTY_KEY,
              props.getProperty(GSA_FEED_PORT_PROPERTY_KEY, GSA_FEED_PORT_DEFAULT));
        } catch (PropertiesException e) {
          LOGGER.log(Level.WARNING, "Unable to read application context properties"
              + " file " + propFileName,
              e);
        }
        return result;
      }
    
      public void setConnectorManagerConfig(String feederGateHost,
          int feederGatePort) throws InstantiatorException {
        initApplicationContext();
    
        // Update the feed host and port in the CM properties file.
        String propFileName = getPropFileName();
        File propFile = getPropFile(propFileName);
        Properties props;
        try {
          props = PropertiesUtils.loadFromFile(propFile);
        } catch (PropertiesException e) {
          LOGGER.log(Level.WARNING, "Unable to read application context properties"
              + " file "+ propFileName + "; attempting instantiation stand-alone.",
              e);
          props = new Properties();
        }
        props.put(GSA_FEED_HOST_PROPERTY_KEY, feederGateHost);
        props.put(GSA_FEED_PORT_PROPERTY_KEY, Integer.toString(feederGatePort));
        // Lock down the manager at this point.
        props.put(MANAGER_LOCKED_PROPERTY_KEY, Boolean.TRUE.toString());
        try {
          PropertiesUtils.storeToFile(props, propFile,
              CONNECTOR_MANGER_CONFIG_HEADER);
        } catch (PropertiesException e) {
          LOGGER.log(Level.WARNING, "Unable to save application context properties"
              + " file " + propFileName + ". ", e);
          throw new InstantiatorException(e);
        }
        LOGGER.info("Updated Connector Manager Config: "
            + GSA_FEED_HOST_PROPERTY_KEY + "=" + feederGateHost + "; "
            + GSA_FEED_PORT_PROPERTY_KEY + "=" + feederGatePort + ";"
            + MANAGER_LOCKED_PROPERTY_KEY + "="
            + props.getProperty(MANAGER_LOCKED_PROPERTY_KEY));
    
        // Update our local cached feed host.
        gsaFeedHost = feederGateHost;
        isGsaFeedHostInitialized = true;
    
        // Notify the GsaFeedConnection of new host and port.
        try {
          GsaFeedConnection feeder = (GsaFeedConnection)
            applicationContext.getBean("FeedConnection", GsaFeedConnection.class);
          feeder.setFeedHostAndPort(feederGateHost, feederGatePort);
        } catch (BeansException be) {
          // The configured FeedConnection isn't a GSA, so it doesn't care
          // about the GSA host and port.
        } catch (MalformedURLException e) {
          throw new InstantiatorException("Invalid GSA Feed specification", e);
        }
      }
    
      /**
       * Whether or not the GSA requires the connector manager to prepend a
       * connector-manager-specific prefix to connector configuration
       * property names.  Older GSA require the prefix, and newer GSAs do not.
       * This value is read from the <code>gsa.admin.requiresPrefix</code>
       * property in the application context properties file.
       * If the <code>gsa.admin.requiresPrefix</code> property is not defined, the
       * default value is <code>false</code>.
       */
      public boolean gsaAdminRequiresPrefix() {
        initApplicationContext();
        if (gsaAdminRequiresPrefix == null) {
            String prop = getProperty(
                GSA_ADMIN_REQUIRES_PREFIX_KEY,
                GSA_ADMIN_REQUIRES_PREFIX_DEFAULT.toString());
            gsaAdminRequiresPrefix = Boolean.valueOf(prop);
        }
        return gsaAdminRequiresPrefix;
      }
    
      /**
       * Reads <code>teedFeedFile</code> from the application context properties file.
       * See google-enterprise-connector-manager/projects/connector-manager/etc/applicationContext.properties
       * for additional documentation.
       */
      public String getTeedFeedFile() {
        initApplicationContext();
        if (!isTeedFeedFileInitialized) {
          teedFeedFile = getProperty(TEED_FEED_FILE_PROPERTY_KEY, null);
          isTeedFeedFileInitialized = true;
        }
        return teedFeedFile;
      }
    
      /**
       * Reads <code>gsa.feed.host</code> from the application context properties file.
       * See google-enterprise-connector-manager/projects/connector-manager/etc/applicationContext.properties
       * for additional documentation.
       */
      public String getGsaFeedHost() {
        initApplicationContext();
        if (!isGsaFeedHostInitialized) {
          gsaFeedHost = getProperty(GSA_FEED_HOST_PROPERTY_KEY, null);
          isGsaFeedHostInitialized = true;
        }
        return gsaFeedHost;
      }
    
      /**
       * Reads <code>manager.locked</code> property from the application context
       * properties file.
       *
       * @return true if the property does not exist.  Returns true if the property
       *         is set to 'true', ignoring case.  Returns false otherwise.
       */
      public boolean getIsManagerLocked() {
        initApplicationContext();
        String isManagerLocked = getProperty(MANAGER_LOCKED_PROPERTY_KEY, null);
        if (isManagerLocked != null) {
          return Boolean.valueOf(isManagerLocked).booleanValue();
        }
        // Consider older, but uninitialized properties files to be unlocked.
        if (propertiesVersion < 2 && "localhost".equals(getGsaFeedHost())) {
          return false;
        }
        return true;
      }
    
      /**
       * Reads a property from the application context properties file.
       *
       * @param key the property name
       * @param defaultValue if property does not exist
       */
      private String getProperty(String key, String defaultValue) {
        try {
          String propFileName = getPropFileName();
          File propFile = getPropFile(propFileName);
          try {
            Properties props = PropertiesUtils.loadFromFile(propFile);
            propertiesVersion = PropertiesUtils.getPropertiesVersion(props);
            return props.getProperty(key, defaultValue);
          } catch (PropertiesException e) {
            LOGGER.log(Level.WARNING, "Unable to read application context "
                       + "properties file " + propFileName, e);
          }
        } catch (InstantiatorException ie) {
          LOGGER.log(Level.WARNING, "Unable to read application context "
                     + "properties file.", ie);
        }
        return defaultValue;
      }
    
      /**
       * Notify all listeners registered with this context of an application event.
       * Events may be framework events or application-specific events.
       *
       * @param event the event to publish.
       */
      public void publishEvent(ApplicationEvent event) {
        initApplicationContext();
        applicationContext.publishEvent(event);
      }
    }

     这里可以看到,Context上下文对象支持两种方式初始化,作为web应用方式(部署在应用服务器内)和以独立方式

    如果以独立方式初始化,则我们可以如下调用:

    public static void main(String[] args) throws ConnectorNotFoundException, InstantiatorException {
            // TODO Auto-generated method stub
            Context.refresh();         
            Context.getInstance().setStandaloneContext("/WebRoot/WEB-INF/applicationContext.xml", "D:/googlework/app");
            Context.getInstance().setFeeding(false);
            Context.getInstance().start();        
            
            SpringInstantiator instantiator = (SpringInstantiator) Context.getInstance().getBean("Instantiator", SpringInstantiator.class);
            //instantiator.init();
            //Manager manager=Context.getInstance().getManager();
            for( String str:instantiator.getConnectorTypeNames())
            {
               System.out.println("ConnectorTypeName:"+str);
            }
            for( String str:instantiator.getConnectorNames())
            {
               System.out.println("ConnectorName:"+str);
            }
           
            ConfigureResponse cr = instantiator.getConfigFormForConnector("default_huilan_http-connector_1363539625968", "http-connector", Locale.ENGLISH);
            System.out.println(cr.getFormSnippet());        
                    
            Map<String, String> map =instantiator.getConnectorConfig("default_huilan_http-connector_1363539625968");
            Iterator<String> it=map.keySet().iterator();
           while(it.hasNext()){
               String key= (String) it.next();
               System.out.println("key:"+key+"||value:"+map.get(key));
           }        
        
            System.out.println("ConnectorSchedule:"+instantiator.getConnectorSchedule("default_huilan_http-connector_1363539625968"));        
        
            ConnectorCoordinator coordinator = instantiator.getConnectorCoordinator("default_huilan_http-connector_1363539625968");
                   
            Context.getInstance().shutdown(true);
            Context.refresh();
        }

    需要注意的是以独立方式初始化需要修改applicationContext.xml文件里面applicationContext.properties的路径,并且在applicationContext.properties文件里面指定catalina.base变量(如果是tomcat容器,默认为tomcat的安装路径,如我的是C:/apache-tomcat-7.0.33)

    Context上下文对象对外提供spring容器里面的bean,并负责初始化相关bean,它的启动方法如下

    /**
       * Do everything necessary to start up the application.
       */
      public void start() {
        if (started) {
          return;
        }
        initApplicationContext();
        startInstantiator();
        if (isFeeding) {
          startScheduler();
        }
        startServices();
        started = true;
      }
    /**
       * Start up the Instantiator.
       */
      private void startInstantiator() {
        instantiator =
            (SpringInstantiator) getBean("Instantiator", SpringInstantiator.class);
        if (instantiator != null) {
          instantiator.init();
        }
      }
     /**
       * Start up the Scheduler.
       */
      private void startScheduler() {
        traversalScheduler =
            (TraversalScheduler) getRequiredBean("TraversalScheduler",
                TraversalScheduler.class);
        if (traversalScheduler != null) {
          traversalScheduler.init();
        }
      }

    这里的start方法启动实例化对象和定时调度对象,这两个对象的类源码及功能待下文分析吧

  • 相关阅读:
    LeetCode OJ 107. Binary Tree Level Order Traversal II
    LeetCode OJ 116. Populating Next Right Pointers in Each Node
    LeetCode OJ 108. Convert Sorted Array to Binary Search Tree
    LeetCode OJ 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode OJ 98. Validate Binary Search Tree
    老程序员解Bug的通用套路
    转载 四年努力,梦归阿里,和大家聊聊成长感悟
    转载面试感悟----一名3年工作经验的程序员应该具备的技能
    Web Service和Servlet的区别
    关于spring xml文件中的xmlns,xsi:schemaLocation
  • 原文地址:https://www.cnblogs.com/chenying99/p/2964136.html
Copyright © 2011-2022 走看看