org.kuali.coeus.propdev.impl.s2s.connect.S2SConnectorServiceBase.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.coeus.propdev.impl.s2s.connect.S2SConnectorServiceBase.java

Source

/*
 * Kuali Coeus, a comprehensive research administration system for higher education.
 * 
 * Copyright 2005-2015 Kuali, Inc.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.coeus.propdev.impl.s2s.connect;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.kuali.coeus.propdev.api.s2s.S2SConfigurationService;
import org.kuali.kra.infrastructure.Constants;
import org.kuali.kra.infrastructure.KeyConstants;
import org.kuali.coeus.propdev.impl.core.DevelopmentProposal;
import org.kuali.coeus.s2sgen.api.core.ConfigurationConstants;
import org.kuali.rice.krad.data.DataObjectService;

import gov.grants.apply.services.applicantwebservices_v2.GetApplicationListRequest;
import gov.grants.apply.services.applicantwebservices_v2.GetApplicationListResponse;
import gov.grants.apply.services.applicantwebservices_v2.GetApplicationStatusDetailRequest;
import gov.grants.apply.services.applicantwebservices_v2.GetApplicationStatusDetailResponse;
import gov.grants.apply.services.applicantwebservices_v2.GetOpportunitiesRequest;
import gov.grants.apply.services.applicantwebservices_v2.GetOpportunitiesResponse;
import gov.grants.apply.services.applicantwebservices_v2.SubmitApplicationRequest;
import gov.grants.apply.services.applicantwebservices_v2.SubmitApplicationResponse;
import gov.grants.apply.services.applicantwebservices_v2_0.ApplicantWebServicesPortType;
import gov.grants.apply.services.applicantwebservices_v2_0.ErrorMessage;
import gov.grants.apply.system.grantscommonelements_v1.ApplicationFilter;
import gov.grants.apply.system.grantscommonelements_v1.Attachment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import javax.activation.DataHandler;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.SOAPFaultException;
import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.*;

/**
 * 
 * This class is used to make web service call to grants.gov
 */
public class S2SConnectorServiceBase implements S2SConnectorService {

    private static final String KEY_OPPORTUNITY_ID = "OpportunityID";
    private static final String KEY_CFDA_NUMBER = "CFDANumber";
    private static final String KEY_SUBMISSION_TITLE = "SubmissionTitle";
    protected static final Log LOG = LogFactory.getLog(S2SConnectorServiceBase.class);

    @Autowired
    @Qualifier("dataObjectService")
    private DataObjectService dataObjectService;

    @Autowired
    @Qualifier("s2SConfigurationService")
    private S2SConfigurationService s2SConfigurationService;

    protected S2SCertificateReader s2sCertificateReader;
    protected String serviceHost;
    protected String servicePort;

    /**
     * This method is to get Opportunity List for the given cfda number,opportunity Id and competition Id from the grants guv. It
     * sets the given parameters on {@link GetOpportunitiesRequest} object and passes it to the web service.
     * 
     * @param cfdaNumber of the opportunity.
     * @param opportunityId parameter for the opportunity.
     * @param competitionId parameter for the opportunity.
     * @return GetOpportunityListResponse available list of opportunities applicable for the given cfda number,opportunity Id and
     *         competition Id.
     * @throws S2sCommunicationException
     * @see org.kuali.coeus.propdev.impl.s2s.connect.S2SConnectorService#getOpportunityList(java.lang.String, java.lang.String,
     *      java.lang.String)
     */
    public GetOpportunitiesResponse getOpportunityList(String cfdaNumber, String opportunityId,
            String competitionId) throws S2sCommunicationException {
        ApplicantWebServicesPortType port = configureApplicantIntegrationSoapPort(null, false);
        GetOpportunitiesRequest getOpportunityListRequest = new GetOpportunitiesRequest();

        getOpportunityListRequest.setCFDANumber(cfdaNumber);
        getOpportunityListRequest.setCompetitionID(competitionId);
        getOpportunityListRequest.setFundingOpportunityNumber(opportunityId);
        try {
            return port.getOpportunities(getOpportunityListRequest);
        } catch (SOAPFaultException soapFault) {
            LOG.error("Error while getting list of opportunities", soapFault);
            if (soapFault.getMessage().contains("Connection refused")) {
                throw new S2sCommunicationException(KeyConstants.ERROR_GRANTSGOV_OPP_SER_UNAVAILABLE,
                        soapFault.getMessage());
            } else {
                throw new S2sCommunicationException(KeyConstants.ERROR_S2S_UNKNOWN, soapFault.getMessage());
            }
        } catch (ErrorMessage | WebServiceException e) {
            LOG.error("Error while getting list of opportunities", e);
            throw new S2sCommunicationException(KeyConstants.ERROR_S2S_UNKNOWN, e.getMessage());
        }
    }

