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

com.cyc.session.CycServerPool Maven / Gradle / Ivy

package com.cyc.session;



/*
 * #%L
 * File: CycServerPool.java
 * Project: Session Client
 * %%
 * Copyright (C) 2013 - 2017 Cycorp, 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.
 * #L%
 */

import com.cyc.session.exception.SessionCommunicationException;
import com.cyc.session.exception.SessionConfigurationException;
import com.cyc.session.exception.SessionInitializationException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 */
public class CycServerPool {

  // Factory methods
  
  /**
   * Creates a CycServerPool for a set of CycAddresses, with a default concurrency level for each if
   * unspecified in the original addresses. This method will not modify any of the original
   * CycAddresses.
   *
   * @param addresses               iterable group of CycAddresses from which to create the pool
   * @param defaultConcurrencyLevel the default concurrency level for each CycAddress, if
   *                                unspecified
   *
   * @return a new CycServerPool
   */
  public static CycServerPool createFromServersWithDefaults(Iterable addresses,
                                                            int defaultConcurrencyLevel) {
    final List servers = new ArrayList();
    addresses.forEach(address
            -> servers.add(CycAddress.fromAddressWithDefaults(address, defaultConcurrencyLevel)));
    final CycServerPool result = new CycServerPool(servers);
    LOG.info("Created {} for Cyc servers (each with concurrencyLevel {}): {}",
            result, defaultConcurrencyLevel, addresses);
    return result;
  }

  /**
   * Creates a CycServerPool for a CycAddress, with a default concurrency level if unspecified in
   * the original address. This method will not modify the original CycAddress.
   *
   * @param address                 CycAddress from which to create the pool
   * @param defaultConcurrencyLevel the default concurrency level for the CycAddress, if unspecified
   *
   * @return a new CycServerPool
   */
  public static CycServerPool createFromServerWithDefaults(CycAddress address,
                                                           int defaultConcurrencyLevel) {
    final CycServerPool result
            = new CycServerPool(
                    CycAddress.fromAddressWithDefaults(address, defaultConcurrencyLevel));
    LOG.info("Created {} for Cyc server at {}", result, address);
    return result;
  }

  /**
   * Creates a CycServerPool from the CycAddress specified in a CycSession, with a default
   * concurrency level if unspecified in the original address. This method will not modify the
   * original CycAddress or CycSession.
   *
   * @param session                 CycAddress from which to create the pool
   * @param defaultConcurrencyLevel the default concurrency level for the session's CycAddress, if
   *                                unspecified
   *
   * @return a new CycServerPool
   */
  public static CycServerPool createFromSessionWithDefaults(CycSession session,
                                                            int defaultConcurrencyLevel) {
    return createFromServerWithDefaults(
            session.getConfiguration().getCycAddress(), defaultConcurrencyLevel);
  }

  /**
   * Creates a CycServerPool from the CycAddress specified in the current CycSession, with a default
   * concurrency level if unspecified in the original address. This method will not modify the
   * original CycAddress or CycSession.
   *
   * @param defaultConcurrencyLevel the default concurrency level for the session's CycAddress, if
   *                                unspecified
   *
   * @return a new CycServerPool
   *
   * @throws com.cyc.session.exception.SessionConfigurationException
   * @throws com.cyc.session.exception.SessionCommunicationException
   * @throws com.cyc.session.exception.SessionInitializationException
   */
  public static CycServerPool createFromCurrentSessionWithDefaults(int defaultConcurrencyLevel)
          throws SessionConfigurationException, SessionCommunicationException,
                 SessionInitializationException {
    return createFromSessionWithDefaults(CycSession.getCurrent(), defaultConcurrencyLevel);
  }

  /**
   * Creates a CycServerPool from the CycAddress specified in the current CycSession, with a default
   * concurrency level of 1 if unspecified in the original address. This method will not modify the
   * original CycAddress or CycSession.
   *
   * @return a new CycServerPool
   *
   * @throws com.cyc.session.exception.SessionConfigurationException
   * @throws com.cyc.session.exception.SessionCommunicationException
   * @throws com.cyc.session.exception.SessionInitializationException
   */
  public static CycServerPool createFromCurrentSession()
          throws SessionConfigurationException, SessionCommunicationException,
                 SessionInitializationException {
    return createFromCurrentSessionWithDefaults(1);
  }
  
  // Fields
  
  private static final Logger LOG = LoggerFactory.getLogger(CycServerPool.class);
  private final List cycServers;
  private final BlockingQueue cycWorkerPool;
  private int maxWorkerCount;
  
  // Construction
  
  public CycServerPool(List cycAddresses) {
    cycServers = new ArrayList(cycAddresses);
    cycWorkerPool = createServerPool(cycServers);
  }
  
