// Copyright 2015 ThoughtWorks, Inc. // This file is part of Gauge-Java. // This program is free software. // // It is dual-licensed under: // 1) the GNU General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version; // or // 2) the Eclipse Public License v1.0. // // You can redistribute it and/or modify it under the terms of either license. // We would then provide copied of each license in a separate .txt file with the name of the license as the title of the file. package com.thoughtworks.gauge.connection; import com.google.protobuf.CodedInputStream; import com.google.protobuf.CodedOutputStream; import com.thoughtworks.gauge.ConceptInfo; import com.thoughtworks.gauge.PluginNotInstalledException; import com.thoughtworks.gauge.StepValue; import gauge.messages.Api; import gauge.messages.Spec; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class GaugeConnection { private static final int GET_STEP_MSG_ID = 2; private static final int GET_INSTALLATION_ROOT_MSG_ID = 3; private static final int GET_STEP_VALUE_MSG_ID = 4; private static final int GET_LIB_PATH_MSG_ID = 5; private static final int GET_CONCEPT_MSG_ID = 6; private static final int PERFORM_REFACTORING_MSG_ID = 7; private static final int EXTRACT_CONCEPT_MSG_ID = 8; private final int port; private Socket gaugeSocket; public GaugeConnection(int port) { this.port = port; createConnection(5); // SUPPRESS CHECKSTYLE } public GaugeConnection(Socket socket) { gaugeSocket = socket; port = socket.getPort(); } private static MessageLength getMessageLength(InputStream is) throws IOException { CodedInputStream codedInputStream = CodedInputStream.newInstance(is); long size = codedInputStream.readRawVarint64(); return new MessageLength(size, codedInputStream); } private static byte[] toBytes(MessageLength messageLength) throws IOException { long messageSize = messageLength.getLength(); CodedInputStream stream = messageLength.getRemainingStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); for (int i = 0; i < messageSize; i++) { outputStream.write(stream.readRawByte()); } return outputStream.toByteArray(); } private void createConnection(int tries) { if (tries == 0) { throw new RuntimeException("Gauge API not started"); } try { gaugeSocket = new Socket("localhost", port); } catch (IOException e) { try { //waits for the process to start accepting connection Thread.sleep(1000); // SUPPRESS CHECKSTYLE } catch (InterruptedException e1) { e1.printStackTrace(); } createConnection(tries - 1); } } /** * Fetches all the steps in the gauge project as a list. * * @return a list of all steps * @throws IOException */ public List<StepValue> fetchAllSteps() throws IOException { Api.APIMessage message = getStepRequest(); Api.APIMessage response = getAPIResponse(message); Api.GetAllStepsResponse allStepsResponse = response.getAllStepsResponse(); List<StepValue> steps = new ArrayList<StepValue>(); for (Spec.ProtoStepValue stepValueResponse : allStepsResponse.getAllStepsList()) { StepValue stepValue = new StepValue(stepValueResponse.getStepValue(), stepValueResponse.getParameterizedStepValue(), stepValueResponse.getParametersList()); steps.add(stepValue); } return steps; } /** * Fetches all the concepts in the Gauge project as a list of ConceptInfos which has details of file location. * * @return a list of all the ConceptInfo * @throws IOException */ public List<ConceptInfo> fetchAllConcepts() throws IOException { Api.APIMessage message = getConceptRequest(); Api.APIMessage response = getAPIResponse(message); Api.GetAllConceptsResponse allConceptsResponse = response.getAllConceptsResponse(); List<ConceptInfo> conceptsInfo = new ArrayList<ConceptInfo>(); for (Api.ConceptInfo conceptInfoResponse : allConceptsResponse.getConceptsList()) { Spec.ProtoStepValue protoStepValue = conceptInfoResponse.getStepValue(); StepValue stepValue = new StepValue(protoStepValue.getStepValue(), protoStepValue.getParameterizedStepValue(), protoStepValue.getParametersList()); ConceptInfo conceptInfo = new ConceptInfo(stepValue, conceptInfoResponse.getFilepath(), conceptInfoResponse.getLineNumber()); conceptsInfo.add(conceptInfo); } return conceptsInfo; } /** * Gets the Absolute path to libs location for the particular language plugin. * * @param language - The language plugin name, eg. java * @return absolute path to libs location * @throws IOException * @throws com.thoughtworks.gauge.PluginNotInstalledException */ public String getLibPath(String language) throws IOException, PluginNotInstalledException { Api.APIMessage message = getLibPathRequest(language); Api.APIMessage response = getAPIResponse(message); if (response.getMessageType().equals(Api.APIMessage.APIMessageType.ErrorResponse)) { throw new PluginNotInstalledException(response.getError().getError()); } return response.getLibPathResponse().getPath(); } private Api.APIMessage getAPIResponse(Api.APIMessage message) throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); CodedOutputStream cos = CodedOutputStream.newInstance(stream); byte[] bytes = message.toByteArray(); cos.writeRawVarint64(bytes.length); cos.flush(); stream.write(bytes); synchronized (gaugeSocket) { gaugeSocket.getOutputStream().write(stream.toByteArray()); gaugeSocket.getOutputStream().flush(); InputStream remoteStream = gaugeSocket.getInputStream(); MessageLength messageLength = getMessageLength(remoteStream); bytes = toBytes(messageLength); } Api.APIMessage apiMessage = Api.APIMessage.parseFrom(bytes); return apiMessage; } /** * Gets the location of gauge installation on the system. * * @return gauge installation file * @throws IOException */ public File getInstallationRoot() throws IOException { Api.APIMessage installationRootRequest = getInstallationRootRequest(); Api.APIMessage response = getAPIResponse(installationRootRequest); Api.GetInstallationRootResponse allStepsResponse = response.getInstallationRootResponse(); File installationRoot = new File(allStepsResponse.getInstallationRoot()); if (installationRoot.exists()) { return installationRoot; } else { throw new IOException(installationRoot + " does not exist"); } } public StepValue getStepValue(String stepText) { return getStepValue(stepText, false); } /** * Gets the step value for a particular step name. * * @param stepText - The name of the step, eg. login as "admin" * @param hasInlineTable - set to true if the step has an inline table parameter * @return value of the given step name */ public StepValue getStepValue(String stepText, boolean hasInlineTable) { Api.APIMessage stepValueRequest = getStepValueRequest(stepText, hasInlineTable); Api.APIMessage response; try { response = getAPIResponse(stepValueRequest); } catch (IOException e) { e.printStackTrace(); return null; } Api.GetStepValueResponse stepValueResponse = response.getStepValueResponse(); Spec.ProtoStepValue protoStepValue = stepValueResponse.getStepValue(); String stepValue = protoStepValue.getStepValue(); List<String> parametersList = protoStepValue.getParametersList(); String parameterizedStepValue = protoStepValue.getParameterizedStepValue(); return new StepValue(stepValue, parameterizedStepValue, parametersList); } /** * Check if gauge connection is still active. * * @return true if connected */ public boolean isConnected() { return gaugeSocket.isConnected(); } /** * Closes the connection. * * @throws IOException - If fails to close the socket */ public void close() throws IOException { gaugeSocket.close(); } /** * Sends a request to gauge to perform a step name refactoring on the project. * * @param oldName old Step Name * @param newName New Step Name * @return RefactoringResponse message * @throws Exception On failure in connecting to gauge core or if invalid message is received. */ public Api.PerformRefactoringResponse sendPerformRefactoringRequest(String oldName, String newName) throws Exception { Api.APIMessage refactoringRequest = createPerformRefactoringRequest(oldName, newName); Api.APIMessage response = getAPIResponse(refactoringRequest); return response.getPerformRefactoringResponse(); } public Api.ExtractConceptResponse sendGetExtractConceptRequest(List<Api.step> steps, Api.step concept, boolean changeAcrossProject, String fileName, Api.textInfo selectedTextInfo) throws Exception { Api.APIMessage request = createExtractConceptRequest(steps, concept, changeAcrossProject, fileName, selectedTextInfo); Api.APIMessage response = getAPIResponse(request); return response.getExtractConceptResponse(); } private Api.APIMessage getStepRequest() { Api.GetAllStepsRequest stepRequest = Api.GetAllStepsRequest.newBuilder().build(); return Api.APIMessage.newBuilder() .setMessageType(Api.APIMessage.APIMessageType.GetAllStepsRequest) .setMessageId(GET_STEP_MSG_ID) .setAllStepsRequest(stepRequest) .build(); } private Api.APIMessage getInstallationRootRequest() { Api.GetInstallationRootRequest installationRootRequest = Api.GetInstallationRootRequest.newBuilder().build(); return Api.APIMessage.newBuilder() .setMessageType(Api.APIMessage.APIMessageType.GetInstallationRootRequest) .setMessageId(GET_INSTALLATION_ROOT_MSG_ID) .setInstallationRootRequest(installationRootRequest) .build(); } private Api.APIMessage getStepValueRequest(String stepText, boolean hasInlineTable) { Api.GetStepValueRequest stepValueRequest = Api.GetStepValueRequest.newBuilder().setStepText(stepText).setHasInlineTable(hasInlineTable).build(); return Api.APIMessage.newBuilder() .setMessageType(Api.APIMessage.APIMessageType.GetStepValueRequest) .setMessageId(GET_STEP_VALUE_MSG_ID) .setStepValueRequest(stepValueRequest) .build(); } private Api.APIMessage getLibPathRequest(String language) { Api.GetLanguagePluginLibPathRequest libPathRequest = Api.GetLanguagePluginLibPathRequest.newBuilder().setLanguage(language).build(); return Api.APIMessage.newBuilder() .setMessageType(Api.APIMessage.APIMessageType.GetLanguagePluginLibPathRequest) .setMessageId(GET_LIB_PATH_MSG_ID) .setLibPathRequest(libPathRequest) .build(); } private Api.APIMessage getConceptRequest() { Api.GetAllConceptsRequest conceptRequest = Api.GetAllConceptsRequest.newBuilder().build(); return Api.APIMessage.newBuilder() .setMessageType(Api.APIMessage.APIMessageType.GetAllConceptsRequest) .setMessageId(GET_CONCEPT_MSG_ID) .setAllConceptsRequest(conceptRequest) .build(); } private Api.APIMessage createPerformRefactoringRequest(String oldName, String newName) { Api.PerformRefactoringRequest performRefactoringRequest = Api.PerformRefactoringRequest.newBuilder() .setOldStep(oldName) .setNewStep(newName) .build(); return Api.APIMessage.newBuilder() .setMessageType(Api.APIMessage.APIMessageType.PerformRefactoringRequest) .setMessageId(PERFORM_REFACTORING_MSG_ID) .setPerformRefactoringRequest(performRefactoringRequest) .build(); } private Api.APIMessage createExtractConceptRequest(List<Api.step> steps, Api.step concept, boolean changeAcrossProject, String fileName, Api.textInfo selectedTextInfo) { Api.ExtractConceptRequest request = Api.ExtractConceptRequest.newBuilder().addAllSteps(steps).setChangeAcrossProject(changeAcrossProject) .setSelectedTextInfo(selectedTextInfo).setConceptFileName(fileName).setConceptName(concept).build(); return Api.APIMessage.newBuilder() .setMessageType(Api.APIMessage.APIMessageType.ExtractConceptRequest) .setMessageId(EXTRACT_CONCEPT_MSG_ID) .setExtractConceptRequest(request) .build(); } }