/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.usergrid.chop.webapp.coordinator.rest;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nullable;
import javax.mail.internet.MimeMultipart;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.usergrid.chop.webapp.ChopUiFig;
import org.apache.usergrid.chop.webapp.coordinator.CoordinatorUtils;
import org.apache.usergrid.chop.webapp.dao.model.BasicCommit;
import org.apache.usergrid.chop.webapp.dao.model.BasicRun;
import org.apache.usergrid.chop.webapp.dao.model.BasicRunResult;
import org.apache.usergrid.chop.webapp.elasticsearch.Util;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.apache.usergrid.chop.api.Commit;
import org.apache.usergrid.chop.api.Module;
import org.apache.usergrid.chop.api.RestParams;
import org.apache.usergrid.chop.webapp.dao.CommitDao;
import org.apache.usergrid.chop.webapp.dao.ModuleDao;
import org.apache.usergrid.chop.webapp.dao.RunDao;
import org.apache.usergrid.chop.webapp.dao.RunResultDao;
import org.apache.usergrid.chop.webapp.dao.model.BasicModule;
import org.safehaus.jettyjam.utils.TestMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.sun.jersey.multipart.FormDataParam;
/**
* REST operation to upload (a.k.a. deploy) a project war file.
*/
@Singleton
@Produces( MediaType.TEXT_PLAIN )
@Path( UploadResource.ENDPOINT )
public class UploadResource extends TestableResource implements RestParams {
public final static String ENDPOINT = "/upload";
public final static String SUCCESSFUL_TEST_MESSAGE = "Test parameters are OK";
private final static Logger LOG = LoggerFactory.getLogger( UploadResource.class );
@Inject
private ChopUiFig chopUiFig;
@Inject
private ModuleDao moduleDao;
@Inject
private RunDao runDao;
@Inject
private RunResultDao runResultDao;
@Inject
private CommitDao commitDao;
public UploadResource() {
super( ENDPOINT );
}
/**
* Uploads a file to the servlet context temp directory. More for testing proper uploads.
*/
@POST
@Consumes( MediaType.MULTIPART_FORM_DATA )
@Produces( MediaType.APPLICATION_JSON )
public Response upload( MimeMultipart multipart )
{
try {
String filename = multipart.getBodyPart( 0 ).getContent().toString();
LOG.warn( "FILENAME: " + filename );
InputStream in = multipart.getBodyPart( 1 ).getInputStream();
File tempDir = new File( chopUiFig.getContextTempDir() );
String fileLocation = new File( tempDir, filename ).getAbsolutePath();
CoordinatorUtils.writeToFile( in, fileLocation );
}
catch ( Exception ex ) {
LOG.error( "upload", ex );
return Response.status( Response.Status.INTERNAL_SERVER_ERROR ).entity( ex.getMessage() ).build();
}
return Response.status( Response.Status.CREATED ).entity( "ok" ).build();
}
/**
* Uploads an executable runner jar into a special path in the temp directory for the application.
*/
@POST
@Path( "/runner" )
@Consumes( MediaType.MULTIPART_FORM_DATA )
@Produces( MediaType.TEXT_PLAIN )
public Response uploadRunner(
@FormDataParam( COMMIT_ID ) String commitId,
@FormDataParam( MODULE_GROUPID ) String groupId,
@FormDataParam( MODULE_ARTIFACTID ) String artifactId,
@FormDataParam( MODULE_VERSION ) String version,
@FormDataParam( USERNAME ) String username,
@FormDataParam( VCS_REPO_URL ) String vcsRepoUrl,
@FormDataParam( TEST_PACKAGE ) String testPackage,
@FormDataParam( MD5 ) String md5,
@FormDataParam( CONTENT ) InputStream runnerJarStream,
@Nullable @QueryParam( TestMode.TEST_MODE_PROPERTY ) String testMode
) throws Exception {
if( inTestMode( testMode ) ) {
LOG.info( "Calling /upload/runner in test mode ..." );
}
else {
LOG.info( "/upload/runner called ..." );
}
LOG.debug( "extracted {} = {}", RestParams.COMMIT_ID, commitId );
LOG.debug( "extracted {} = {}", RestParams.MODULE_GROUPID, groupId );
LOG.debug( "extracted {} = {}", RestParams.MODULE_ARTIFACTID, artifactId );
LOG.debug( "extracted {} = {}", RestParams.MODULE_VERSION, version );
LOG.debug( "extracted {} = {}", RestParams.USERNAME, username );
LOG.debug( "extracted {} = {}", RestParams.VCS_REPO_URL, vcsRepoUrl );
LOG.debug( "extracted {} = {}", RestParams.TEST_PACKAGE, testPackage );
LOG.debug( "extracted {} = {}", RestParams.MD5, md5 );
if( inTestMode( testMode ) ) {
return Response.status( Response.Status.CREATED )
.entity( SUCCESSFUL_TEST_MESSAGE )
.type( MediaType.TEXT_PLAIN )
.build();
}
File runnerJar = CoordinatorUtils.getRunnerJar( chopUiFig.getContextPath(), username, groupId, artifactId,
version, commitId );
if ( ! runnerJar.getParentFile().exists() ) {
if ( runnerJar.getParentFile().mkdirs() ) {
LOG.info( "Created parent directory {} for uploaded runner file", runnerJar.getAbsolutePath() );
}
else {
String errorMessage = "Failed to create parent directory " + runnerJar.getAbsolutePath()
+ " for uploaded runner file.";
LOG.error( errorMessage );
return Response.status( Response.Status.INTERNAL_SERVER_ERROR ).entity( errorMessage ).build();
}
}
// Download and write the file to the proper position on disk & reference
CoordinatorUtils.writeToFile( runnerJarStream, runnerJar.getAbsolutePath() );
// - this is bad news because we will get commits of other users :(
// - we also need to qualify the commit with username, groupId,
// and the version of module as well
Commit commit = null;
Module module = null;
List<Commit> commits = commitDao.getByModule( artifactId );
for ( Commit returnedCommit : commits ) {
Module commitModule = moduleDao.get( returnedCommit.getModuleId() );
if ( commitModule.getArtifactId().equals( artifactId ) &&
commitModule.getGroupId().equals( groupId ) &&
commitModule.getVersion().equals( version ) )
{
commit = returnedCommit;
module = commitModule;
}
}
if ( module == null ) {
module = new BasicModule( groupId, artifactId, version, vcsRepoUrl, testPackage );
moduleDao.save( module );
}
if ( commit == null ) {
commit = new BasicCommit( commitId, module.getId(), md5, new Date(), runnerJar.getAbsolutePath() );
commitDao.save( commit );
}
return Response.status( Response.Status.CREATED ).entity( runnerJar.getAbsolutePath() ).build();
}
@SuppressWarnings( "unchecked" )
@POST
@Path( "/results" )
@Consumes( MediaType.MULTIPART_FORM_DATA )
@Produces( MediaType.TEXT_PLAIN )
public Response uploadResults( MimeMultipart multipart ) throws Exception
{
String runId = multipart.getBodyPart( RestParams.RUN_ID ).getContent().toString();
LOG.debug( "extracted {} = {}", RestParams.RUN_ID, runId );
InputStream in = multipart.getBodyPart( RestParams.CONTENT ).getInputStream();
JSONObject object = ( JSONObject ) new JSONParser().parse( new InputStreamReader( in ) );
JSONArray runResults = ( JSONArray ) object.get( "runResults" );
Iterator<JSONObject> iterator = runResults.iterator();
//noinspection WhileLoopReplaceableByForEach
while( iterator.hasNext() ) {
JSONObject jsonResult = iterator.next();
BasicRunResult runResult = new BasicRunResult(
runId,
Util.getInt(jsonResult, "runCount"),
Util.getInt( jsonResult, "runTime" ),
Util.getInt( jsonResult, "ignoreCount" ),
Util.getInt( jsonResult, "failureCount" )
);
if ( runResultDao.save( runResult ) ) {
LOG.info( "Saved run result: {}", runResult );
}
}
return Response.status( Response.Status.CREATED ).entity( "TRUE" ).build();
}
@POST
@Path( "/summary" )
@Consumes( MediaType.MULTIPART_FORM_DATA )
@Produces( MediaType.TEXT_PLAIN )
public Response uploadSummary( MimeMultipart multipart ) throws Exception
{
String runnerHostname = multipart.getBodyPart( RestParams.RUNNER_HOSTNAME ).getContent().toString();
LOG.debug( "extracted {} = {}", RestParams.RUNNER_HOSTNAME, runnerHostname );
InputStream in = multipart.getBodyPart( RestParams.CONTENT ).getInputStream();
JSONObject json = ( JSONObject ) new JSONParser().parse( new InputStreamReader( in ) );
BasicRun run = new BasicRun(
COMMIT_ID,
runnerHostname,
Util.getInt( json, "runNumber" ),
Util.getString( json, "testName" ) );
run.copyJson( json );
if ( runDao.save( run ) ) {
LOG.info( "Created new Run {} ", run );
}
else {
LOG.warn( "Failed to create new Run" );
}
return Response.status( Response.Status.CREATED ).entity( run.getId() ).build();
}
}