/** * This work was created by the National Center for Ecological Analysis and Synthesis * at the University of California Santa Barbara (UCSB). * * Copyright 2021 Regents of the University of California * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.ucsb.nceas.osti_elink; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import edu.ucsb.nceas.osti_elink.exception.ClassNotSupported; import edu.ucsb.nceas.osti_elink.exception.PropertyNotFound; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * A simple client application for the OSTI Elink Service that allows calling applications * to set up a connection to the service and maintain that connection across a series of * service invocations. Service call requests are maintained in a queue and submitted * to Elink asynchronously, allowing the rate of requests to the Elink service to be * controlled by the calling application. * @author tao */ public class OSTIElinkClient { public static final String USER_NAME_PROPERTY = "guid.doi.username"; public static final String PASSWORD_PROPERTY = "guid.doi.password"; public static final String BASE_URL_PROPERTY = "guid_doi_baseurl"; private OSTIElinkErrorAgent errorAgent = null; private OSTIElinkService service = null; private ExecutorService executor = null; private static Properties properties = null; protected static Log log = LogFactory.getLog(OSTIElinkClient.class); /** * Constructor * @param username the username of an OSTIElink account * @param password the password of the OSTIElink account * @param baseURL the base url of the OSTIElink service * @param errorAgent the class used to send error message to administers. It can be null. * If it is null, the error messages will only be logged in the error level. * */ public OSTIElinkClient( String username, String password, String baseURL, OSTIElinkErrorAgent errorAgent) { if (properties == null) { loadDefaultPropertyFile(); } if (username != null) { properties.setProperty(USER_NAME_PROPERTY, username); } if (password != null) { properties.setProperty(PASSWORD_PROPERTY, password); } properties.setProperty(BASE_URL_PROPERTY, baseURL); try { service = OSTIServiceFactory.getOSTIElinkService(properties); } catch (PropertyNotFound | ClassNotFoundException | ClassNotSupported | IOException | OSTIElinkException e) { log.error("Can't generate the OSTIElinkService instance since " + e.getMessage(), e); throw new RuntimeException(e); } this.errorAgent = errorAgent; startExecutorLoop(); } private void loadDefaultPropertyFile() { try (InputStream is = getClass().getClassLoader().getResourceAsStream("osti.properties")) { properties = new Properties(); properties.load(is); } catch (IOException e) { log.error("Can't load the default property file into properties " + e.getMessage(), e); throw new RuntimeException(e); } } /** * Set the given properties to the class. This method is for testing only * @param properties1 the properties will be used to create the client. */ public static void setProperties(Properties properties1) { properties = properties1; } /** * Set the meta data for a given identifier. The identifier should already exist in the elink service. * The method will run the commands in another thread. * We always use the query method to figure out internal OSTI id (not the prefix comparison). * @param identifier the identifier of object which will be set a new metadata * @param metadata the new metadata which will be used */ public void setMetadata(String identifier, String metadata) throws InterruptedException { OSTIElinkServiceRequest request = new OSTIElinkServiceRequest(service, OSTIElinkServiceRequest.SETMETADATA, identifier, errorAgent, metadata); executor.execute(request); } /** * Ask the elink service to generate a doi for the given siteCode. * The thread blocks until the identifier is returned * @param siteCode the siteCode will be used. If it is null, the default one, ess-dive, will be used. * @return the newly generated doi * @throws OSTIElinkException */ public String mintIdentifier(String siteCode) throws OSTIElinkException { String identifier = null; try { identifier = service.mintIdentifier(siteCode); } catch (OSTIElinkException e) { if (errorAgent != null) { errorAgent.notify(e.getMessage()); } throw e; } return identifier; } /** * Get the associated metadata for the given identifier. * The thread blocks until the identifier is returned * @param identifier for which metadata should be returned * @return the metadata associated with the identifier * @throws OSTIElinkException */ public String getMetadata(String identifier) throws OSTIElinkException { return service.getMetadata(identifier); } /** * Get the status for the given identifier. If there are multiple records * associate with the identifier, the status of the first one will be returned. * The thread blocks until the status is returned * @param identifier id to identify whose status should be returned * @return the metadata associated with the identifier * @throws OSTIElinkException */ public String getStatus(String identifier) throws OSTIElinkException { return service.getStatus(identifier); } private void startExecutorLoop() { // Query the runtime to see how many CPUs are available, and configure that many threads Runtime runtime = Runtime.getRuntime(); int numCores = runtime.availableProcessors(); log.debug("OSTIElinkClient.startExecutorLoop - Number of cores available: " + numCores); executor = Executors.newFixedThreadPool(numCores); } /** * Shut down the excutor loop until all submitted tasks are completed. */ public void shutdown() { log.debug("Shutting down executor..."); // Stop the executor from accepting new requests and finishing existing Runnables executor.shutdown(); // Wait until all Runnables are finished while (!executor.isTerminated()) { //log.debug("OSTIElinkClient.shutdown...."); } } /** * Get the OSTIElinkService object associated with the client * @return the OSTIElinkService object */ public OSTIElinkService getService() { return this.service; } }