    /**
     * This method is to get status of the submitted application.
     * 
     * @param ggTrackingId grants gov tracking id for the proposal.
     * @param proposalNumber Proposal number.
     * @return GetApplicationStatusDetailResponse status of the submitted application.
     * @throws S2sCommunicationException
     * @see org.kuali.coeus.propdev.impl.s2s.connect.S2SConnectorService#getApplicationStatusDetail(java.lang.String, java.lang.String)
     */
    public GetApplicationStatusDetailResponse getApplicationStatusDetail(String ggTrackingId, String proposalNumber)
            throws S2sCommunicationException {
        ApplicantWebServicesPortType port = getApplicantIntegrationSoapPort(proposalNumber);
        GetApplicationStatusDetailRequest applicationStatusDetailRequest = new GetApplicationStatusDetailRequest();
        applicationStatusDetailRequest.setGrantsGovTrackingNumber(ggTrackingId);
        try {
            return port.getApplicationStatusDetail(applicationStatusDetailRequest);
        } catch (ErrorMessage | WebServiceException e) {
            LOG.error("Error while getting proposal submission status details", e);
            throw new S2sCommunicationException(KeyConstants.ERROR_GRANTSGOV_SERVER_STATUS_REFRESH, e.getMessage());
        }
    }

    /**
     * This method is to get Application List from grants.gov for opportunityId, cfdaNumber and proposalNumber
     * 
     * @param opportunityId of the opportunity.
     * @param cfdaNumber of the opportunity.
     * @param proposalNumber proposal number.
     * @return GetApplicationListResponse application list.
     * @throws S2sCommunicationException
     * @see org.kuali.coeus.propdev.impl.s2s.connect.S2SConnectorService#getApplicationList(java.lang.String, java.lang.String,
     *      java.lang.String)
     */
    public GetApplicationListResponse getApplicationList(String opportunityId, String cfdaNumber,
            String proposalNumber) throws S2sCommunicationException {
        GetApplicationListRequest applicationListRequest = new GetApplicationListRequest();
        List<ApplicationFilter> filterList = applicationListRequest.getApplicationFilter();
        ApplicationFilter applicationFilter = new ApplicationFilter();
        applicationFilter.setFilter(KEY_OPPORTUNITY_ID);
        applicationFilter.setFilterValue(opportunityId);
        filterList.add(applicationFilter);
        if (cfdaNumber != null) {
            applicationFilter = new ApplicationFilter();
            applicationFilter.setFilter(KEY_CFDA_NUMBER);
            applicationFilter.setFilterValue(cfdaNumber);
            filterList.add(applicationFilter);
        }
        applicationFilter = new ApplicationFilter();
        applicationFilter.setFilter(KEY_SUBMISSION_TITLE);
        applicationFilter.setFilterValue(proposalNumber);
        filterList.add(applicationFilter);
        ApplicantWebServicesPortType port = getApplicantIntegrationSoapPort(proposalNumber);
        try {
            return port.getApplicationList(applicationListRequest);
        } catch (ErrorMessage | WebServiceException e) {
            LOG.error("Error occured while fetching application list", e);
            throw new S2sCommunicationException(KeyConstants.ERROR_GRANTSGOV_SERVER_APPLICATION_LIST_REFRESH,
                    e.getMessage());
        }
    }

