/* * The MIT License * * Copyright (c) 2010, Stellar Science Ltd Co, K. R. Walker * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package hudson.plugins.measurement_plots; /** * Resolves TestActions for the given TestObject. * This object itself is persisted as a part of AbstractBuild, so it needs to be XStream-serializable. * * @author krwalker */ class TestActionResolver extends hudson.tasks.junit.TestResultAction.Data { private static final java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(TestActionResolver.class.getName()); private static final com.thoughtworks.xstream.XStream XSTREAM = new hudson.util.XStream2(); // Convert BuildMeasurements to XML in a more friendly way. private static class BuildMeasurementsConverter extends com.thoughtworks.xstream.converters.collections.MapConverter { BuildMeasurementsConverter(com.thoughtworks.xstream.mapper.Mapper mapper) { super(mapper); } @Override public boolean canConvert(Class type) { return type.equals(BuildMeasurements.class); } } // Convert TestObjectMeasurements to XML in a more friendly way. private static class TestObjectMeasurementsConverter extends com.thoughtworks.xstream.converters.collections.CollectionConverter { TestObjectMeasurementsConverter(com.thoughtworks.xstream.mapper.Mapper mapper) { super(mapper); } @Override public boolean canConvert(Class type) { return type.equals(TestObjectMeasurements.class); } } // Convert TestObjectIds to XML in a more friendly way. private static class TestObjectIdConverter extends com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter { @Override public boolean canConvert(Class type) { return type.equals(TestObjectId.class); } @Override public Object fromString(String string) { return TestObjectId.fromString(string); } } static { XSTREAM.registerConverter(new BuildMeasurementsConverter(XSTREAM.getMapper())); XSTREAM.alias("build-measurements", BuildMeasurements.class); XSTREAM.alias("test-object", java.util.Map.Entry.class); XSTREAM.registerConverter(new TestObjectIdConverter()); XSTREAM.alias("id", TestObjectId.class); XSTREAM.registerConverter(new TestObjectMeasurementsConverter(XSTREAM.getMapper())); XSTREAM.alias("measurements", TestObjectMeasurements.class); XSTREAM.alias("measurement", Measurement.class); // Is this needed? XSTREAM.registerConverter(new hudson.util.HeapSpaceStringConverter(),100); } // Store a weak reference to the build measurements. They are stored in // their own file (measurement-plots.xml) and loaded on demand. private transient java.lang.ref.WeakReference<BuildMeasurements> weakBuildMeasurements; // The build used during both the load and save, and may be set by // the constructor or getTestAction(). private transient hudson.model.AbstractBuild<?, ?> build; // The listener is used only during the save. It is set during the constructor. private transient final hudson.model.BuildListener listener; TestActionResolver(final BuildMeasurements buildMeasurements, hudson.model.AbstractBuild<?, ?> build, hudson.model.BuildListener listener) { this.build = build; this.listener = listener; setBuildMeasurements(buildMeasurements, listener); } @Override public java.util.List<hudson.tasks.junit.TestAction> getTestAction( @SuppressWarnings({"deprecation"}) hudson.tasks.junit.TestObject junitTestObject) { if (build == null) { build = junitTestObject.getOwner(); } TestObjectMeasurements testObjectMeasurements = getBuildMeasurements().get(TestObjectId.fromString(junitTestObject.getId())); if (testObjectMeasurements != null && !testObjectMeasurements.isEmpty()) { // This cast should always succeed. hudson.tasks.test.TestObject testObject = (hudson.tasks.test.TestObject)junitTestObject; return java.util.Collections.<hudson.tasks.junit.TestAction> singletonList( new TestAction(testObject, testObjectMeasurements)); } return java.util.Collections.emptyList(); } public synchronized BuildMeasurements getBuildMeasurements() { BuildMeasurements buildMeasurements; if(weakBuildMeasurements == null) { buildMeasurements = load(); weakBuildMeasurements = new java.lang.ref.WeakReference<BuildMeasurements>(buildMeasurements); } else { buildMeasurements = weakBuildMeasurements.get(); } if(buildMeasurements == null) { buildMeasurements = load(); weakBuildMeasurements = new java.lang.ref.WeakReference<BuildMeasurements>(buildMeasurements); } return buildMeasurements; } /** * Overwrites the {@link BuildMeasurements} by a new data set. */ public synchronized void setBuildMeasurements(BuildMeasurements buildMeasurements, hudson.model.BuildListener listener) { // persist the data try { getDataFile().write(buildMeasurements); } catch (java.io.IOException exception) { exception.printStackTrace(listener.fatalError("Failed to save build measurements")); } this.weakBuildMeasurements = new java.lang.ref.WeakReference<BuildMeasurements>(buildMeasurements); } private hudson.XmlFile getDataFile() { return new hudson.XmlFile(XSTREAM, new java.io.File(build.getRootDir(), "measurement-plots.xml")); } /** * Loads a {@link BuildMeasurements} from disk. */ private BuildMeasurements load() { BuildMeasurements loadedMeasurements; try { loadedMeasurements = (BuildMeasurements)getDataFile().read(); } catch (java.io.IOException exception) { LOGGER.log(java.util.logging.Level.WARNING, "Failed to load " + getDataFile(), exception); loadedMeasurements = new BuildMeasurements(); // return a dummy } return loadedMeasurements; } }