JiraService.java
/*
* Copyright (c) 2002-2020, City of Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.lutecetools.service;
import java.io.IOException;
import java.net.URI;
import org.apache.commons.lang.StringUtils;
import com.atlassian.jira.rest.client.api.JiraRestClient;
import com.atlassian.jira.rest.client.api.RestClientException;
import com.atlassian.jira.rest.client.api.VersionRestClient;
import com.atlassian.jira.rest.client.api.domain.Project;
import com.atlassian.jira.rest.client.api.domain.Version;
import com.atlassian.jira.rest.client.api.domain.VersionRelatedIssuesCount;
import com.atlassian.jira.rest.client.auth.BasicHttpAuthenticationHandler;
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory;
import fr.paris.lutece.plugins.lutecetools.business.Component;
import fr.paris.lutece.portal.service.util.AppLogService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
/**
* JIRA Service name
*/
public class JiraService implements ComponentInfoFiller
{
public static final String JIRA_STATUS = "jiraStatus";
public static final String JIRA_ISSUES_COUNT = "jiraIssuesCount";
public static final String JIRA_UNRESOLVED_ISSUES_COUNT = "jiraUnresolvedIssuesCount";
public static final String JIRA_FIXED_ISSUES_COUNT = "jiraFixedIssuesCount";
private static final String JIRA_LAST_RELEASED_VERSION = "jiraLastReleasedVersion";
private static final String JIRA_LAST_UNRELEASED_VERSION = "jiraLastUnreleasedVersion";
private static final String JIRA_KEY_ERROR = "jiraKeyError";
private static final String JIRA_ERRORS = "jiraErrors";
private static final String PROPERTY_URL_JIRA_SERVER = "lutecetools.jira.url";
private static final String URL_JIRA_SERVER = AppPropertiesService.getProperty( PROPERTY_URL_JIRA_SERVER );
private static final String PROPERTY_URL_API_VERSION = "lutecetools.jira.api";
private static final String URL_API_VERSION = URL_JIRA_SERVER + AppPropertiesService.getProperty( PROPERTY_URL_API_VERSION );
private static final String PROPERTY_JIRA_USR = "lutecetools.jira.user";
private static final String JIRA_USR = AppPropertiesService.getProperty( PROPERTY_JIRA_USR );
private static final String PROPERTY_JIRA_PWD = "lutecetools.jira.pwd";
private static final String JIRA_PWD = AppPropertiesService.getProperty( PROPERTY_JIRA_PWD );
private static final String SERVICE_NAME = "JIRA Info filler service registered";
private static final int JIRAKEY_ERROR_MISSING = 1;
private static final int JIRAKEY_ERROR_INVALID = 2;
private static final AsynchronousJiraRestClientFactory FACTORY = new AsynchronousJiraRestClientFactory( );
private static final BasicHttpAuthenticationHandler AUTH = new BasicHttpAuthenticationHandler( JIRA_USR, JIRA_PWD );
/**
* Constructor
*/
public JiraService( )
{
String strProxyHost = AppPropertiesService.getProperty( "httpAccess.proxyHost" );
String strProxyPort = AppPropertiesService.getProperty( "httpAccess.proxyPort" );
String strProxyUserName = AppPropertiesService.getProperty( "httpAccess.proxyUserName" );
String strProxyPassword = AppPropertiesService.getProperty( "httpAccess.proxyPassword" );
if ( !StringUtils.isEmpty( strProxyHost ) )
{
System.getProperties( ).put( "https.proxyHost", strProxyHost );
System.getProperties( ).put( "https.proxyPort", strProxyPort );
System.getProperties( ).put( "https.proxyUser", strProxyUserName );
System.getProperties( ).put( "https.proxyPassword", strProxyPassword );
System.getProperties( ).put( "https.proxySet", "true" );
AppLogService.info( "LuteceTools : Using httpaccess.properties defined proxy to connect to JIRA." );
}
}
/**
* {@inheritDoc }
*/
@Override
public String getName( )
{
return SERVICE_NAME;
}
/**
* {@inheritDoc }
*/
@Override
public void fill( Component component, StringBuilder sbLogs )
{
JiraRestClient client = null;
String strJiraKey = component.get( Component.JIRA_KEY );
if ( strJiraKey == null )
{
component.set( JIRA_KEY_ERROR, JIRAKEY_ERROR_MISSING );
sbLogs.append( "\n*** ERROR *** Error no Jira key defined for component " )
.append( component.getArtifactId( ) );
return;
}
try
{
client = FACTORY.create( new URI( URL_JIRA_SERVER ), AUTH );
Project project = client.getProjectClient( ).getProject( strJiraKey ).claim( );
Version versionLastReleased = null;
Version versionLastUnreleased = null;
for ( Version version : project.getVersions( ) )
{
if ( !version.isReleased( ) )
{
versionLastUnreleased = version;
}
else
{
versionLastReleased = version;
}
}
if ( versionLastReleased != null )
{
component.set( JIRA_LAST_RELEASED_VERSION, versionLastReleased.getName( ) );
}
if ( versionLastUnreleased != null )
{
component.set( JIRA_LAST_UNRELEASED_VERSION, versionLastUnreleased.getName( ) );
String strURI = URL_API_VERSION + versionLastUnreleased.getId( );
URI uriVersion = new URI( strURI );
VersionRestClient clientVersion = client.getVersionRestClient( );
int nUnresolvedIssues = clientVersion.getNumUnresolvedIssues( uriVersion ).claim( );
component.set( JIRA_UNRESOLVED_ISSUES_COUNT, nUnresolvedIssues );
VersionRelatedIssuesCount vRelatedIssues = clientVersion.getVersionRelatedIssuesCount( uriVersion )
.claim( );
component.set( JIRA_ISSUES_COUNT, vRelatedIssues.getNumFixedIssues( ) );
component.set( JIRA_FIXED_ISSUES_COUNT, vRelatedIssues.getNumFixedIssues( ) - nUnresolvedIssues );
if ( AppLogService.isDebugEnabled( ) )
{
StringBuilder sbDebug = new StringBuilder( );
sbDebug.append( "LuteceTools : JiraService - Project " ).append( strJiraKey )
.append( " AffectedIssues : " ).append( vRelatedIssues.getNumAffectedIssues( ) )
.append( " FixedIssues : " ).append( vRelatedIssues.getNumFixedIssues( ) )
.append( " Unresolved : " ).append( nUnresolvedIssues );
AppLogService.debug( sbDebug.toString( ) );
}
}
component.set( JIRA_STATUS, getJiraStatus( component ) );
component.set( JIRA_ERRORS, getJiraErrors( component ) );
}
catch ( RestClientException ex )
{
component.set( JIRA_KEY_ERROR, JIRAKEY_ERROR_INVALID );
sbLogs.append( "\n*** ERROR *** Invalid Jira Key '" ).append( strJiraKey ).append( " for component " )
.append( component.getArtifactId( ) );
}
catch ( Exception ex )
{
sbLogs.append( "\n*** ERROR *** Error getting Jira Infos for Key : '" ).append( strJiraKey )
.append( "' : " ).append( ex.getMessage( ) ).append( " for component " )
.append( component.getArtifactId( ) );
}
finally
{
if ( client != null )
{
try
{
client.close( );
}
catch ( IOException ex )
{
AppLogService.error( "LuteceTools : Error using Jira Client API : " + ex.getMessage( ), ex );
}
}
}
}
/**
* Returns Jira Errors
*
* @param component The component
* @return The errors
*/
public String getJiraErrors( Component component )
{
StringBuilder sbErrors = new StringBuilder( );
if ( ( component.getVersion( ) != null )
&& !component.getVersion( ).equals( component.get( JIRA_LAST_RELEASED_VERSION ) ) )
{
sbErrors.append( "Last Jira released version is not matching the last version in maven repository. \n" );
}
if ( ( component.get( Component.SNAPSHOT_VERSION ) != null )
&& ( component.get( JIRA_LAST_UNRELEASED_VERSION ) != null ) && !component
.get( Component.SNAPSHOT_VERSION ).startsWith( component.get( JIRA_LAST_UNRELEASED_VERSION ) ) )
{
sbErrors.append( "Current Jira roadmap version is not matching current snapshot version. \n" );
}
if ( component.get( Component.JIRA_KEY ) == null )
{
sbErrors.append( "JIRA key is missing in the pom.xml. \n" );
}
if ( component.getObject( JIRA_KEY_ERROR ) != null
&& component.getInt( JIRA_KEY_ERROR ) == JIRAKEY_ERROR_INVALID )
{
sbErrors.append( "JIRA key '" ).append( component.get( Component.JIRA_KEY ) ).append( "' is invalid. \n" );
}
return sbErrors.toString( );
}
/**
* Gets Jira status
*
* @param component The component
* @return The status
*/
public int getJiraStatus( Component component )
{
int nStatus = 0;
if ( ( component.getVersion( ) != null )
&& component.getVersion( ).equals( component.get( JIRA_LAST_RELEASED_VERSION ) ) )
{
nStatus++;
}
if ( ( component.get( Component.SNAPSHOT_VERSION ) != null )
&& ( component.get( JIRA_LAST_UNRELEASED_VERSION ) != null ) && component
.get( Component.SNAPSHOT_VERSION ).startsWith( component.get( JIRA_LAST_UNRELEASED_VERSION ) ) )
{
nStatus++;
}
return nStatus;
}
}