    /**
     * This method is to submit a proposal to grants.gov
     * 
     * @param xmlText xml format of the form object.
     * @param attachments attachments of the proposal.
     * @param proposalNumber proposal number.
     * @return SubmitApplicationResponse corresponding to the input parameters passed.
     * @throws S2sCommunicationException
     * @see org.kuali.coeus.propdev.impl.s2s.connect.S2SConnectorService#submitApplication(java.lang.String, java.util.Map, java.lang.String)
     */
    public SubmitApplicationResponse submitApplication(String xmlText, Map<String, DataHandler> attachments,
            String proposalNumber) throws S2sCommunicationException {
        ApplicantWebServicesPortType port = getApplicantIntegrationSoapPort(proposalNumber);
        SubmitApplicationRequest request = new SubmitApplicationRequest();

        for (Map.Entry<String, DataHandler> entry : attachments.entrySet()) {
            DataHandler dataHandler = entry.getValue();
            Attachment attachment = new Attachment();
            attachment.setFileContentId(entry.getKey());
            attachment.setFileDataHandler(dataHandler);
            request.getAttachment().add(attachment);
        }
        request.setGrantApplicationXML(xmlText);

        try {
            return port.submitApplication(request);
        } catch (ErrorMessage e) {
            LOG.error("Error occured while submitting proposal to Grants Gov", e);
            throw new S2sCommunicationException(KeyConstants.ERROR_GRANTSGOV_SERVER_SUBMIT_APPLICATION,
                    e.getMessage());
        } catch (WebServiceException e) {
            LOG.error("Error occured while submitting proposal to Grants Gov", e);
            throw new S2sCommunicationException(KeyConstants.ERROR_S2S_UNKNOWN, e.getMessage());
        }
    }

    /**
     * 
     * This method is to get Soap Port in case of multicampus
     * 
     * @param proposalNumber Proposal number.
     * @return ApplicantIntegrationPortType Soap port used for applicant integration.
     * @throws S2sCommunicationException
     */
    protected ApplicantWebServicesPortType getApplicantIntegrationSoapPort(String proposalNumber)
            throws S2sCommunicationException {
        DevelopmentProposal pdDoc = dataObjectService.find(DevelopmentProposal.class, proposalNumber);
        Boolean multiCampusEnabled = s2SConfigurationService
                .getValueAsBoolean(ConfigurationConstants.MULTI_CAMPUS_ENABLED);
        return configureApplicantIntegrationSoapPort(
                pdDoc.getApplicantOrganization().getOrganization().getDunsNumber(), multiCampusEnabled);
    }

    /**
     * 
     * This method is to get Soap Port
     * 
     * @return ApplicantIntegrationPortType Soap port used for applicant integration.
     * @throws S2sCommunicationException
     */
    protected ApplicantWebServicesPortType configureApplicantIntegrationSoapPort(String alias,
            boolean mulitCampusEnabled) throws S2sCommunicationException {
        System.clearProperty("java.protocol.handler.pkgs");
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        factory.setAddress(getS2SSoapHost());
        factory.setServiceClass(ApplicantWebServicesPortType.class);
        // enabling mtom from V2 onwards
        // works for grants.gov but not for research.gov, get a mime related error.
        //Couldn't find MIME boundary: --uuid
        //disable for research.gov. This is not a big deal because submissions with attachments
        // go to grants.gov anyways and not to research.gov
        if (!StringUtils.equalsIgnoreCase(serviceHost, Constants.RESEARCH_GOV_SERVICE_HOST)) {
            Map<String, Object> properties = new HashMap<>();
            properties.put("mtom-enabled", Boolean.TRUE);
            factory.setProperties(properties);
        }

        ApplicantWebServicesPortType applicantWebService = (ApplicantWebServicesPortType) factory.create();
        Client client = ClientProxy.getClient(applicantWebService);
        HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
        httpClientPolicy.setConnectionTimeout(0);
        httpClientPolicy.setReceiveTimeout(0);
        httpClientPolicy.setAllowChunking(false);
        HTTPConduit conduit = (HTTPConduit) client.getConduit();
        conduit.setClient(httpClientPolicy);
        TLSClientParameters tlsConfig = new TLSClientParameters();
        setPossibleCypherSuites(tlsConfig);
        configureKeyStoreAndTrustStore(tlsConfig, alias, mulitCampusEnabled);
        conduit.setTlsClientParameters(tlsConfig);
        return applicantWebService;
    }

    protected void setPossibleCypherSuites(TLSClientParameters tlsConfig) {
        FiltersType filters = new FiltersType();
        filters.getInclude().add("SSL_RSA_WITH_RC4_128_MD5");
        filters.getInclude().add("SSL_RSA_WITH_RC4_128_SHA");
        filters.getInclude().add("SSL_RSA_WITH_3DES_EDE_CBC_SHA");
        filters.getInclude().add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
        filters.getInclude().add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
        filters.getInclude().add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256");
        filters.getInclude().add(".*_EXPORT_.*");
        filters.getInclude().add(".*_EXPORT1024_.*");
        filters.getInclude().add(".*_WITH_DES_.*");
        filters.getInclude().add(".*_WITH_3DES_.*");
        filters.getInclude().add(".*_WITH_RC4_.*");
        filters.getInclude().add(".*_WITH_NULL_.*");
        filters.getInclude().add(".*_DH_anon_.*");

        tlsConfig.setDisableCNCheck(true);

        String certAlgorithm = getS2SConfigurationService().getValueAsString("s2s.cert.algorithm");
        if (certAlgorithm != null) {
            tlsConfig.setSecureSocketProtocol(certAlgorithm);
        }

        tlsConfig.setCipherSuitesFilter(filters);
    }

