All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.meltmedia.cadmium.cli.HistoryCommand Maven / Gradle / Ivy

/**
 *    Copyright 2012 meltmedia
 *
 *    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 com.meltmedia.cadmium.cli;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import com.meltmedia.cadmium.core.history.HistoryEntry;

/**
 * Retrieves and displays the history of the requested Cadmium site in a human readable format.
 * 
 * @author John McEntire
 *
 */
@Parameters(commandDescription = "Lists history for a given site in human readable form.", separators="=")
public class HistoryCommand extends AbstractAuthorizedOnly implements CliCommand {
  private static final Pattern URI_PATTERN = Pattern.compile("^(http[s]{0,1}://.*)$");

  @Parameter(names="-n", description="Limits number of history items returned.", required=false)
  private Integer limit = -1;
  
  @Parameter(names="-r", description="Filters out non revertible history items from list.", required=false)
  private boolean filter = false;
  
  @Parameter(description="Site", required=true)
  private List site;
  
  public void execute() throws Exception {
    String siteUri = getSecureBaseUrl(site.get(0));
    Matcher siteMatcher = URI_PATTERN.matcher(siteUri);
    if(siteMatcher.matches()) {
      siteUri = siteMatcher.group(1);
      
      System.out.println("Showing history for "+siteUri+":");
      
      List history = getHistory(siteUri, limit, filter, token);
            
      displayHistory(history, false, null);
    } else {
      System.err.println("Invalid value for site parameter!");
      System.exit(1);
    }
  }

  /**
   * Displays a list of {@link HistoryEntry} objects.
   * @param history The list of entries to display.
   * @param filter If true then only revertable history entries will be displayed.
   * @param limitHistory If set this will limit the number of history entries to display.
   */
  public static void displayHistory(List history, boolean filter, Integer limitHistory) {
    if(history != null && history.size() > 0) {
      System.out.format("%7s|%7s|%12s|%7s|%14s|%52s|%18s|%42s|%24s|%6s|%6s|%6s|%6s\n", "Index", "Type", "Date", "Time", "User", "Repository", "Branch", "Revision", "Time Live", "Maint", "Revert", "Done", "Fail");
      for(int i=0; i<218; i++) {
        System.out.print("-");
      }
      System.out.println();
      boolean showing = false;
      for(HistoryEntry entry : history) {
        if(!filter || entry.isRevertible()) {
          if(limitHistory == null || limitHistory-- > 0) {
            showing = true;
            System.out.format("%7d|%7s|%4tm/%<2td/%<4tY|%<4tH:%<2tM|%14s|%52s|%18s|%42s|%24s|%6b|%6b|%6s|%6s\n",
                entry.getIndex(),
                entry.getType(),
                entry.getTimestamp(),
                entry.getOpenId(),
                entry.getRepoUrl(),
                entry.getBranch(),
                entry.getRevision(),
                formatTimeLive(entry.getTimeLive() == 0 ? System.currentTimeMillis() - entry.getTimestamp().getTime() : entry.getTimeLive()),
                entry.isMaintenance(),
                entry.isRevertible(),
                entry.isFinished(),
                entry.isFailed());
            printComments(entry.getComment());
          }
        }
      }
      if(!showing) {
        System.out.println("No history to show");
      }
    } else {
      System.out.println("No history to show");
    }
  }
  
  /**
   * Waits until a timeout is reached or a token shows up in the history of a site as finished or failed.
   * 
   * @param siteUri The uri to a cadmium site.
   * @param token The token that represents a history event to wait for.
   * @param since A timestamp to pass on the the cadmium site to set a limit on how far back to check the history for a token.
   * @param timeout The timeout in milliseconds to wait for if the token never shows up or fails in the sites log.
   * @throws Exception
   */
  public static void waitForToken(String siteUri, String token, Long since, Long timeout) throws Exception {

    if(!siteUri.endsWith("/system/history")) {
      siteUri += "/system/history";
    }
    siteUri += "/" + token;
    if(since != null) {
      siteUri += "/" + since;
    }
    
    HttpClient httpClient = setTrustAllSSLCerts(new DefaultHttpClient());
    HttpGet get = new HttpGet(siteUri);
    Long currentTime = System.currentTimeMillis();
    Long timeoutTime = currentTime + timeout;
    do {
      currentTime = System.currentTimeMillis();
      
      HttpResponse resp = httpClient.execute(get);
      if(resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
        String response = EntityUtils.toString(resp.getEntity());
        if(response != null && response.trim().equalsIgnoreCase("true")) {
          return;
        } else {
          Thread.sleep(1000l);
        }
      } else {
        String errorResponse = EntityUtils.toString(resp.getEntity());
        if(errorResponse != null) {
          throw new Exception(errorResponse.trim());
        } else {
          throw new Exception("Command failed!");
        }
      }
    } while(currentTime < timeoutTime);
    if(currentTime >= timeoutTime) {
      throw new Exception("Timed out waiting for command to complete!");
    }
  }

