1 /* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.caliper.runner; 18 19 import static java.util.logging.Level.SEVERE; 20 import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; 21 22 import com.google.caliper.api.ResultProcessor; 23 import com.google.caliper.config.InvalidConfigurationException; 24 import com.google.caliper.config.ResultProcessorConfig; 25 import com.google.caliper.model.Trial; 26 import com.google.common.base.Optional; 27 import com.google.common.base.Strings; 28 import com.google.common.collect.ImmutableList; 29 import com.google.gson.Gson; 30 31 import com.sun.jersey.api.client.Client; 32 import com.sun.jersey.api.client.ClientHandlerException; 33 import com.sun.jersey.api.client.UniformInterfaceException; 34 import com.sun.jersey.api.client.WebResource; 35 36 import java.io.PrintWriter; 37 import java.net.URI; 38 import java.net.URISyntaxException; 39 import java.util.UUID; 40 import java.util.logging.Logger; 41 42 import javax.annotation.Nullable; 43 44 /** 45 * {@link ResultProcessor} implementation that uploads the JSON-serialized results to the Caliper 46 * webapp. 47 */ 48 abstract class ResultsUploader implements ResultProcessor { 49 private static final Logger logger = Logger.getLogger(ResultsUploader.class.getName()); 50 private static final String POST_PATH = "/data/trials"; 51 private static final String RESULTS_PATH_PATTERN = "/runs/%s"; 52 53 private final PrintWriter stdout; 54 private final Client client; 55 private final Gson gson; 56 private final Optional<UUID> apiKey; 57 private final Optional<URI> uploadUri; 58 private Optional<UUID> runId = Optional.absent(); 59 private boolean failure = false; 60 61 ResultsUploader(PrintWriter stdout, Gson gson, Client client, 62 ResultProcessorConfig resultProcessorConfig) throws InvalidConfigurationException { 63 this.stdout = stdout; 64 this.client = client; 65 this.gson = gson; 66 @Nullable String apiKeyString = resultProcessorConfig.options().get("key"); 67 Optional<UUID> apiKey = Optional.absent(); 68 if (Strings.isNullOrEmpty(apiKeyString)) { 69 logger.info("No api key specified. Uploading results anonymously."); 70 } else { 71 try { 72 apiKey = Optional.of(UUID.fromString(apiKeyString)); 73 } catch (IllegalArgumentException e) { 74 throw new InvalidConfigurationException(String.format( 75 "The specified API key (%s) is not valid. API keys are UUIDs and should look like %s.", 76 apiKeyString, new UUID(0L, 0L))); 77 } 78 } 79 this.apiKey = apiKey; 80 81 @Nullable String urlString = resultProcessorConfig.options().get("url"); 82 if (Strings.isNullOrEmpty(urlString)) { 83 logger.info("No upload URL was specified. Results will not be uploaded."); 84 this.uploadUri = Optional.absent(); 85 } else { 86 try { 87 this.uploadUri = Optional.of(new URI(urlString).resolve(POST_PATH)); 88 } catch (URISyntaxException e) { 89 throw new InvalidConfigurationException(urlString + " is an invalid upload url", e); 90 } 91 } 92 } 93 94 @Override public final void processTrial(Trial trial) { 95 if (uploadUri.isPresent()) { 96 WebResource resource = client.resource(uploadUri.get()); 97 if (apiKey.isPresent()) { 98 resource = resource.queryParam("key", apiKey.get().toString()); 99 } 100 boolean threw = true; 101 try { 102 // TODO(gak): make the json part happen automagically 103 resource.type(APPLICATION_JSON_TYPE).post(gson.toJson(ImmutableList.of(trial))); 104 // only set the run id if a result has been successfully uploaded 105 runId = Optional.of(trial.run().id()); 106 threw = false; 107 } catch (ClientHandlerException e) { 108 logUploadFailure(trial, e); 109 } catch (UniformInterfaceException e) { 110 logUploadFailure(trial, e); 111 logger.fine("Failed upload response: " + e.getResponse().getStatus()); 112 } finally { 113 failure |= threw; 114 } 115 } 116 } 117 118 private static void logUploadFailure(Trial trial, Exception e) { 119 logger.log(SEVERE, String.format( 120 "Could not upload trial %s. Consider uploading it manually.", trial.id()), e); 121 } 122 123 @Override public final void close() { 124 if (uploadUri.isPresent()) { 125 if (runId.isPresent()) { 126 stdout.printf("Results have been uploaded. View them at: %s%n", 127 uploadUri.get().resolve(String.format(RESULTS_PATH_PATTERN, runId.get()))); 128 } 129 if (failure) { 130 // TODO(gak): implement some retry 131 stdout.println("Some trials failed to upload. Consider uploading them manually."); 132 } 133 } else { 134 logger.fine("No upload URL was provided, so results were not uploaded."); 135 } 136 } 137 } 138