    /**
     * This method is to confgiure KeyStore and Truststore for Grants.Gov webservice client
     * @param tlsConfig
     * @param alias
     * @param mulitCampusEnabled
     * @throws S2sCommunicationException
     */
    protected void configureKeyStoreAndTrustStore(TLSClientParameters tlsConfig, String alias,
            boolean mulitCampusEnabled) throws S2sCommunicationException {
        KeyStore keyStore = s2sCertificateReader.getKeyStore();
        KeyManagerFactory keyManagerFactory;
        try {
            keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            if (alias != null && mulitCampusEnabled) {
                KeyStore keyStoreAlias;
                keyStoreAlias = KeyStore.getInstance(s2sCertificateReader.getJksType());
                Certificate[] certificates = keyStore.getCertificateChain(alias);
                Key key = keyStore.getKey(alias, s2SConfigurationService
                        .getValueAsString(s2sCertificateReader.getKeyStorePassword()).toCharArray());
                keyStoreAlias.load(null, null);
                keyStoreAlias.setKeyEntry(
                        alias, key, s2SConfigurationService
                                .getValueAsString(s2sCertificateReader.getKeyStorePassword()).toCharArray(),
                        certificates);
                keyManagerFactory.init(keyStoreAlias, s2SConfigurationService
                        .getValueAsString(s2sCertificateReader.getKeyStorePassword()).toCharArray());
            } else {
                keyManagerFactory.init(keyStore, s2SConfigurationService
                        .getValueAsString(s2sCertificateReader.getKeyStorePassword()).toCharArray());
            }
            KeyManager[] km = keyManagerFactory.getKeyManagers();
            tlsConfig.setKeyManagers(km);
            KeyStore trustStore = s2sCertificateReader.getTrustStore();
            TrustManagerFactory trustManagerFactory = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trustStore);
            TrustManager[] tm = trustManagerFactory.getTrustManagers();
            tlsConfig.setTrustManagers(tm);
        } catch (NoSuchAlgorithmException | KeyStoreException | UnrecoverableKeyException | CertificateException
                | IOException e) {
            LOG.error(e.getMessage(), e);
            throw new S2sCommunicationException(KeyConstants.ERROR_KEYSTORE_CONFIG, e.getMessage());
        }
    }

    /**
     * 
     * This method returns the Host URL for S2S web services
     * 
     * @return {@link String} host URL
     * @throws S2sCommunicationException if unable to read property file
     */

    protected String getS2SSoapHost() throws S2sCommunicationException {
        StringBuilder host = new StringBuilder();
        host.append(s2SConfigurationService.getValueAsString(getServiceHost()));
        String port = s2SConfigurationService.getValueAsString(getServicePort());
        if ((!host.toString().endsWith("/")) && (!port.startsWith("/"))) {
            host.append("/");
        }
        host.append(port);
        return host.toString();
    }

    public String getServiceHost() {
        return serviceHost;
    }

    public void setServiceHost(String serviceHost) {
        this.serviceHost = serviceHost;
    }

    public String getServicePort() {
        return servicePort;
    }

    public void setServicePort(String servicePort) {
        this.servicePort = servicePort;
    }

    public S2SCertificateReader getS2sCertificateReader() {
        return s2sCertificateReader;
    }

    public void setS2sCertificateReader(S2SCertificateReader s2sCertificateReader) {
        this.s2sCertificateReader = s2sCertificateReader;
    }

    public DataObjectService getDataObjectService() {
        return dataObjectService;
    }

    public void setDataObjectService(DataObjectService dataObjectService) {
        this.dataObjectService = dataObjectService;
    }

    public S2SConfigurationService getS2SConfigurationService() {
        return s2SConfigurationService;
    }

    public void setS2SConfigurationService(S2SConfigurationService s2SConfigurationService) {
        this.s2SConfigurationService = s2SConfigurationService;
    }
}