  /**
   * Retrieves the history of a Cadmium site.
   * 
   * @param siteUri The uri of a cadmium site.
   * @param limit The maximum number of history entries to retrieve or if set to -1 tells the site to retrieve all history.
   * @param filter If true filters out the non revertable history entries.
   * @param token The Github API token to pass to the Cadmium site for authentication.
   * 
   * @return A list of {@link HistoryEntry} Objects that are populated with the history returned from the Cadmium site.
   * 
   * @throws URISyntaxException
   * @throws IOException
   * @throws ClientProtocolException
   * @throws Exception
   */
  public static List getHistory(String siteUri, int limit, boolean filter, String token)
      throws URISyntaxException, IOException, ClientProtocolException, Exception {

    if(!siteUri.endsWith("/system/history")) {
      siteUri += "/system/history";
    }
    
    List history = null;
    
    HttpClient httpClient = setTrustAllSSLCerts(new DefaultHttpClient());
    HttpGet get = null;
    try {
      URIBuilder uriBuilder = new URIBuilder(siteUri);
      if(limit > 0) {
        uriBuilder.addParameter("limit", limit+"");
      }
      if(filter) {
        uriBuilder.addParameter("filter", filter+"");
      }
      URI uri = uriBuilder.build();
      get = new HttpGet(uri);
      addAuthHeader(token, get);
      
      HttpResponse resp = httpClient.execute(get);
      if(resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
        HttpEntity entity = resp.getEntity();
        if(entity.getContentType().getValue().equals("application/json")) {
          String responseContent = EntityUtils.toString(entity);
          Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new JsonDeserializer() {

            @Override
            public Date deserialize(JsonElement json, Type typeOfT,
                JsonDeserializationContext ctx) throws JsonParseException {
              return new Date(json.getAsLong());
            }
            
          }).create();
          history = gson.fromJson(responseContent, new TypeToken>() {}.getType());
        } else {
          System.err.println("Invalid response content type ["+entity.getContentType().getValue()+"]");
          System.exit(1);
        }
      } else {
        System.err.println("Request failed due to a ["+resp.getStatusLine().getStatusCode()+":"+resp.getStatusLine().getReasonPhrase()+"] response from the remote server.");
        System.exit(1);
      }
    } finally {
      if(get != null) {
        get.releaseConnection();
      }
    }
    return history;
  }

  /**
   * Helper method to format comments to standard out.
   * 
   * @param comment
   */
  private static void printComments(String comment) {
    int index = 0;
    int nextIndex = 154;
    while(index < comment.length()) {
      nextIndex = nextIndex <= comment.length() ? nextIndex : comment.length();
      String commentSegment = comment.substring(index, nextIndex);
      int lastSpace = commentSegment.lastIndexOf(' ');
      int lastNewLine = commentSegment.indexOf('\n');
      char lastChar = ' ';
      if(nextIndex < comment.length() ) {
        lastChar = comment.charAt(nextIndex);
      }
      if(lastNewLine > 0) {
        nextIndex = index + lastNewLine;
        commentSegment = comment.substring(index, nextIndex);
      } else
      if(Character.isWhitespace(lastChar)) {
        
      } else
      if(lastSpace > 0) {
        nextIndex = index + lastSpace;
        commentSegment = comment.substring(index, nextIndex);
      }
      System.out.println("  " + commentSegment);
      index = nextIndex;
      if(lastNewLine > 0 || lastSpace > 0) {
        index++;
      }
      nextIndex = index + 154;
    }
  }

  /**
   * Helper method to format a timestamp.
   * 
   * @param timeLive
   * @return
   */
  private static String formatTimeLive(long timeLive) {
    String timeString = "ms";
    timeString = (timeLive % 1000) + timeString;
    timeLive = timeLive / 1000;
    if(timeLive > 0) {
      timeString = (timeLive % 60) + "s" + timeString;
      timeLive = timeLive / 60;
      if(timeLive > 0) {
        timeString = (timeLive % 60) + "m" + timeString;
        timeLive = timeLive / 60;
        if(timeLive > 0) {
          timeString = (timeLive % 24) + "h" + timeString;
          timeLive = timeLive / 24;
          if(timeLive > 0) {
            timeString = (timeLive) + "d" + timeString;
          }
        }
      }
    }
    return timeString;
  }

  @Override
  public String getCommandName() {
    return "history";
  }
  
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy