/*
* 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.dao;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.apache.usergrid.chop.api.Commit;
import org.apache.usergrid.chop.api.Run;
import org.apache.usergrid.chop.api.Runner;
import org.apache.usergrid.chop.webapp.dao.model.BasicRun;
import org.apache.usergrid.chop.webapp.elasticsearch.IElasticSearchClient;
import org.apache.usergrid.chop.webapp.elasticsearch.Util;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.inQuery;
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
/**
* Run persistence operations
*/
public class RunDao extends Dao {
public static final String DAO_INDEX_KEY = "modules";
public static final String DAO_TYPE_KEY = "run";
private static final int MAX_RESULT_SIZE = 10000;
@Inject
public RunDao( IElasticSearchClient elasticSearchClient ) {
super( elasticSearchClient );
}
/**
* @param run Run to save in elastic search
* @return Whether the operation succeeded
* @throws Exception
*/
public boolean save( Run run ) throws IOException {
IndexResponse response = elasticSearchClient.getClient()
.prepareIndex( DAO_INDEX_KEY, DAO_TYPE_KEY, run.getId() )
.setRefresh( true )
.setSource(
jsonBuilder()
.startObject()
.field( "id", run.getId() )
.field( "commitId", run.getCommitId() )
.field( "runner", run.getRunner() )
.field( "runNumber", run.getRunNumber() )
.field( "testName", run.getTestName() )
.field( "chopType", run.getChopType() )
.field( "iterations", run.getIterations() )
.field( "totalTestsRun", run.getTotalTestsRun() )
.field( "threads", run.getThreads() )
.field( "delay", run.getDelay() )
.field( "time", run.getTime() )
.field( "actualTime", run.getActualTime() )
.field( "minTime", run.getMinTime() )
.field( "maxTime", run.getMaxTime() )
.field( "meanTime", run.getAvgTime() )
.field( "failures", run.getFailures() )
.field( "ignores", run.getIgnores() )
.field( "saturate", run.getSaturate() )
.field( "startTime", run.getStartTime() )
.field( "stopTime", run.getStopTime() )
.endObject()
)
.execute()
.actionGet();
return response.isCreated();
}
/**
*
* @param run
* @return
*/
public boolean delete( Run run ) {
DeleteResponse response = elasticSearchClient.getClient()
.prepareDelete( DAO_INDEX_KEY, DAO_TYPE_KEY, run.getId() )
.setRefresh( true )
.execute()
.actionGet();
return response.isFound();
}
/**
* @param runId Id of queried Run
* @return Run object or null if it doesn't exist
*/
public Run get( String runId ) {
SearchResponse response = getRequest( DAO_INDEX_KEY, DAO_TYPE_KEY )
.setQuery( termQuery( "_id", runId ) )
.execute()
.actionGet();
SearchHit hits[] = response.getHits().hits();
return hits.length > 0 ? toRun( hits[ 0 ] ) : null;
}
/**
* Returns a map of all Runs with queried commitId, runNumber and testName.
* <p>
* <ul>
* <li>Key of the map is Run's id in elastic search</li>
* <li>Value of the map is Run itself</li>
* </ul>
*
* @param commitId commit id of the Run
* @param runNumber Run number to filter queried Runs
* @param testName Test class name that resulting Run is about
* @return Map satisfying given parameters. The map is empty if there are no Runs.
*/
public Map<String, Run> getMap( String commitId, int runNumber, String testName ) {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must( termQuery( "commitId", commitId.toLowerCase() ) )
.must( termQuery( "runNumber", runNumber ) )
.must( termQuery( "testName", testName.toLowerCase() ) );
SearchResponse response = getRequest( DAO_INDEX_KEY, DAO_TYPE_KEY )
.setQuery( queryBuilder )
.setSize( MAX_RESULT_SIZE )
.execute()
.actionGet();
HashMap<String, Run> runs = new HashMap<String, Run>();
for ( SearchHit hit : response.getHits().hits() ) {
runs.put( hit.getId(), toRun( hit ) );
}
return runs;
}
/**
* Returns a map of all runs with matching commitId, runNumber, testName, and one of given runners' hostname.
* <p>
* <ul>
* <li>Key field of the map is Run's id in elastic search</li>
* <li>Value field of the map is the Run itself</li>
* </ul>
*
* @param commitId commit id of the Run
* @param runNumber Run number to filter queried Runs
* @param testName Test class name that resulting Run is about
* @param runners A Run whose runner field is one of runners' hostname fields is a match
* @return Map satisfying given parameters. The map is empty if there are no matching Runs.
*/
public Map<String, Run> getMap( String commitId, int runNumber, String testName, Collection<Runner> runners ) {
Map<String, Run> map = getMap( commitId, runNumber, testName );
Map<String, Run> mapFilteredWithRunners = new HashMap<String, Run>();
for( String key : map.keySet() ) {
boolean matchesOne = false;
for( Runner runner: runners ) {
if( runner.getHostname().equals( map.get( key ).getRunner() ) ) {
matchesOne = true;
break;
}
}
if( matchesOne ) {
mapFilteredWithRunners.put( key, map.get( key ) );
}
}
return mapFilteredWithRunners;
}
private static Run toRun( SearchHit hit ) {
Map<String, Object> json = hit.getSource();
BasicRun run = new BasicRun(
Util.getString( json, "id" ),
Util.getString( json, "commitId" ),
Util.getString( json, "runner" ),
Util.getInt( json, "runNumber" ),
Util.getString(json, "testName" )
);
run.copyJson( hit.getSource() );
return run;
}
private static String concatIds( List<Commit> commits ) {
StringBuilder ids = new StringBuilder();
for ( Commit commit : commits ) {
ids.append( commit.getId() )
.append( " " );
}
return ids.toString();
}
private static List<Run> toList( SearchResponse response ) {
ArrayList<Run> list = new ArrayList<Run>();
if( response.getHits().getTotalHits() == 0 ) {
return list;
}
for ( SearchHit hit : response.getHits().hits() ) {
list.add( toRun( hit ) );
}
return list;
}
/**
* @return All registered Runs in elastic search
*/
public List<Run> getAll() {
SearchResponse response = getRequest( DAO_INDEX_KEY, DAO_TYPE_KEY )
.setSize( MAX_RESULT_SIZE )
.execute().actionGet();
return toList( response );
}
/**
* Narrows the registered Runs list to ones that match given commitId and testName
*
* @param commitId
* @param testName
* @return
*/
public List<Run> getList( String commitId, String testName ) {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must( termQuery( "testName", testName.toLowerCase() ) )
.must( termQuery( "commitId", commitId.toLowerCase() ) );
SearchResponse response = getRequest( DAO_INDEX_KEY, DAO_TYPE_KEY )
.setQuery( queryBuilder )
.setSize( MAX_RESULT_SIZE )
.execute().actionGet();
return toList( response );
}
/**
* Narrows the registered Runs list to ones that match given commitId and runNumber
*
* @param commitId
* @param runNumber
* @return
*/
public List<Run> getList( String commitId, int runNumber ) {
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
.must( termQuery( "commitId", commitId.toLowerCase() ) )
.must( termQuery( "runNumber", runNumber ) );
SearchResponse response = getRequest( DAO_INDEX_KEY, DAO_TYPE_KEY )
.setQuery( queryBuilder )
.setSize( MAX_RESULT_SIZE )
.execute()
.actionGet();
return toList( response );
}
/**
* @param commits
* @param testName
* @return
*/
public List<Run> getList( List<Commit> commits, String testName ) {
String commitIds = concatIds( commits );
LOG.debug( "commitIds: {}; testName: {}", commitIds, testName );
SearchResponse response = getRequest( DAO_INDEX_KEY, DAO_TYPE_KEY )
.setQuery( multiMatchQuery( commitIds, "commitId" ) )
.setQuery( termQuery( "testName", testName.toLowerCase() ) )
.setSize( MAX_RESULT_SIZE )
.execute()
.actionGet();
List<Run> runs = toList( response );
LOG.debug( "runs found: {}", runs.size() );
return runs;
}
/**
* Gets the runs given runner has run for given commit.
*
* @param runner Runner hostname
* @param commitId Runs return will be the runs for this commitId
* @return List of runs of this runner
*/
public List<Run> getRuns( String runner, String commitId ) {
List<Run> runnerRuns = new ArrayList<Run>();
List<Run> runs = getAll();
for( Run run: runs ) {
if( runner.equals( run.getRunner() ) && commitId.equals( run.getCommitId() ) ) {
runnerRuns.add( run );
}
}
return runnerRuns;
}
/**
* @param commitId commit id of the Run
* @return collection of runs for the given commitId
*/
public Collection<Run> getRuns( String commitId ) {
SearchResponse response = getRequest( DAO_INDEX_KEY, DAO_TYPE_KEY )
.setQuery( termQuery( "commitId", commitId.toLowerCase() ) )
.setSize( MAX_RESULT_SIZE )
.execute()
.actionGet();
Collection<Run> runs = new LinkedList<Run>();
for ( SearchHit hit : response.getHits().hits() ) {
runs.add( toRun( hit ) );
}
return runs;
}
/**
*
* @param commits
* @return
*/
public Set<String> getTestNames( List<Commit> commits ) {
String commitIds = StringUtils.join( commits, ' ' );
SearchResponse response = getRequest( DAO_INDEX_KEY, DAO_TYPE_KEY )
.setQuery( multiMatchQuery( commitIds, "commitId" ) )
.setSize( MAX_RESULT_SIZE )
.execute()
.actionGet();
HashSet<String> names = new HashSet<String>();
for ( SearchHit hit : response.getHits().hits() ) {
names.add( Util.getString( hit.getSource(), "testName" ) );
}
return names;
}
/**
* Gets the stored runs for the given commit Id
* and returns +1 of the maximum runNumber in all found runs.
* <p>
* If no runs for this commitId could be found, this automatically is 1.
*
*
* @param commitId commit id of the Run
* @return next run number for the tests for this commitId
*/
public int getNextRunNumber( String commitId ) {
Collection<Run> runs = getRuns( commitId );
int maxRunNumber = 0;
for( Run run: runs ) {
if( run.getRunNumber() > maxRunNumber ) {
maxRunNumber = run.getRunNumber();
}
}
return maxRunNumber + 1;
}
}