/* * Copyright 2014-2015. Adaptive.me. * * 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 me.adaptive.che.plugin.server.builder; import me.adaptive.core.data.domain.BuildRequestEntity; import me.adaptive.infra.client.ApiClient; import org.eclipse.che.api.builder.BuildStatus; import org.eclipse.che.api.builder.internal.BuildLogger; import org.eclipse.che.api.builder.internal.BuilderEvent; import org.eclipse.che.api.core.notification.EventService; import org.slf4j.LoggerFactory; import retrofit.RetrofitError; import retrofit.client.Response; import java.io.*; import java.nio.file.Files; import java.util.concurrent.*; /** * The Builder Looger for adaptive builds. * <p> * Created by panthro on 08/07/15. */ public class AdaptiveBuilderLogger implements BuildLogger { private ApiClient apiClient; private BuildRequestEntity requestEntity; private ExecutorService executor = Executors.newSingleThreadExecutor(); private File myFile; private Future<Boolean> populatorTask; private EventService eventService; /** * Constructor * * @param requestEntity the requestEntity * @param builder the adaptiveBuilder * @param apiClient an already authenticated apiClient */ public AdaptiveBuilderLogger(BuildRequestEntity requestEntity, AdaptiveBuilder builder, ApiClient apiClient, EventService eventService) { this.requestEntity = requestEntity; File root = builder.getBuildsRoot(requestEntity.getWorkspace().getWorkspaceId(), requestEntity.getProjectName(), requestEntity.getId()); myFile = new File(root, builder.getBuildLogName()); this.apiClient = apiClient; this.eventService = eventService; //TODO find a way to populate the log as it happens or somehow have a better Reader() populatorTask = executor.submit(new FileLogPopulator()); } /** * the Reader instance that will be used to print the logs to the clients * TODO need to figure out a way to make this reder work during the build * * @return the Reader instance * @throws IOException case the reader cannot be created */ @Override public Reader getReader() throws IOException { //writeLine("[WARNING] Could not get logs from builder"); //writeLine("[WARNING] This does not means your build has failed, check the status."); return Files.newBufferedReader(myFile.toPath()); } @Override public String getContentType() { return "text/plain"; } @Override public File getFile() { return myFile; } @Override public void writeLine(String line) throws IOException { //Shouldnt write anything actually } @Override public void close() throws IOException { executor.shutdown(); try { if (!populatorTask.isDone()) { populatorTask.get(); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } /** * Used to populate the log file while the build process is still running */ private class FileLogPopulator implements Callable<Boolean> { BuildStatus status = BuildStatus.IN_QUEUE; int lastReadLine; @Override public Boolean call() throws Exception { while (status.equals(BuildStatus.IN_QUEUE) || status.equals(BuildStatus.IN_PROGRESS)) { if (BuildStatus.IN_PROGRESS.equals(status)) { readAndWriteLines(); } Thread.sleep(500); updateStatus(); } if (status.equals(BuildStatus.SUCCESSFUL) || status.equals(BuildStatus.FAILED)) { readAndWriteLines(); } return true; } /** * Read the lines from the API and write to the log file. * * @throws IOException */ private void readAndWriteLines() throws IOException { try { Response response = apiClient.getBuilderApi().logs(requestEntity.getId(), lastReadLine); BufferedReader reader = new BufferedReader(new InputStreamReader(response.getBody().in())); for (String line = reader.readLine(); line != null; line = reader.readLine()) { eventService.publish(BuilderEvent.messageLoggedEvent(requestEntity.getId(), requestEntity.getWorkspace().getWorkspaceId(), requestEntity.getProjectName(), new BuilderEvent.LoggedMessage(line, lastReadLine))); lastReadLine++; writeLine(line); } } catch (Exception e) { if (e instanceof RetrofitError) { if (((RetrofitError) e).getResponse().getStatus() < 400) { //log errors only if response wasn't bad LoggerFactory.getLogger(AdaptiveBuilderLogger.class).info("Could not get update status from remote builder", e); } } } } /** * update the status from the API */ private void updateStatus() { try { status = BuildStatus.valueOf(apiClient.getBuilderApi().status(requestEntity.getId())); } catch (Exception e) { if (e instanceof RetrofitError) { if (((RetrofitError) e).getResponse().getStatus() < 400) { //log errors only if response wasn't bad LoggerFactory.getLogger(AdaptiveBuilderLogger.class).info("Could not get update status from remote builder", e); } } } } } }