  public CycServerPool(CycAddress cycAddress) {
    this(Arrays.asList(cycAddress));
  }
  
  /**
   * Creates a Cyc server pool from provided collection of CycAddress string specifications.
   * @param cycServersSpecs specification of a Cyc pool, listing servers in the following form:
   *                        Collection { "host1:port1:c1", "host2:port2:c2", ... },
   *                        where hostX specifies the host name, portX specifies the network port,
   *                        cX specifies the concurrency level - maximum number of
   *                        concurrent jobs a Cyc server will allow. The cX parameter is
   *                        optional and will default to 1 if absent.
   */
  public CycServerPool(Collection cycServersSpecs) {
    this(serversFromSpecs(cycServersSpecs));
  }
  
  /**
   * Creates a Cyc server pool from provided string specification.
   * @param cycServersSpecs specification of a Cyc pool, listing servers in the following form:
   *                        "host1:port1:c1,host2:port2:c2,host3:port3:c2...",
   *                        where hostX specifies the host name, portX specifies the network port,
   *                        cX specifies the concurrency level - maximum number of
   *                        concurrent jobs a Cyc server will allow. The cX parameter is
   *                        optional and will default to 1 if absent.
   */
  public CycServerPool(String cycServersSpecs) {
    this(Arrays.asList(cycServersSpecs.split(",")));
  }
  
  
  private BlockingQueue createServerPool(Collection cycServers) {
    List tempPool = new ArrayList<>();
  
    for (CycAddress server : cycServers) {
      for (int i = 0; i < server.getConcurrencyLevel().orElse(1); i++) {
        tempPool.add(server);
      }
    }
  
    maxWorkerCount = tempPool.size();
    Collections.shuffle(tempPool);
    
    BlockingQueue pool = new ArrayBlockingQueue<>(maxWorkerCount);
    pool.addAll(tempPool);
    
    return pool;
  }
  
  private static List serversFromSpecs(Collection cycServersSpecs) {
    List servers = new ArrayList<>(cycServersSpecs.size());
    
    for (String serverSpec : cycServersSpecs) {
      serverSpec = serverSpec.trim();
      
      if (CycAddress.isValidString(serverSpec)) {
        CycAddress server = CycAddress.fromString(serverSpec);
        servers.add(server);
      } else {
        LOG.info("{} is not a valid Cyc server specification, skipping.", serverSpec);
      }
    }
    
    return servers;
  }
  
  /**
   * Returns true if possiblyCycPoolSpec represents a valid Cyc server pool specification.
   * A string should have the following form: "host1:port1:c1,host2:port2:c2".
   * @param possiblyCycPoolSpec string to check for validity
   * @return true if possiblyCycPoolSpec is a valid Cyc server pool spec, false otherwise
   */
  public static boolean isValidString(String possiblyCycPoolSpec) {
    if (possiblyCycPoolSpec.isEmpty()) {
      return false;
    }
    
    String[] possiblyCycSpecs = possiblyCycPoolSpec.split(",");
    for (String possiblyCycSpec : possiblyCycSpecs) {
      if (!CycAddress.isValidString(possiblyCycSpec)) {
        return false;
      }
    }
    
    return true;
  }
  
  public CycAddress getDefaultServer() {
    if (cycServers.isEmpty()) {
      throw new RuntimeException("Unable to get default cyc server from pool, pool is empty.");
    }
    return cycServers.get(0);
  }
  
  public int getCycServerCount() {
    return cycServers.size();
  }
  
  public int getAvailableWorkerCount() {
    return cycWorkerPool.size();
  }
  
  public int getMaxWorkerCount() {
    return maxWorkerCount;
  }
  
  /**
   * Requests a Cyc worker from the pool, will block until one is available.
   * @return Cyc server specification
   * @throws InterruptedException
   */
  public CycAddress requestWorker() throws InterruptedException {
    return cycWorkerPool.take();
  }
  
  /**
   * Returns an unused Cyc worker back to the pool.
   * @param cyc Cyc worker to return to the pool
   */
  public void releaseWorker(CycAddress cyc) {
    // TODO: mark CycAddress as a part of the pool and change state whenever it's returned to the pool
    // to prevent the same Cyc server being released to the pool more than once
    cycWorkerPool.offer(cyc);
  }
  
  public List getServerAddresses() {
    return new ArrayList<>(cycServers);
  }
  
  public String toCycAddressString() {
    final StringBuilder sb = new StringBuilder();
    cycServers.forEach((addr) -> {
      sb.append(sb.length() > 0 ? "," : "").append(addr);
    });
    return sb.toString();
  }
  
  @Override
  public String toString() {
    return CycServerPool.class.getSimpleName()
                   + ":maxWorkerCount=" + getMaxWorkerCount()
                   + "#" + hashCode();
  }
  
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy