• 企业搜索引擎开发之连接器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方法启动实例化对象和定时调度对象,这两个对象的类源码及功能待下文分析吧

  • 相关阅读:
    Git输出格式——Git placeholders
    Unix的缺陷_王垠_新浪博客
    关于文件流的模拟上传——人人网首页拖拽上传详解(HTML5 Drag&Drop、FileReader API、formdata) | 彬Go
    perl正则表达式中的常用模式
    FileReader详解与实例读取并显示图像文件 | JS Mix
    Gitweb源码解析
    用ATL的W2A和A2W宏转换Unicode与ANSI字符串
    JavaScript正则表达式
    显示访客的浏览器类型
    显示屏幕分辨率
  • 原文地址:https://www.cnblogs.com/chenying99/p/2964136.html
Copyright © 2020-2023  润新知