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

org.helmetsrequired.jacocotogo.JaCoCoToGo Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2013 Matthew C. Jenkins ([email protected])
 *
 * 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 org.helmetsrequired.jacocotogo;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.jacoco.core.data.ExecFileLoader;
import org.jacoco.core.data.ExecutionDataWriter;
import org.jacoco.core.runtime.RemoteControlReader;
import org.jacoco.core.runtime.RemoteControlWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

* JaCoCoToGo class.

* * @author Matthew C. Jenkins */ public class JaCoCoToGo { private static final Logger logger = LoggerFactory.getLogger(JaCoCoToGo.class); private static final String JMX_CREDENTIALS_KEY = "jmx.remote.credentials"; private static final int MAX_PORT = (int) (Math.pow(2, 16) - 1); private static final String JACOCO_OBJECT_NAME_STRING = "org.jacoco:type=Runtime"; private static final String JACOCO_FETCH_METHOD_NAME = "getExecutionData"; /** *

* fetchJaCoCoDataOverJmx.

* * @param serviceUrl a {@link java.lang.String} object representing a * {@link javax.management.remote.JMXServiceURL}. * @param username the username to use for the JMX connection if * authentication is enabled. * @param password the password to use for the JMX connection if * authentication is enabled. * @param outputFile a {@link java.io.File} where the retrieved jacoco data * should be written. * @param resetAfterFetch whether the jacoco data on the remote system * should be reset after fetching. * @throws org.helmetsrequired.jacocotogo.JaCoCoToGoValidationException if * there is a problem with the supplied arguments */ public static final void fetchJaCoCoDataOverJmx(String serviceUrl, String username, String password, File outputFile, boolean resetAfterFetch) throws JaCoCoToGoValidationException { // construct JMX Service URL JMXServiceURL url = constructJMXServiceURL(serviceUrl); // fetch the execution data byte[] executionData = getExecutionDataViaJMX(url, username, password, resetAfterFetch); // save to file saveExecutionData(executionData, outputFile); } /** *

* fetchJaCoCoDataOverTcp.

* * @param hostname the hostname where the remote jvm is running * @param port the port where the JaCoCo java agent TCP Server is listening * @param outputFile a {@link java.io.File} where the retrieved jacoco data * should be written. * @param resetAfterFetch whether the jacoco data on the remote system * should be reset after fetching. * @throws org.helmetsrequired.jacocotogo.JaCoCoToGoValidationException if * there is a problem with the supplied arguments. */ public static final void fetchJaCoCoDataOverTcp(String hostname, int port, File outputFile, boolean resetAfterFetch) throws JaCoCoToGoValidationException { InetAddress hostAddress = checkHostname(hostname); checkPort(port); // fetch the execution data byte[] executionData = getExecutionDataViaJaCoCoTCPServer(hostAddress, port, resetAfterFetch); // save to file saveExecutionData(executionData, outputFile); } private static String[] getCredentials(String username, String password) { return new String[]{username == null ? "" : username, password == null ? "" : password}; } private static void populateEnvironmentMapWithCredentials(Map envMap, String username, String password) { envMap.put(JMX_CREDENTIALS_KEY, getCredentials(username, password)); } private static JMXServiceURL constructJMXServiceURL(String serviceUrl) throws JaCoCoToGoValidationException { logger.debug("Constructing JMXServiceURL from String: '{}'", serviceUrl); try { return new JMXServiceURL(serviceUrl); } catch (MalformedURLException ex) { throw new JaCoCoToGoValidationException("Could not create JMXServiceURL", ex); } } private static JMXConnector constructJMXConnector(JMXServiceURL url, Map envMap) throws IOException { logger.debug("Constructing JMXConnector for JMXServiceURL: '{}'", url); return JMXConnectorFactory.newJMXConnector(url, envMap); } private static ObjectName constructJaCoCoObjectName() throws JaCoCoToGoValidationException { logger.debug("Constructing JMX ObjectName for JaCoCo MBean, using String: '{}'", JACOCO_OBJECT_NAME_STRING); try { return new ObjectName(JACOCO_OBJECT_NAME_STRING); } catch (MalformedObjectNameException ex) { throw new JaCoCoToGoValidationException("Unable to create ObjectName for JaCoCo MBean", ex); } } private static void saveExecutionData(byte[] executionData, File outputFile) { logger.info("Saving JaCoCo execution data to file: '{}'", outputFile.getAbsolutePath()); if (outputFile.exists()) { throw new JaCoCoToGoException("outputFile '" + outputFile.getAbsolutePath() + "' already exists."); } File outputFileDir = outputFile.getAbsoluteFile().getParentFile(); if (!outputFileDir.exists()) { if (!outputFileDir.mkdirs()) { throw new IllegalArgumentException("Failed to create directory: '" + outputFileDir.getAbsolutePath() + "'"); } } if (executionData == null) { logger.warn("executionData is null, nothing to save"); return; } FileOutputStream fos = null; BufferedOutputStream bos = null; try { fos = new FileOutputStream(outputFile); bos = new BufferedOutputStream(fos); bos.write(executionData); bos.flush(); } catch (IOException ex) { throw new JaCoCoToGoException("Error saving execution data to file: " + outputFile.getAbsolutePath(), ex); } finally { if (fos != null) { try { fos.close(); } catch (IOException ex) { // bummer } } if (bos != null) { try { bos.close(); } catch (IOException ex) { // bummer } } } } /** * * @param url a {@link javax.management.remote.JMXServiceURL} where the JMX * service is running * @param username the username to use for the JMX connection if * authentication is enabled. * @param password the password to use for the JMX connection if * authentication is enabled. * @param resetAfterFetch whether the JaCoCo data on the remote system * should be reset after fetching. * @return byte array containing the JaCoCo execution data * @throws JaCoCoToGoValidationException if there is a problem with the * supplied arguments. */ private static byte[] getExecutionDataViaJMX(JMXServiceURL url, String username, String password, boolean resetAfterFetch) throws JaCoCoToGoValidationException { try { Map envMap = new HashMap(); populateEnvironmentMapWithCredentials(envMap, username, password); JMXConnector connector = constructJMXConnector(url, envMap); connector.connect(); MBeanServerConnection connection = connector.getMBeanServerConnection(); ObjectName objectName = constructJaCoCoObjectName(); logger.info("Invoking method: '{}' on ObjectName: {}", JACOCO_FETCH_METHOD_NAME, objectName); Object result = connection.invoke(objectName, JACOCO_FETCH_METHOD_NAME, new Object[]{resetAfterFetch}, new String[]{boolean.class.getName()}); try { byte[] data = (byte[]) result; logger.debug("{} bytes of JaCoCo execution data received", data.length); return data; } catch (ClassCastException ex) { throw new JaCoCoToGoException("Expected byte[] but got " + result.getClass().getName(), ex); } } catch (InstanceNotFoundException ex) { throw new JaCoCoToGoException("Could not find JaCoCo MBean at JMXServiceURL: '" + url + "'", ex); } catch (MBeanException ex) { throw new JaCoCoToGoException("Error fetching execution data from JaCoCo MBean at JMXServiceURL: '" + url + "'", ex); } catch (ReflectionException ex) { throw new JaCoCoToGoException("Error fetching execution data from JaCoCo MBean at JMXServiceURL: '" + url + "'", ex); } catch (IOException ex) { throw new JaCoCoToGoException("IOException while communicating with JMXServiceURL: '" + url + "'", ex); } } private static InetAddress checkHostname(String hostname) throws JaCoCoToGoValidationException { try { logger.debug("Verifying that hostname: '{}' can be resolved.", hostname); return InetAddress.getByName(hostname); } catch (UnknownHostException ex) { throw new JaCoCoToGoValidationException("Unable to resolve hostname: '" + hostname + "'", ex); } } private static void checkPort(int port) throws JaCoCoToGoValidationException { if (port < 1 || port > MAX_PORT) { throw new JaCoCoToGoValidationException("Invalid port: '" + port + "'"); } } /** * * @param hostname the hostname where the remote jvm is running. * @param port the port where the JaCoCo Java Agent TCP Server is listening. * @param resetAfterFetch whether JaCoCo coverage data should be reset after * fetch * @return a byte array containing the JaCoCo execution data. */ private static byte[] getExecutionDataViaJaCoCoTCPServer(InetAddress address, int port, boolean resetAfterFetch) throws JaCoCoToGoValidationException { ByteArrayOutputStream output = null; Socket socket = null; try { // 1. Open socket connection socket = new Socket(address, port); logger.info("Connecting to {}", socket.getRemoteSocketAddress()); RemoteControlWriter remoteWriter = new RemoteControlWriter(socket.getOutputStream()); RemoteControlReader remoteReader = new RemoteControlReader(socket.getInputStream()); output = new ByteArrayOutputStream(); ExecutionDataWriter outputWriter = new ExecutionDataWriter(output); remoteReader.setSessionInfoVisitor(outputWriter); remoteReader.setExecutionDataVisitor(outputWriter); // 2. Request dump remoteWriter.visitDumpCommand(true, resetAfterFetch); remoteReader.read(); // 3. verify valid JaCoCo execution data byte[] outputBytes = output.toByteArray(); if (outputBytes.length <= 5) { throw new JaCoCoToGoException("No JaCoCo execution data received."); } // 4. Return data return outputBytes; } catch (final IOException e) { throw new JaCoCoToGoException("Unable to dump coverage data", e); } finally { if (socket != null) { try { socket.close(); } catch (IOException ex) { // bummer } } if (output != null) { try { output.close(); } catch (IOException ex) { // bummer } } } } /** *

* mergeJaCoCoData.

* * Combines the specified inputFiles into a single merged file. * * @param inputFiles a {@link java.util.List} of JaCoCo execution data files to merge. * @param mergeFile the {@link java.io.File} where merged data should be written */ public static void mergeJaCoCoData(List inputFiles, File mergeFile) { // check the mergeFile if (mergeFile == null) { throw new IllegalArgumentException("mergeFile is null"); } if (mergeFile.exists()) { throw new JaCoCoToGoException("File already exists: '" + mergeFile.getAbsolutePath()); } File mergeFileDir = mergeFile.getAbsoluteFile().getParentFile(); if (! mergeFileDir.exists()) { if (!mergeFileDir.mkdirs()) { throw new JaCoCoToGoException("Error creating directory: '" + mergeFileDir.getAbsolutePath() + "'"); } } // load data from each file ExecFileLoader execFileLoader = new ExecFileLoader(); for (File inputFile : inputFiles) { try { logger.debug("Loading data from input file: '" + inputFile.getAbsolutePath() + "'"); execFileLoader.load(inputFile); } catch (IOException ex) { throw new JaCoCoToGoException("Error loading data from file: '" + inputFile.getAbsolutePath() + "'"); } } FileOutputStream fos = null; BufferedOutputStream bos = null; logger.info("Writing merged data to '" + mergeFile.getAbsolutePath() + "'"); ExecutionDataWriter executionDataWriter; try { fos = new FileOutputStream(mergeFile); bos = new BufferedOutputStream(fos); executionDataWriter = new ExecutionDataWriter(bos); execFileLoader.getSessionInfoStore().accept(executionDataWriter); execFileLoader.getExecutionDataStore().accept(executionDataWriter); } catch (IOException ex) { throw new JaCoCoToGoException("Error saving merged execution data to file: " + mergeFile.getAbsolutePath(), ex); } finally { if (fos != null) { try { fos.close(); } catch (IOException ex) { // bummer } } if (bos != null) { try { bos.close(); } catch (IOException ex) { // bummer } } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy