changeset 0:53cae6c1c352

Initial load
author duke
date Tue, 25 Mar 2014 18:12:53 +0400
parents
children f928322a5e0b
files src/classes/com/sun/tdk/jcov/Agent.java src/classes/com/sun/tdk/jcov/DiffCoverage.java src/classes/com/sun/tdk/jcov/Exec.java src/classes/com/sun/tdk/jcov/Filter.java src/classes/com/sun/tdk/jcov/Grabber.java src/classes/com/sun/tdk/jcov/GrabberManager.java src/classes/com/sun/tdk/jcov/Helper.java src/classes/com/sun/tdk/jcov/Instr.java src/classes/com/sun/tdk/jcov/Instr2.java src/classes/com/sun/tdk/jcov/JCov.java src/classes/com/sun/tdk/jcov/JREInstr.java src/classes/com/sun/tdk/jcov/Merger.java src/classes/com/sun/tdk/jcov/ProductInstr.java src/classes/com/sun/tdk/jcov/RepGen.java src/classes/com/sun/tdk/jcov/RepMerge.java src/classes/com/sun/tdk/jcov/TmplGen.java src/classes/com/sun/tdk/jcov/ant/AllTasks.java src/classes/com/sun/tdk/jcov/ant/AntableSPI.java src/classes/com/sun/tdk/jcov/ant/Grabber.java src/classes/com/sun/tdk/jcov/ant/GrabberManager.java src/classes/com/sun/tdk/jcov/ant/Instrument.java src/classes/com/sun/tdk/jcov/ant/Merge.java src/classes/com/sun/tdk/jcov/ant/Report.java src/classes/com/sun/tdk/jcov/api/API.java src/classes/com/sun/tdk/jcov/api/APIReader.java src/classes/com/sun/tdk/jcov/api/APIWriter.java src/classes/com/sun/tdk/jcov/api/AbstractDescr.java src/classes/com/sun/tdk/jcov/api/Attribute.java src/classes/com/sun/tdk/jcov/api/ClassDescr.java src/classes/com/sun/tdk/jcov/api/FileFormatException.java src/classes/com/sun/tdk/jcov/api/MemberDescr.java src/classes/com/sun/tdk/jcov/constants/InstrConstants.java src/classes/com/sun/tdk/jcov/constants/MiscConstants.java src/classes/com/sun/tdk/jcov/constants/VMConstants.java src/classes/com/sun/tdk/jcov/data/FileFormatException.java src/classes/com/sun/tdk/jcov/data/Result.java src/classes/com/sun/tdk/jcov/data/Scale.java src/classes/com/sun/tdk/jcov/data/ScaleOptions.java src/classes/com/sun/tdk/jcov/filter/AcceptAllSpi.java src/classes/com/sun/tdk/jcov/filter/ConveyerFilter.java src/classes/com/sun/tdk/jcov/filter/FilterFactory.java src/classes/com/sun/tdk/jcov/filter/FilterSpi.java src/classes/com/sun/tdk/jcov/filter/MemberFilter.java src/classes/com/sun/tdk/jcov/filter/package-info.java src/classes/com/sun/tdk/jcov/insert/AbstractUniversalInstrumenter.java src/classes/com/sun/tdk/jcov/instrument/BasicBlock.java src/classes/com/sun/tdk/jcov/instrument/BlockCodeMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/BranchCodeMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/CharacterRangeTableAttribute.java src/classes/com/sun/tdk/jcov/instrument/ClassMorph.java src/classes/com/sun/tdk/jcov/instrument/ClassMorph2.java src/classes/com/sun/tdk/jcov/instrument/Constants.java src/classes/com/sun/tdk/jcov/instrument/DataAbstract.java src/classes/com/sun/tdk/jcov/instrument/DataAnnotated.java src/classes/com/sun/tdk/jcov/instrument/DataBlock.java src/classes/com/sun/tdk/jcov/instrument/DataBlockCatch.java src/classes/com/sun/tdk/jcov/instrument/DataBlockFallThrough.java src/classes/com/sun/tdk/jcov/instrument/DataBlockMethEnter.java src/classes/com/sun/tdk/jcov/instrument/DataBlockTarget.java src/classes/com/sun/tdk/jcov/instrument/DataBlockTargetCase.java src/classes/com/sun/tdk/jcov/instrument/DataBlockTargetCond.java src/classes/com/sun/tdk/jcov/instrument/DataBlockTargetDefault.java src/classes/com/sun/tdk/jcov/instrument/DataBlockTargetGoto.java src/classes/com/sun/tdk/jcov/instrument/DataBranch.java src/classes/com/sun/tdk/jcov/instrument/DataBranchCond.java src/classes/com/sun/tdk/jcov/instrument/DataBranchGoto.java src/classes/com/sun/tdk/jcov/instrument/DataBranchSwitch.java src/classes/com/sun/tdk/jcov/instrument/DataClass.java src/classes/com/sun/tdk/jcov/instrument/DataExit.java src/classes/com/sun/tdk/jcov/instrument/DataExitSimple.java src/classes/com/sun/tdk/jcov/instrument/DataField.java src/classes/com/sun/tdk/jcov/instrument/DataMethod.java src/classes/com/sun/tdk/jcov/instrument/DataMethodEntryOnly.java src/classes/com/sun/tdk/jcov/instrument/DataMethodInvoked.java src/classes/com/sun/tdk/jcov/instrument/DataMethodWithBlocks.java src/classes/com/sun/tdk/jcov/instrument/DataPackage.java src/classes/com/sun/tdk/jcov/instrument/DataRoot.java src/classes/com/sun/tdk/jcov/instrument/DeferringMethodClassAdapter.java src/classes/com/sun/tdk/jcov/instrument/EntryCodeMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/FieldAnnotationVisitor.java src/classes/com/sun/tdk/jcov/instrument/ForkingMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/InstrumentationOptions.java src/classes/com/sun/tdk/jcov/instrument/InstrumentationParams.java src/classes/com/sun/tdk/jcov/instrument/InstrumentedAttributeClassAdapter.java src/classes/com/sun/tdk/jcov/instrument/Instrumenter.java src/classes/com/sun/tdk/jcov/instrument/InvokeClassAdapter.java src/classes/com/sun/tdk/jcov/instrument/InvokeMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/LocationAbstract.java src/classes/com/sun/tdk/jcov/instrument/LocationConcrete.java src/classes/com/sun/tdk/jcov/instrument/LocationRef.java src/classes/com/sun/tdk/jcov/instrument/MergeException.java src/classes/com/sun/tdk/jcov/instrument/MethodAnnotationAdapter.java src/classes/com/sun/tdk/jcov/instrument/NativeWrappingMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/OffsetLabel.java src/classes/com/sun/tdk/jcov/instrument/OffsetLabelingClassReader.java src/classes/com/sun/tdk/jcov/instrument/OffsetRecordingMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/OverriddenClassWriter.java src/classes/com/sun/tdk/jcov/instrument/SavePointsMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/SimpleBasicBlock.java src/classes/com/sun/tdk/jcov/instrument/StaticInvokeMethodAdapter.java src/classes/com/sun/tdk/jcov/instrument/XmlContext.java src/classes/com/sun/tdk/jcov/instrument/XmlNames.java src/classes/com/sun/tdk/jcov/instrument/package-info.java src/classes/com/sun/tdk/jcov/instrument/reader/BasicBlockStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/CharacterRangeTableAttributeStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataAnnotatedStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataBlockStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataBranchCondStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataBranchGotoStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataBranchSwitchStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataClassStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataExitSimpleStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataFieldStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataMethodStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataMethodWithBlocksStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/DataRootStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/LocationConcreteStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/LocationRefStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/Reader.java src/classes/com/sun/tdk/jcov/instrument/reader/ReaderFactory.java src/classes/com/sun/tdk/jcov/instrument/reader/ReaderFactoryStAX.java src/classes/com/sun/tdk/jcov/instrument/reader/RootReader.java src/classes/com/sun/tdk/jcov/instrument/reader/SimpleBasicBlockStAX.java src/classes/com/sun/tdk/jcov/io/ClassSignatureFilter.java src/classes/com/sun/tdk/jcov/io/Reader.java src/classes/com/sun/tdk/jcov/logging.properties src/classes/com/sun/tdk/jcov/package-info.java src/classes/com/sun/tdk/jcov/processing/CombinerDataProcessor.java src/classes/com/sun/tdk/jcov/processing/ConveyerProcessor.java src/classes/com/sun/tdk/jcov/processing/DataProcessor.java src/classes/com/sun/tdk/jcov/processing/DataProcessorFactory.java src/classes/com/sun/tdk/jcov/processing/DataProcessorSPI.java src/classes/com/sun/tdk/jcov/processing/DefaultDataProcessorSPI.java src/classes/com/sun/tdk/jcov/processing/ProcessingException.java src/classes/com/sun/tdk/jcov/processing/StubSpi.java src/classes/com/sun/tdk/jcov/processing/package-info.java src/classes/com/sun/tdk/jcov/report/AbstractCoverage.java src/classes/com/sun/tdk/jcov/report/ClassCoverage.java src/classes/com/sun/tdk/jcov/report/CoverageData.java src/classes/com/sun/tdk/jcov/report/DataType.java src/classes/com/sun/tdk/jcov/report/DefaultReportGeneratorSPI.java src/classes/com/sun/tdk/jcov/report/FieldCoverage.java src/classes/com/sun/tdk/jcov/report/ItemCoverage.java src/classes/com/sun/tdk/jcov/report/LineCoverage.java src/classes/com/sun/tdk/jcov/report/MemberCoverage.java src/classes/com/sun/tdk/jcov/report/MethodCoverage.java src/classes/com/sun/tdk/jcov/report/PackageCoverage.java src/classes/com/sun/tdk/jcov/report/ProductCoverage.java src/classes/com/sun/tdk/jcov/report/ReportGenerator.java src/classes/com/sun/tdk/jcov/report/ReportGeneratorSPI.java src/classes/com/sun/tdk/jcov/report/SmartTestService.java src/classes/com/sun/tdk/jcov/report/SubpackageCoverage.java src/classes/com/sun/tdk/jcov/report/Test.java src/classes/com/sun/tdk/jcov/report/html/CoverageReport.java src/classes/com/sun/tdk/jcov/report/html/JavaToHtml.java src/classes/com/sun/tdk/jcov/report/html/resources/CopyResources.java src/classes/com/sun/tdk/jcov/report/html/resources/sorttable.js src/classes/com/sun/tdk/jcov/report/html/resources/style.css src/classes/com/sun/tdk/jcov/report/javap/JavapClass.java src/classes/com/sun/tdk/jcov/report/javap/JavapClassReader.java src/classes/com/sun/tdk/jcov/report/javap/JavapCodeLine.java src/classes/com/sun/tdk/jcov/report/javap/JavapLine.java src/classes/com/sun/tdk/jcov/report/javap/JavapRepGen.java src/classes/com/sun/tdk/jcov/report/package-info.java src/classes/com/sun/tdk/jcov/report/text/TextReportGenerator.java src/classes/com/sun/tdk/jcov/runtime/AgentSocketSaver.java src/classes/com/sun/tdk/jcov/runtime/Collect.java src/classes/com/sun/tdk/jcov/runtime/CollectDetect.java src/classes/com/sun/tdk/jcov/runtime/FileSaver.java src/classes/com/sun/tdk/jcov/runtime/JCovSEServerSocketSaver.java src/classes/com/sun/tdk/jcov/runtime/JCovSESocketSaver.java src/classes/com/sun/tdk/jcov/runtime/JCovSaver.java src/classes/com/sun/tdk/jcov/runtime/JCovServerSocketSaver.java src/classes/com/sun/tdk/jcov/runtime/JCovSocketSaver.java src/classes/com/sun/tdk/jcov/runtime/JCovXMLFileSaver.java src/classes/com/sun/tdk/jcov/runtime/NetworkSatelliteDecorator.java src/classes/com/sun/tdk/jcov/runtime/PropertyFinder.java src/classes/com/sun/tdk/jcov/runtime/SaverDecorator.java src/classes/com/sun/tdk/jcov/runtime/TemplateFileSaver.java src/classes/com/sun/tdk/jcov/tools/DeflaterScaleCompressor.java src/classes/com/sun/tdk/jcov/tools/DelegateIterator.java src/classes/com/sun/tdk/jcov/tools/EnvHandler.java src/classes/com/sun/tdk/jcov/tools/EnvServiceProvider.java src/classes/com/sun/tdk/jcov/tools/JCovCMDTool.java src/classes/com/sun/tdk/jcov/tools/JCovTool.java src/classes/com/sun/tdk/jcov/tools/JcovStats.java src/classes/com/sun/tdk/jcov/tools/JcovVersion.java src/classes/com/sun/tdk/jcov/tools/LoggingFormatter.java src/classes/com/sun/tdk/jcov/tools/OneElemIterator.java src/classes/com/sun/tdk/jcov/tools/OptionDescr.java src/classes/com/sun/tdk/jcov/tools/SPIDescr.java src/classes/com/sun/tdk/jcov/tools/ScaleCompressor.java src/classes/com/sun/tdk/jcov/tools/ServiceProvider.java src/classes/com/sun/tdk/jcov/tools/SimpleScaleCompressor.java src/classes/com/sun/tdk/jcov/tools/package-info.java src/classes/com/sun/tdk/jcov/util/AddToProduct.java src/classes/com/sun/tdk/jcov/util/DebugUtils.java src/classes/com/sun/tdk/jcov/util/MapHelper.java src/classes/com/sun/tdk/jcov/util/NaturalComparator.java src/classes/com/sun/tdk/jcov/util/RuntimeUtils.java src/classes/com/sun/tdk/jcov/util/Utils.java src/classes/jcov/JTObserver.java
diffstat 202 files changed, 42939 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/Agent.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,846 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.constants.MiscConstants;
+import com.sun.tdk.jcov.instrument.*;
+import com.sun.tdk.jcov.runtime.AgentSocketSaver;
+import com.sun.tdk.jcov.runtime.Collect;
+import com.sun.tdk.jcov.runtime.CollectDetect;
+import com.sun.tdk.jcov.runtime.FileSaver;
+import com.sun.tdk.jcov.runtime.JCovSaver;
+import com.sun.tdk.jcov.runtime.PropertyFinder;
+import com.sun.tdk.jcov.runtime.SaverDecorator;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.*;
+import java.lang.instrument.*;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.Charset;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Dmitry Fazunenko
+ * @author Alexey Fedorchenko
+ */
+public class Agent extends JCovTool {
+
+    private boolean detectInternal;
+    private boolean classesReload;
+    private boolean instrumentField;
+    private boolean instrumentAbstract;
+    private boolean instrumentNative;
+    private boolean instrumentAnonymous = true;
+    private boolean instrumentSynthetic = true;
+    private String[] include;
+    private String[] exclude;
+    private String[] callerInclude;
+    private String[] callerExclude;
+    private String[] fm;
+    private String[] saveBegin;
+    private String[] saveEnd;
+    private String template;
+    private String filename;
+    private String flushPath;
+    private InstrumentationOptions.InstrumentationMode mode;
+    private InstrumentationOptions.MERGE merge;
+    private boolean grabberSaver = false;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(Agent.class.getName());
+    }
+    private final static Logger logger;
+    private static ClassMorph classMorph;
+    private String host;
+    private int port;
+    private static final Object LOCK = new Object();
+
+    private static class SynchronizedSaverDecorator implements SaverDecorator {
+
+        private JCovSaver wrap;
+
+        public SynchronizedSaverDecorator(JCovSaver wrap) {
+            init(wrap);
+        }
+
+        @Override
+        public final void init(JCovSaver saver) {
+            this.wrap = saver;
+        }
+
+        public void saveResults() {
+            synchronized (LOCK) {
+                wrap.saveResults();
+            }
+        }
+    }
+
+    /**
+     * ClassFileTransformer implementation. Gets classfile binary data from VM
+     * and runs ClassMorph.morph method.
+     */
+    private static class Tr implements ClassFileTransformer {
+
+        /**
+         * Path to flush instrumented classfiles to. Null means that
+         * instrumented classfiles should not be flushed.
+         */
+        private final String flushpath;
+        /**
+         * Transformer name. Is not used.
+         */
+        private final String trname;
+        /**
+         * Can turn off agent instrumentation
+         */
+        private boolean ignoreLoads = true;
+
+        /**
+         * Creates new Tr instance
+         *
+         * @param trname Transformer name. Is not used.
+         * @param flushpath Path to flush instrumented classfiles to. Null means
+         * that instrumented classfiles should not be flushed.
+         */
+        public Tr(String trname, String flushpath) {
+            this.trname = trname;
+            this.flushpath = flushpath;
+        }
+
+        /**
+         * transform method implementation
+         *
+         * @param loader
+         * @param className
+         * @param classBeingRedefined
+         * @param protectionDomain
+         * @param classfileBuffer
+         * @return instrumented classfile binary data (if ignoreLoads is not set
+         * to true, classfileBuffer will be returned otherwise). If collect is
+         * not enabled - null is returned.
+         */
+        public byte[] transform(ClassLoader loader,
+                String className,
+                Class<?> classBeingRedefined,
+                ProtectionDomain protectionDomain,
+                byte[] classfileBuffer) {
+            synchronized (LOCK) {
+                if (Collect.enabled == false) {
+                    return null; // signals to the VM that no changes were done
+                }
+
+                // no need to enter when Collect is disabled
+                CollectDetect.enterInstrumentationCode(); // ensuring that instrumenting will not influence on coverage data
+
+                try {
+                    if (ignoreLoads) {
+                        logger.log(Level.INFO, "Ignore for now {0}", className);
+                    } else {
+                        logger.log(Level.INFO, "Try to transform {0}", className);
+                        byte[] newBuff = classMorph.morph(classfileBuffer, loader, flushpath);
+                        return newBuff;
+                    }
+                } catch (Throwable e) {
+                    logger.log(Level.SEVERE, "Adaption failed for {0} with :{1}", new Object[]{className, e});
+                    e.printStackTrace();
+                } finally {
+                    CollectDetect.leaveInstrumentationCode(); // release instrumentation lock
+                }
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Class for listening agent commands
+     */
+    private static class CommandThread extends Thread {
+
+        /**
+         * Agent commands
+         */
+        public static enum COMMAND {
+
+            SAVE {
+                String cmd() {
+                    return "save";
+                }
+            },
+            SAVED {
+                String cmd() {
+                    return "saved";
+                }
+            },
+            EXIT {
+                String cmd() {
+                    return "exit";
+                }
+            },
+            EXIT_WITHOUT_SAVE {
+                String cmd() {
+                    return "exitWithoutSave".toLowerCase();
+                }
+            },
+            AUTOSAVE_DISABLED {
+                String cmd() {
+                    return "autosave disabled";
+                }
+            };
+
+            abstract String cmd();
+        }
+        /**
+         * Port to listen incoming messages
+         */
+        private int port;
+        /**
+         * Instrumentation params
+         */
+        private InstrumentationParams params;
+
+        /**
+         * Creates CommandThread instance
+         *
+         * @param port Port to listen incoming messages
+         * @param params Instrumentation params
+         */
+        public CommandThread(int port, InstrumentationParams params) {
+            this.port = port;
+            this.params = params;
+            setDaemon(true);
+        }
+
+        @Override
+        public void run() {
+            while (true) {
+                try {
+                    ServerSocket sock = new ServerSocket(port);
+                    Socket s = sock.accept();
+//                    System.out.println("Accepted");
+                    InputStream is = s.getInputStream();
+                    byte[] buff = new byte[1024];
+                    int l;
+                    String rest = "";
+                    while ((l = is.read(buff)) > 0) {
+                        String msg = rest + new String(buff, 0, l, Charset.defaultCharset());
+//                        System.out.println("Message: " + msg);
+                        rest = performTask(msg, s);
+                    }
+                    sock.close();
+                } catch (IOException ex) {
+                    logger.log(Level.SEVERE, "Network IOException", ex);
+                }
+            }
+        }
+
+        /**
+         * Parse and execute incoming message
+         *
+         * @param msg message
+         * @param sock socket
+         * @return exit code
+         * @throws IOException
+         */
+        private String performTask(String msg, Socket sock) throws IOException {
+            Pattern p = Pattern.compile("\\p{Space}*(\\p{Digit}+).*");
+            msg = msg.toLowerCase(Locale.getDefault());
+            PrintStream ps = new PrintStream(sock.getOutputStream(), false, "UTF-8");
+            while (msg.length() > 0) {
+                msg = msg.trim();
+                COMMAND cmd = nextCommand(msg);
+                if (cmd == null) {
+                    break;
+                } else {
+                    switch (cmd) {
+                        case SAVE:
+                            msg = msg.substring(cmd.cmd().length());
+                            if (Collect.enabled) {
+                                Collect.disable();
+                                Collect.saveResults();
+                                params.enable();
+                            }
+                            ps.print(COMMAND.SAVED.cmd());
+                            ps.flush();
+                            break;
+                        case EXIT:
+                            msg = msg.substring(cmd.cmd().length());
+                            Matcher m = p.matcher(msg);
+                            int exitCode = 0;
+                            if (m.matches()) {
+                                exitCode = Integer.parseInt(m.group(1));
+                            }
+                            System.exit(exitCode);
+                            break;
+                        case EXIT_WITHOUT_SAVE:
+                            msg = msg.substring(cmd.cmd().length());
+                            m = p.matcher(msg);
+                            exitCode = 0;
+                            if (m.matches()) {
+                                exitCode = Integer.parseInt(m.group(1));
+                            }
+                            FileSaver.setDisableAutoSave(true);
+                            ps.print(COMMAND.AUTOSAVE_DISABLED.cmd());
+                            ps.flush();
+                            System.exit(exitCode);
+                            break;
+                    }
+                }
+            }
+
+            return msg;
+        }
+
+        /**
+         * Parse incomming message and return COMMAND value
+         *
+         * @param msg message to parse
+         * @return associated COMMAND value
+         */
+        private COMMAND nextCommand(String msg) {
+            String foundPref = "";
+            COMMAND found = null;
+
+            for (COMMAND c : COMMAND.values()) {
+                if (msg.startsWith(c.cmd()) && foundPref.length() < c.cmd().length()) {
+                    found = c;
+                    foundPref = c.cmd();
+                }
+            }
+
+            return found;
+        }
+    }
+
+    /**
+     * javaagent entry point
+     *
+     * @param agentArgs
+     * @param instArg
+     * @throws Exception
+     */
+    public static void premain(String agentArgs, Instrumentation instArg) {
+
+        // handling JCovTool
+
+        // This method manages CLI handling for Agent tool.
+        // If any change is performed here - check JCovCMDTool CLI handling logic.
+
+        Agent tool = new Agent();
+
+        EnvHandler handler = tool.defineHandler();
+
+        try {
+            // proccess cmd options
+            if (agentArgs == null) {
+                agentArgs = "";
+            }
+            handler.parseCLIArgs(EnvHandler.parseAgentString(agentArgs));
+            tool.handleEnv(handler);
+            if (handler.isSet(EnvHandler.PRINT_ENV)) {
+                handler.printEnv();
+            }
+        } catch (EnvHandler.CLParsingException ex) {
+            if (handler.isSet(EnvHandler.HELP)) {
+                handler.usage();
+                handler.getOut().println("\n JCov Agent command line error: " + ex.getMessage() + "\n");
+                System.exit(ERROR_CMDLINE_EXIT_CODE);
+            }
+
+            if (handler.isSet(EnvHandler.HELP_VERBOSE)) {
+                handler.usage(true);
+                handler.getOut().println("\n JCov Agent command line error: " + ex.getMessage() + "\n");
+                System.exit(ERROR_CMDLINE_EXIT_CODE);
+            }
+
+            handler.getOut().println(" JCov Agent command line error: " + ex.getMessage() + "\n");
+            handler.getOut().println("Use \"java -jar jcov.jar Agent -h\" for command-line help or \"java -jar jcov.jar Agent -hv\" for wider description");
+            System.exit(ERROR_CMDLINE_EXIT_CODE);
+        } catch (EnvHandlingException ex) {
+            handler.getOut().println("JCov Agent command line error: " + ex.getMessage() + "\n");
+            handler.getOut().println("Use \"java -jar jcov.jar Agent -h\" for command-line help or \"java -jar jcov.jar Agent -hv\" for wider description");
+            if (handler.isSet(EnvHandler.PRINT_ENV)) {
+                handler.printEnv();
+            }
+            System.exit(ERROR_CMDLINE_EXIT_CODE);
+        } catch (Throwable ex) {
+            handler.getOut().println("JCov Agent command line error: " + ex.getMessage());
+            System.exit(ERROR_CMDLINE_EXIT_CODE);
+        }
+        if (handler.isSet(EnvHandler.PRINT_ENV)) {
+            handler.printEnv();
+            System.exit(SUCCESS_EXIT_CODE);
+        }
+
+        try {
+            if (Utils.getJavaVersion() >= Utils.VER16) {
+                tool.premainV50(agentArgs, instArg);
+            } else {
+                tool.premainV49(agentArgs, instArg);
+            }
+        } catch (Exception ex) {
+            System.out.println("Agent execution error: " + ex.getMessage());
+            ex.printStackTrace();
+            System.exit(ERROR_EXEC_EXIT_CODE);
+        }
+    }
+
+    /**
+     * premain chain for classfiles V50+
+     *
+     * @param agentArgs
+     * @param inst
+     * @throws Exception
+     */
+    public void premainV50(String agentArgs, Instrumentation inst) throws Exception {
+        InstrumentationParams params =
+                new InstrumentationParams(classesReload, true, instrumentNative, instrumentField,
+                detectInternal, instrumentAbstract ? InstrumentationOptions.ABSTRACTMODE.DIRECT : InstrumentationOptions.ABSTRACTMODE.NONE,
+                include, exclude, callerInclude, callerExclude, mode, saveBegin, saveEnd)
+                .setInstrumentAnonymous(instrumentAnonymous)
+                .setInstrumentSynthetic(instrumentSynthetic);
+
+        params.enable();
+        CollectDetect.enterInstrumentationCode();
+        Tr transformer = new Tr("RetransformApp", flushPath);
+        inst.addTransformer(transformer, true);
+        if (params.isInstrumentNative()) {
+            inst.setNativeMethodPrefix(transformer, InstrumentationOptions.nativePrefix);
+        }
+
+        DataRoot root = new DataRoot(agentArgs, params);
+        classMorph = new ClassMorph(filename, root, params);
+        Class[] classes = inst.getAllLoadedClasses();
+        Set<Class> examinedClasses = new HashSet<Class>(Arrays.asList(classes));
+        int keep = 0;
+        for (Class c : classes) {
+            if (inst.isModifiableClass(c)
+                    && classMorph.shouldTransform(c.getName().replace('.', '/'))
+                    && !c.getName().replace('.', '/').equals("sun/reflect/Reflection")) {
+                classes[keep++] = c;
+            }
+        }
+        transformer.ignoreLoads = false;
+        if (keep > 0) {
+            classes = Utils.copyOf(classes, keep);
+            logger.log(Level.INFO, "About to retransform {0} classes {1}", new Object[]{keep, classes[0]});
+            try {
+                inst.retransformClasses(classes);
+            } catch (UnmodifiableClassException e) {
+                System.err.println("Should not happen: " + e);
+                e.printStackTrace(System.err);
+            } catch (Throwable e) {
+                System.err.println("During retransform: " + e);
+                e.printStackTrace(System.err);
+            }
+        }
+        logger.log(Level.INFO, "Retransformed {0} classes", keep);
+        Class[] allClasses = inst.getAllLoadedClasses();
+        keep = 0;
+        for (Class c : allClasses) {
+            if (!examinedClasses.contains(c)
+                    && inst.isModifiableClass(c)
+                    && classMorph.shouldTransform(c.getName().replace('.', '/'))) {
+                allClasses[keep++] = c;
+            }
+        }
+        if (keep > 0) {
+            logger.log(Level.INFO, "New not transformed: {0} classes {1}", new Object[]{keep, allClasses[0]});
+            classes = Utils.copyOf(classes, keep);
+            try {
+                inst.retransformClasses(classes);
+            } catch (UnmodifiableClassException e) {
+                logger.log(Level.SEVERE, "retransformClasses: Should not happen: ", e);
+                //log.log(.printStackTrace(System.err);
+            } catch (Throwable e) {
+                logger.log(Level.SEVERE, "Error during retransform: ", e);
+            }
+        }
+
+        if (!grabberSaver) {
+            // File saver should perform full merge here, not only insert new classes.
+            JCovSaver saver = FileSaver.getFileSaver(root, filename, template, merge, true);
+            Collect.setSaver(Collect.decorateSaver(new SynchronizedSaverDecorator(saver)));
+        } else {
+            AgentSocketSaver saver = new AgentSocketSaver(root, filename, host, port);
+            Collect.setSaver(Collect.decorateSaver(new SynchronizedSaverDecorator(saver)));
+        }
+        CollectDetect.leaveInstrumentationCode();
+        PropertyFinder.addAutoShutdownSave();
+    }
+
+    /**
+     * premain chain for classfiles V49+
+     *
+     * @param agentArgs
+     * @param inst
+     * @throws Exception
+     */
+    public void premainV49(String agentArgs, Instrumentation inst) throws Exception {
+        InstrumentationParams params =
+                new InstrumentationParams(true, instrumentNative, instrumentField,
+                detectInternal, instrumentAbstract ? InstrumentationOptions.ABSTRACTMODE.DIRECT : InstrumentationOptions.ABSTRACTMODE.NONE,
+                include, exclude, callerInclude, callerExclude, mode, saveBegin, saveEnd)
+                .setInstrumentAnonymous(instrumentAnonymous)
+                .setInstrumentSynthetic(instrumentSynthetic);
+
+        params.enable();
+        CollectDetect.enterInstrumentationCode();
+        Tr transformer = new Tr("RetransformApp", flushPath);
+        inst.addTransformer(transformer);
+        /* if (Options.isInstrumentNative()) {
+         inst.setNativeMethodPrefix(transformer, Options.nativePrefix);
+         }
+         */
+        DataRoot root = new DataRoot(agentArgs, params);
+        classMorph = new ClassMorph(filename, root, params);
+        Class[] classes = inst.getAllLoadedClasses();
+        Set<Class> examinedClasses = new HashSet<Class>(Arrays.asList(classes));
+        int keep = 0;
+        for (Class c : classes) {
+            if (/*inst.isModifiableClass(c) &&*/classMorph.shouldTransform(c.getName().replace('.', '/'))) {
+                classes[keep++] = c;
+            }
+        }
+        if (keep > 0) {
+            classes = Utils.copyOf(classes, keep);
+            logger.log(Level.INFO, "About to retransform {0} classes {1}", new Object[]{keep, classes[0]});
+        }
+        logger.log(Level.INFO, "Retransformed {0} classes", keep);
+        transformer.ignoreLoads = false;
+        Class[] allClasses = inst.getAllLoadedClasses();
+        keep = 0;
+        for (Class c : allClasses) {
+            if (!examinedClasses.contains(c)
+                    && //  inst.isModifiableClass(c) &&
+                    classMorph.shouldTransform(c.getName().replace('.', '/'))) {
+                allClasses[keep++] = c;
+            }
+        }
+        if (keep > 0) {
+            classes = Utils.copyOf(allClasses, keep);
+        }
+
+        if (!grabberSaver) {
+            // File saver should perform full merge here, not only insert new classes.
+            JCovSaver saver = FileSaver.getFileSaver(root, filename, template, merge, true);
+            Collect.setSaver(Collect.decorateSaver(new SynchronizedSaverDecorator(saver)));
+        } else {
+            AgentSocketSaver saver = new AgentSocketSaver(root, filename, host, port);
+            Collect.setSaver(Collect.decorateSaver(new SynchronizedSaverDecorator(saver)));
+        }
+        CollectDetect.leaveInstrumentationCode();
+        PropertyFinder.addAutoShutdownSave();
+    }
+
+    public String usageString() {
+        return "java -javaagent:jcov.jar=[=option=value[,option=value]*] ...";
+    }
+
+    public String exampleString() {
+        return "java -javaagent:jcov.jar=include=java\\.lang\\.String,native=on,type=branch,abstract=off -jar MyApp.jar";
+    }
+
+    public String getDescr() {
+        return "print help on usage jcov in dynamic mode";
+    }
+
+    @Override
+    public boolean isMainClassProvided() {
+        return false;
+    }
+
+///////// JCovTool implementation /////////
+    @Override
+    public EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    DSC_OUTPUT,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_MERGE,
+                    // Verbosity
+                    DSC_VERBOSE,
+                    DSC_TIMEOUT,
+                    DSC_PORT,
+                    // Instrumentation parameters.
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TEMPLATE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TYPE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CALLER_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CALLER_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ABSTRACT,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_NATIVE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FIELD,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SYNTHETIC,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ANONYM,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CLASSESRELOAD,
+                    // Data save points
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SAVE_BEGIN,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SAVE_AT_END,
+                    ClassMorph.DSC_FLUSH_CLASSES,
+                    DSC_GRABBER,
+                    DSC_PORT_GRABBER,
+                    DSC_HOST_GRABBER,
+                    DSC_LOG
+                }, this);
+    }
+
+    @Override
+    public int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        String internal = "default";
+        if (internal.equals("detect")) {
+            detectInternal = true;
+        } else if (internal.equals("show")) {
+            detectInternal = true;
+        } else if (internal.equals("include")) {
+            detectInternal = false;
+        } else if (internal.equals("default")) {
+            detectInternal = true;
+        } else {
+            throw new Error("Parameter error");
+        }
+
+        mode = InstrumentationOptions.InstrumentationMode.fromString(opts.getValue(InstrumentationOptions.DSC_TYPE));
+        if (opts.isSet(InstrumentationOptions.DSC_TEMPLATE)) {
+            template = opts.getValue(InstrumentationOptions.DSC_TEMPLATE);
+        }
+
+        include = InstrumentationOptions.handleInclude(opts);
+        exclude = InstrumentationOptions.handleExclude(opts);
+        fm = InstrumentationOptions.handleFM(opts);
+
+        callerInclude = opts.getValues(InstrumentationOptions.DSC_CALLER_INCLUDE);
+//        System.out.println("Setup callerInclude " + Arrays.toString(callerInclude));
+        callerExclude = opts.getValues(InstrumentationOptions.DSC_CALLER_EXCLUDE);
+//        System.out.println("Setup callerExclude " + Arrays.toString(callerExclude));
+
+        String abstractValue = opts.getValue(InstrumentationOptions.DSC_ABSTRACT);
+        if (abstractValue.equals("off")) {
+            instrumentAbstract = false;
+        } else if (abstractValue.equals("on")) {
+            instrumentAbstract = true;
+        } else {
+            // will not happen - checking inside EnvHandler
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_ABSTRACT.name + "' parameter value error: expected 'on' or 'off'; found: '" + abstractValue + "'");
+        }
+
+        String classesReloadValue = opts.getValue(InstrumentationOptions.DSC_CLASSESRELOAD);
+        if (classesReloadValue.equals("on")) {
+            classesReload = true;
+        } else {
+            classesReload = false;
+        }
+
+        String nativeValue = opts.getValue(InstrumentationOptions.DSC_NATIVE);
+        if (nativeValue.equals("on")) {
+            instrumentNative = true;
+        } else if (nativeValue.equals("off")) {
+            instrumentNative = false;
+        } else {
+            // will not happen - checking inside EnvHandler
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_NATIVE.name + "' parameter value error: expected 'on' or 'off'; found: '" + nativeValue + "'");
+        }
+
+        String fieldValue = opts.getValue(InstrumentationOptions.DSC_FIELD);
+        if (fieldValue.equals("on")) {
+            instrumentField = true;
+        } else if (fieldValue.equals("off")) {
+            instrumentField = false;
+        } else {
+            // will not happen - checking inside EnvHandler
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_FIELD.name + "' parameter value error: expected 'on' or 'off'; found: '" + fieldValue + "'");
+        }
+
+        String anonym = opts.getValue(InstrumentationOptions.DSC_ANONYM);
+        if (anonym.equals("on")) {
+            instrumentAnonymous = true;
+        } else { // off
+            instrumentAnonymous = false;
+        }
+
+        String synthetic = opts.getValue(InstrumentationOptions.DSC_SYNTHETIC);
+        if (synthetic.equals("on")) {
+            instrumentSynthetic = true;
+        } else { // off
+            instrumentSynthetic = false;
+        }
+
+        String mergeValue = opts.getValue(InstrumentationOptions.DSC_MERGE);
+        if (mergeValue.equals("merge")) {
+            merge = InstrumentationOptions.MERGE.MERGE;
+        } else if (mergeValue.equals("scale")) {
+            merge = InstrumentationOptions.MERGE.SCALE;
+        } else if (mergeValue.equals("overwrite")) {
+            merge = InstrumentationOptions.MERGE.OVERWRITE;
+        } else if (mergeValue.equals("gensuff")) {
+            merge = InstrumentationOptions.MERGE.GEN_SUFF;
+        } else {
+            // will never happen as this is checked in EnvHandler
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_MERGE.name + "' parameter value error: expected 'merge', 'scale', 'overwrite' or 'gensuff'; found: '" + mergeValue + "'");
+        }
+
+        saveBegin = opts.getValues(InstrumentationOptions.DSC_SAVE_BEGIN);
+        saveEnd = opts.getValues(InstrumentationOptions.DSC_SAVE_AT_END);
+
+        flushPath = opts.getValue(ClassMorph.DSC_FLUSH_CLASSES);
+        if ("none".equals(flushPath)) {
+            flushPath = null;
+        }
+
+        String logfile = opts.getValue(EnvHandler.LOGFILE);
+        if (opts.isSet(DSC_LOG) || logfile != null) {
+            if (logfile == null) {
+                logfile = "jcov.log";
+            }
+            try {
+                Utils.setLoggerHandler(new FileHandler(logfile));
+            } catch (Exception ex) {
+                throw new EnvHandlingException("Can't open file '" + logfile + "' for writing the log", ex);
+            }
+            if (opts.isSet(EnvHandler.LOGLEVEL)) {
+                Utils.setLoggingLevel(opts.getValue(EnvHandler.LOGLEVEL));
+            } else if (opts.isSet(DSC_VERBOSE)) {
+                int verbositylevel = Utils.checkedToInt(opts.getValue(DSC_VERBOSE), "verbosity level", Utils.CheckOptions.INT_NONNEGATIVE);
+                switch (verbositylevel) {
+                    case 0:
+                        logger.setLevel(Level.SEVERE);
+                        Utils.setLoggingLevel(Level.SEVERE);
+                        break;
+                    case 1:
+                        logger.setLevel(Level.CONFIG);
+                        Utils.setLoggingLevel(Level.CONFIG);
+                        break;
+                    case 2:
+                        logger.setLevel(Level.INFO);
+                        Utils.setLoggingLevel(Level.INFO);
+                        break;
+                    case 3:
+                        logger.setLevel(Level.ALL);
+                        Utils.setLoggingLevel(Level.ALL);
+                        break;
+                    default:
+                        throw new EnvHandlingException("Incorrect verbosity level (" + opts.getValue(DSC_VERBOSE) + ") - should be 0..3");
+                }
+            }
+        } else {
+            Utils.setLoggingLevel(Level.OFF);
+        }
+
+        if (opts.isSet(DSC_TIMEOUT)) {
+            long timeout = Utils.checkedToInt(opts.getValue(DSC_TIMEOUT), "timeout value");
+            if (timeout > 0) {
+                Timer timer = new Timer(true);
+                timer.schedule(new TimerTask() {
+                    @Override
+                    public void run() {
+                        logger.log(Level.INFO, "Agent has been timed out.");
+                        if (Collect.enabled) {
+                            Collect.disable();
+                            Collect.saveResults();
+                        }
+                        Runtime.getRuntime().halt(0);
+                    }
+                }, timeout);
+            }
+        }
+
+        grabberSaver = opts.isSet(DSC_GRABBER);
+        if (grabberSaver) {
+            host = opts.getValue(DSC_HOST_GRABBER);
+            Utils.checkHostCanBeNull(host, "grabber host");
+
+            this.port = Utils.checkedToInt(opts.getValue(DSC_PORT_GRABBER), "grabber port number", Utils.CheckOptions.INT_POSITIVE);
+        }
+
+        filename = opts.getValue(DSC_OUTPUT);
+        if (!grabberSaver) {
+            Utils.checkFileNotNull(filename, "output filename", Utils.CheckOptions.FILE_NOTISDIR, Utils.CheckOptions.FILE_PARENTEXISTS);
+        }
+
+        if (opts.isSet(DSC_PORT)) {
+            CommandThread cmdThread = new CommandThread(Utils.checkedToInt(opts.getValue(DSC_PORT), "command listener port number", Utils.CheckOptions.INT_POSITIVE),
+                    new InstrumentationParams(true, instrumentNative, instrumentField, detectInternal,
+                    instrumentAbstract ? InstrumentationOptions.ABSTRACTMODE.DIRECT : InstrumentationOptions.ABSTRACTMODE.NONE,
+                    include, exclude, callerInclude, callerExclude, mode, saveBegin, saveEnd)
+                    .setInstrumentAnonymous(instrumentAnonymous)
+                    .setInstrumentSynthetic(instrumentSynthetic));
+            cmdThread.start();
+        }
+
+        return SUCCESS_EXIT_CODE;
+    }
+    public static final OptionDescr DSC_OUTPUT =
+            new OptionDescr("file", new String[]{"url", "o"}, "Output path definition.",
+            OptionDescr.VAL_SINGLE, "Specifies output data file. \n"
+            + "If specified file already exists, collected data will be merged with data from file",
+            "result.xml");
+    public final static OptionDescr DSC_VERBOSE =
+            new OptionDescr("verbose", "Verbosity level.",
+            new String[][]{
+                {"0", "minimal, only fatal failure diagnostic is printed"},
+                {"1", "moderate, non-fatal errors are included in log"},
+                {"2", "high, all warnings are included in log"},
+                {"3", "highest, all messages are included in log"}
+            },
+            "Set verbosity level.", "0");
+    public static final OptionDescr DSC_TIMEOUT =
+            new OptionDescr("timeout", "Agent process timeout.",
+            OptionDescr.VAL_SINGLE, "Specifies timeout for agent process in milliseconds.\n"
+            + "0 means there is no timeout specified. Default is 0.\n",
+            "0");
+    // port now can be set only as "agent.port" via VM properties and env variables. "Port" is used only for grabber and socket saver.
+    public static final OptionDescr DSC_PORT = new OptionDescr("agent.port", new String[]{"portcl"}, "Agent command listening port",
+            OptionDescr.VAL_SINGLE, "Specifies port number to listen for driving commands.\n"
+            + "Commands are executed sequentially, some may send messages in response. "
+            + "Valid commands to send are: \n"
+            + "   \"save\" - to save already collected data. It will respond with \"saved\" message\n"
+            + "   \"exit\" - to perform System.exit() immediately. Exit code number may be sent with this command.\n"
+            + "              It's chars should follow \"exit\"");
+    public static final OptionDescr DSC_PORT_GRABBER = new OptionDescr("port", new String[]{"grabberport"}, "",
+            OptionDescr.VAL_SINGLE, "Specifies port number to send data to the grabber", MiscConstants.JcovPortNumber + "");
+    public static final OptionDescr DSC_HOST_GRABBER = new OptionDescr("host", new String[]{"grabberhost"}, "",
+            OptionDescr.VAL_SINGLE, "Specifies host name to send data to the grabber", "localhost");
+    public final static OptionDescr DSC_LOG =
+            new OptionDescr("log", "logging", OptionDescr.VAL_NONE, "Turns on JCov's agent logging.\n"
+            + "Log records saved in jcov.log file");
+    public final static OptionDescr DSC_GRABBER =
+            new OptionDescr("grabber", "use grabber saver", OptionDescr.VAL_NONE, "Use grabber saver instead of file saver. jcov.port "
+            + "and jcov.host VM properties could be used to control the saver as well as JCOV_PORT and JCOV_HOST env variable");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/DiffCoverage.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.filter.MemberFilter;
+import com.sun.tdk.jcov.instrument.DataClass;
+import com.sun.tdk.jcov.instrument.DataField;
+import com.sun.tdk.jcov.instrument.DataMethod;
+import com.sun.tdk.jcov.instrument.DataPackage;
+import com.sun.tdk.jcov.instrument.DataRoot;
+import com.sun.tdk.jcov.report.ClassCoverage;
+import com.sun.tdk.jcov.report.LineCoverage;
+import com.sun.tdk.jcov.report.MethodCoverage;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p> Allows to get numbers of changed code lines that were not covered. </p>
+ * <p> Uses a diff file and a JCov XML result file. </p> <p> Mercurial and
+ * Subversion diff files formats are supported. </p>
+ *
+ * @author Andrey Titov
+ */
+public class DiffCoverage extends JCovCMDTool {
+
+    /**
+     * A line in the sources
+     */
+    public static class SourceLine {
+
+        private int line;
+        private String source;
+        boolean checked;
+
+        public SourceLine(int line, String source) {
+            this.line = line;
+            this.source = source;
+        }
+
+        public SourceLine() {
+        }
+
+        public boolean hits(int start, int end) {
+            return line > start && line < end;
+        }
+
+        public String toString() {
+            return "[line = " + line + ", source = " + source + "]";
+        }
+    }
+    private String file;
+    private File diffFile;
+    private HashMap<String, SourceLine[]> sources;
+    private HashMap<Integer, String> sourceLines;
+    private static final Logger logger;
+    private String replaceDiff;
+    private String replaceClass;
+    private boolean all;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(DiffCoverage.class.getName());
+    }
+
+    @Override
+    protected int run() throws Exception {
+        final LinkedList<String> classNames = new LinkedList<String>();
+        try {
+            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(diffFile), "UTF-8"));
+            DiffHandler handler = new HGDiffHandler(in);
+            sources = new HashMap<String, SourceLine[]>();
+            String sourceName;
+            while ((sourceName = handler.getNextSource()) != null) {
+                LinkedList<SourceLine> lines = new LinkedList<SourceLine>();
+                SourceLine line;
+                while ((line = handler.getNextSourceLine()) != null) {
+                    lines.add(line);
+                }
+                if (lines.size() > 0) {
+                    classNames.add(sourceName.substring(sourceName.lastIndexOf('/') + 1)); // rough estimation of needed classes
+                    logger.log(Level.INFO, "File {0} has {1} new lines", new Object[]{sourceName, lines.size()});
+                    if (replaceDiff != null) {
+                        String[] split = replaceDiff.split(":");
+                        String patt = split[0];
+                        String with;
+                        if (split.length == 1) {
+                            with = "";
+                        } else {
+                            with = split[1];
+                        }
+                        sourceName = sourceName.replaceAll(patt, with);
+                    }
+                    sources.put(sourceName, lines.toArray(new SourceLine[lines.size()]));
+                } else {
+                    logger.log(Level.INFO, "File {0} doesn't have new lines", sourceName);
+                }
+            }
+
+        } catch (Exception ex) {
+            logger.log(Level.SEVERE, "Error while parsing diff file", ex);
+            if (ex instanceof NullPointerException) {
+                ex.printStackTrace();
+            }
+            return ERROR_EXEC_EXIT_CODE;
+        }
+
+        DataRoot data = DataRoot.read(file, false, new MemberFilter() {
+            public boolean accept(DataClass clz) {
+                if (classNames.contains(clz.getSource())) {
+                    return true;
+                }
+                return false;
+            }
+
+            public boolean accept(DataClass clz, DataMethod m) {
+                return true;
+            }
+
+            public boolean accept(DataClass clz, DataField f) {
+                return true;
+            }
+        });
+
+        int notCovered = 0, covered = 0, nonCode = 0;
+        for (DataPackage p : data.getPackages()) {
+            for (DataClass c : p.getClasses()) {
+                ClassCoverage cc = new ClassCoverage(c, null, MemberFilter.ACCEPT_ALL);
+                String className = c.getPackageName() + "/" + c.getSource();
+                if (replaceClass != null) {
+                    String[] split = replaceClass.split(":");
+                    String patt = split[0];
+                    String with;
+                    if (split.length == 1) {
+                        with = "";
+                    } else {
+                        with = split[1];
+                    }
+                    className = className.replaceAll(patt, with);
+                }
+
+                SourceLine lines[] = sources.get(className);
+                if (lines != null) {
+                    for (DataMethod m : c.getMethods()) {
+                        boolean changed = false;
+                        LineCoverage lc = new MethodCoverage(m, false).getLineCoverage(); // false is not used
+
+                        for (SourceLine line : lines) {
+                            if (line.checked) {
+                                continue;
+                            }
+                            if (line.line >= lc.firstLine() && line.line <= lc.lastLine()) {
+                                line.checked = true;
+                                if (!changed && all) {
+                                    System.out.println(String.format("   %s: %s.%s", className, c.getName(), m.getFormattedSignature()));
+                                    changed = true;
+                                }
+                                if (cc.isLineCovered(line.line)) {
+                                    ++covered;
+                                    if (all) {
+                                        System.out.println(String.format("+ %6d |%s", line.line, line.source));
+                                    }
+                                } else {
+                                    if (cc.isCode(line.line)) {
+                                        if (!changed && !all) {
+                                            System.out.println(String.format("   %s> %s: %s", className, c.getName(), m.getFormattedSignature()));
+                                            changed = true;
+                                        }
+                                        ++notCovered;
+                                        System.out.println(String.format("- %6d |%s", line.line, line.source));
+                                    } else {
+                                        ++nonCode;
+                                        if (all) {
+                                            System.out.println(String.format("  %6d |%s", line.line, line.source));
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    for (SourceLine line : lines) {
+                        if (!line.checked) {
+                            ++nonCode;
+                        }
+                    }
+                }
+            }
+        }
+        System.out.println(String.format("lines: %d new; %d covered; %d not covered; %d not code", nonCode + notCovered + covered, covered, notCovered, nonCode));
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{DSC_REPLACE_DIFF, DSC_REPLACE_CLASS, DSC_ALL}, this);
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler envHandler) throws EnvHandlingException {
+        String[] tail = envHandler.getTail();
+
+        if (tail == null) {
+            throw new EnvHandlingException("No input files. Please specify JCov data file and diff (mercurial) file.");
+        }
+
+        if (tail.length < 2) {
+            throw new EnvHandlingException("Not enough input files. Please specify JCov data file and diff (mercurial) file.");
+        }
+
+        file = tail[0];
+        Utils.checkFileNotNull(tail[0], "JCov datafile", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_CANREAD);
+        diffFile = new File(tail[1]);
+        Utils.checkFile(diffFile, "diff file", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_CANREAD);
+
+        replaceDiff = envHandler.getValue(DSC_REPLACE_DIFF);
+        replaceClass = envHandler.getValue(DSC_REPLACE_CLASS);
+        all = envHandler.isSet(DSC_ALL);
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected String getDescr() {
+        return "check whether changed lines were covered";
+    }
+
+    @Override
+    protected String usageString() {
+        return "java -jar jcov.jar diffcoverage result.xml diff";
+    }
+
+    @Override
+    protected String exampleString() {
+        return "java -jar jcov.jar diffcoverage -replace src/classes/: result.xml diff";
+    }
+
+    static interface DiffHandler {
+
+        public String getNextSource() throws IOException;
+
+        public SourceLine getNextSourceLine() throws IOException;
+    }
+
+    static class HGDiffHandler implements DiffHandler {
+
+        BufferedReader in;
+        String current;
+        int startLines = -1;
+        int endLines = -1;
+
+        public HGDiffHandler(BufferedReader in) {
+            this.in = in;
+        }
+
+        public String getNextSource() throws IOException {
+            if (current == null || !current.startsWith("+++")) {
+                current = in.readLine();
+            }
+            while (current != null && !(current.startsWith("+++") && current.contains(".java") && !current.contains("package-info.java") && !current.contains("module-info.java"))) {
+                current = in.readLine();
+            }
+            if (current != null) {
+                String next = in.readLine();
+                if (next.startsWith("@@")) {
+//                    String filepath = current.substring(4, current.indexOf(".java") + 5);
+                    String filepath = current.substring(0, current.lastIndexOf(".java") + 5);
+                    filepath = filepath.replaceAll("\\+\\+\\+ ([a-zA-Z]/)?", ""); // !!! deleting _one_ symbol from the begining of the filename if
+                    current = next;
+                    return filepath;
+                }
+                current = null;
+            } // else - EOF
+
+            return null;
+        }
+        private static String LINES_NUMBERS_PATTERN = "@@ [-+][\\d]+,[\\d]+ [-+][\\d]+,[\\d]+ @@(?s).*";
+
+        public SourceLine getNextSourceLine() throws IOException {
+            if (current != null && !current.startsWith("---")) {
+                if (startLines == -1 && endLines == -1) {
+                    String linesNumbersLine = current;
+                    while (linesNumbersLine == null || !linesNumbersLine.matches(LINES_NUMBERS_PATTERN)) { // e.g. @@ +1,44 -4,8 @@
+                        linesNumbersLine = in.readLine();
+                        if (linesNumbersLine == null) {
+                            startLines = -1;
+                            endLines = -1;
+                            current = null;
+                            return null;
+                        }
+                        if (linesNumbersLine.matches("---")) { // end of file block
+                            startLines = -1;
+                            endLines = -1;
+                            current = null;
+                            return null;
+                        }
+                    }
+                    linesNumbersLine = linesNumbersLine.substring(0, linesNumbersLine.lastIndexOf("@@") + 2);
+                    linesNumbersLine = linesNumbersLine.replaceAll(" @@", "");
+                    linesNumbersLine = linesNumbersLine.replaceAll("@@ .*\\+", "");
+                    String[] split = linesNumbersLine.split(",");
+
+                    startLines = Integer.parseInt(split[0]);
+                    endLines = Integer.parseInt(split[1]) + startLines;
+                }
+            } else if (current != null) {
+                return null;
+            }
+
+            String plusLine = null;
+            while (plusLine == null || !(plusLine.startsWith("+"))) {
+                plusLine = in.readLine(); // searching '+' statement
+                if (plusLine == null) {
+                    startLines = -1;
+                    endLines = -1;
+                    current = null;
+                    return null;
+                }
+                if (!plusLine.startsWith("-")) {
+                    ++startLines;
+                }
+                if (plusLine.startsWith("---")) {
+                    startLines = -1;
+                    endLines = -1;
+                    current = plusLine;
+                    return null;
+                } else if (plusLine.matches(LINES_NUMBERS_PATTERN)) {
+                    startLines = -1;
+                    endLines = -1;
+                    current = plusLine;
+
+                    return getNextSourceLine();
+                }
+            }
+            // plusLine is not null if we are here
+            SourceLine line = new SourceLine(startLines - 1, plusLine.substring(1));
+            if (startLines > endLines) {
+                startLines = -1;
+                endLines = -1;
+                current = null;
+            }
+            return line;
+        }
+    }
+    static OptionDescr DSC_REPLACE_DIFF = new OptionDescr("replaceDiff", "Manage replacing", OptionDescr.VAL_SINGLE, "Set replacement pattern for diff filenames (e.g. to cut out \"src/classes\" you can specify -replaceDiff src/classes:)");
+    static OptionDescr DSC_REPLACE_CLASS = new OptionDescr("replaceClass", "", OptionDescr.VAL_SINGLE, "Set replacement pattern for class filenames (e.g. to cut out \"com/sun\" you can specify -replaceDiff com/sun:)");
+    static OptionDescr DSC_ALL = new OptionDescr("all", "Manage output", OptionDescr.VAL_NONE, "Show covered and non-code lines as well as not covered");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/Exec.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.constants.MiscConstants;
+import com.sun.tdk.jcov.data.FileFormatException;
+import com.sun.tdk.jcov.data.Result;
+import com.sun.tdk.jcov.insert.AbstractUniversalInstrumenter;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.processing.ProcessingException;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.logging.FileHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p> This tool is designed to simplify JCov Grabber usage. It starts a
+ * grabber, then executes specified command and waits while command is active.
+ * After finishing the command Exec will stop the grabber. </p> <p> Exec also
+ * can instrument the product with -instr option. Instrumentation is performed
+ * recursively so that entire product is copied to new location and every single
+ * binary file (class or jar/zip/war) will be instrumented. </p>
+ *
+ * @author Andrey Titov
+ */
+public class Exec extends JCovCMDTool {
+
+    static final Logger logger;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(Exec.class.getName());
+    }
+    private String command[];
+    private Grabber grabber;
+    private String outLogFile;
+    private String errLogFile;
+    private String grabberLogFile;
+    private String template = MiscConstants.JcovTemplateFileNameXML;
+    private String outTestList;
+    private String outputFile = MiscConstants.JcovSaveFileNameXML;
+    private File commandDir = null;
+    // Product instrumentation
+    private ProductInstr pInstr;
+    // Report generation
+    private File reportDir;
+
+    private void instrumentProduct() throws Exception {
+        pInstr.instrumentProduct();
+    }
+
+    private void runCommand() throws IOException, Exception {
+        if (pInstr != null || reportDir != null) {
+            logger.log(Level.INFO, " - Starting command");
+        }
+        logger.log(Level.CONFIG, "Command to run: ''{0}''", Arrays.toString(command));
+
+        OutputStream outStream = null;
+        OutputStream errStream = null;
+        Process proc = null;
+        try {
+            grabber.createServer();
+            logger.log(Level.INFO, "Starting the Grabber");
+            grabber.startServer();
+
+            if (outLogFile != null) {
+                outStream = new FileOutputStream(outLogFile);
+            } else {
+                outStream = System.out;
+            }
+
+            if (errLogFile != null) {
+                if (errLogFile.equals(outLogFile)) {
+                    errStream = outStream;
+                } else {
+                    errStream = new FileOutputStream(errLogFile);
+                }
+            } else {
+                if (outLogFile != null) {
+                    errStream = outStream;
+                } else {
+                    errStream = System.err;
+                }
+            }
+
+            ProcessBuilder pb =
+                    new ProcessBuilder(command)
+                    .redirectErrorStream(errStream == outStream)
+                    .directory(commandDir);
+
+            pb.environment().put("JCOV_PORT", Integer.toString(grabber.getServerPort()));
+            logger.log(Level.INFO, "Starting the command");
+            proc = pb.start();
+
+            InputStream inputStream = proc.getInputStream();
+            new Thread(new Redirector(inputStream, outStream)).start();
+            if (errStream != outStream) {
+                new Thread(new Redirector(proc.getErrorStream(), errStream)).start();
+            }
+
+            int exitStatus = proc.waitFor();
+
+            if (exitStatus == 0) {
+                logger.log(Level.FINE, "Command finished with 0");
+            } else {
+                logger.log(Level.WARNING, "Command finished with {0}", exitStatus);
+            }
+
+            proc = null;
+        } catch (Throwable e) {
+            e.printStackTrace();
+        } finally {
+            if (proc != null) {
+                logger.log(Level.INFO, "Destroying the process...");
+                proc.destroy();
+            }
+            if (outLogFile != null) {
+                outStream.close();
+                if (outStream != errStream) {
+                    errStream.close();
+                }
+            }
+            try {
+                logger.log(Level.INFO, "Stopping the grabber (not forcely)...");
+                grabber.stopServer(false);
+            } catch (Throwable e) {
+                e.printStackTrace();
+            }
+
+            if (!new File(outputFile).exists()) {
+                throw new Exception("Output file " + outputFile + " was not created after grabber stop." + (reportDir != null ? " Can't write the report." : ""));
+            }
+        }
+    }
+
+    private void createReport() throws ProcessingException, FileFormatException, Exception {
+        logger.log(Level.INFO, " - Generating report");
+        logger.log(Level.CONFIG, "Output report directory: ''{0}''", reportDir.getPath());
+        RepGen rg = new RepGen();
+        rg.configure(null, null, null, null, false, false, false, false, false, false, false, false);
+
+        Result res;
+        if (outTestList == null) {
+            res = new Result(outputFile);
+        } else {
+            res = new Result(outputFile, outTestList);
+        }
+        rg.generateReport(reportDir.getAbsolutePath(), res);
+    }
+
+    @Override
+    protected int run() throws Exception {
+        if (pInstr != null) {
+            instrumentProduct();
+        }
+
+        runCommand();
+
+        if (reportDir != null) {
+            createReport();
+        }
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    DSC_TEST_COMMAND,
+                    DSC_TEST_COMMANDS,
+                    DSC_COMMAND_DIR,
+                    Grabber.DSC_PORT,
+                    Grabber.DSC_OUTPUT,
+                    Merger.DSC_OUTPUT_TEST_LIST,
+                    DSC_REDIRECT_OUT,
+                    DSC_REDIRECT_ERR,
+                    DSC_GRABBER_REDIRECT,
+                    ProductInstr.DSC_INSTRUMENT,
+                    ProductInstr.DSC_INSTRUMENT_TO,
+                    ProductInstr.DSC_RT_TO,
+                    Instr.DSC_INCLUDE_RT,
+                    DSC_REPORT,
+                    InstrumentationOptions.DSC_TEMPLATE,}, this);
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler envHandler) throws EnvHandlingException {
+        if (envHandler.isSet(DSC_TEST_COMMAND)) {
+            command = splitNoQuotes(envHandler.getValue(DSC_TEST_COMMAND));
+            if (envHandler.isSet(DSC_TEST_COMMANDS)) {
+                logger.log(Level.CONFIG, "'-commands' option ignored as '-command' option specified");
+            }
+        } else if (envHandler.isSet(DSC_TEST_COMMANDS)) {
+            command = envHandler.getValues(DSC_TEST_COMMANDS);
+        } else {
+            throw new EnvHandlingException("'-command' or '-commands' option needed");
+        }
+
+        if (envHandler.isSet(DSC_COMMAND_DIR)) {
+            commandDir = Utils.checkFileNotNull(envHandler.getValue(DSC_COMMAND_DIR), "command dir");
+        }
+
+        if (envHandler.isSet(ProductInstr.DSC_INSTRUMENT)) {
+            pInstr = new ProductInstr();
+            pInstr.handleEnv(envHandler);
+        }
+
+        reportDir = Utils.checkFileCanBeNull(envHandler.getValue(DSC_REPORT), "report directory", Utils.CheckOptions.FILE_NOTEXISTS, Utils.CheckOptions.FILE_CANWRITE);
+
+        if (envHandler.isSet(DSC_GRABBER_REDIRECT)) {
+            grabberLogFile = envHandler.getValue(DSC_GRABBER_REDIRECT);
+            Logger grLogger = Logger.getLogger(Grabber.class.getName());
+            grLogger.setUseParentHandlers(false);
+            try {
+                grLogger.addHandler(new FileHandler(grabberLogFile));
+            } catch (Exception ex) {
+                throw new EnvHandlingException("Error opening file for logging grabber: " + ex.getMessage(), ex);
+            }
+        }
+
+        grabber = new Grabber();
+        grabber.handleEnv(envHandler);
+
+        if (envHandler.isSet(DSC_REDIRECT_ERR)) {
+            errLogFile = envHandler.getValue(DSC_REDIRECT_ERR);
+            Utils.checkFileNotNull(errLogFile, "error log file", Utils.CheckOptions.FILE_CANWRITE, Utils.CheckOptions.FILE_NOTISDIR);
+        }
+        if (envHandler.isSet(DSC_REDIRECT_OUT)) {
+            outLogFile = envHandler.getValue(DSC_REDIRECT_OUT);
+            Utils.checkFileNotNull(outLogFile, "output log file", Utils.CheckOptions.FILE_CANWRITE, Utils.CheckOptions.FILE_NOTISDIR);
+        }
+
+        if (envHandler.isSet(InstrumentationOptions.DSC_TEMPLATE)) {
+            template = envHandler.getValue(InstrumentationOptions.DSC_TEMPLATE);
+            Utils.checkFileNotNull(template, "template file", Utils.CheckOptions.FILE_CANREAD, Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_EXISTS);
+        }
+
+        if (envHandler.isSet(Merger.DSC_OUTPUT_TEST_LIST)) {
+            outTestList = envHandler.getValue(Merger.DSC_OUTPUT_TEST_LIST);
+            Utils.checkFileNotNull(outTestList, "output testlist file", Utils.CheckOptions.FILE_CANWRITE, Utils.CheckOptions.FILE_NOTISDIR, Utils.CheckOptions.FILE_NOTEXISTS);
+        }
+
+        if (envHandler.isSet(Grabber.DSC_OUTPUT)) {
+            outputFile = envHandler.getValue(Grabber.DSC_OUTPUT);
+            Utils.checkFileNotNull(outputFile, "output file", Utils.CheckOptions.FILE_CANWRITE, Utils.CheckOptions.FILE_NOTISDIR);
+        }
+
+        return 0;
+    }
+
+    @Override
+    protected String getDescr() {
+        return "Executes a command collecting coverage data in a grabber";
+    }
+
+    @Override
+    protected String usageString() {
+        return "java -jar jcov.jar exec -command <command to run> [-option value]";
+    }
+
+    @Override
+    protected String exampleString() {
+        return "java -jar jcov.jar exec -command \"./runtests.sh -testoptions:\\\"-javaagent:jcov.jar=grabber=\\\"\"";
+    }
+
+    /**
+     * <p> Splits a string containing quotes ' and " by spaces. Everything
+     * quoted is not splitted. Non-quoted quotes are removed. </p>
+     *
+     * @param str
+     * @return
+     */
+    public static String[] splitNoQuotes(String str) {
+        Character quote = null;
+
+        LinkedList<String> r = new LinkedList<String>();
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < str.length(); ++i) {
+            char charAt = str.charAt(i);
+            if (quote == null) {
+                switch (charAt) {
+                    case '\'':
+                    case '\"':
+                        quote = charAt;
+                        break;
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\f':
+                    case '\r':
+                    case 0x0B:
+                        if (sb.length() > 0) {
+                            r.add(sb.toString());
+                            sb = new StringBuilder();
+                        }
+                        break;
+                    default:
+                        sb.append(charAt);
+                }
+            } else {
+                if (charAt == quote) {
+                    quote = null;
+                } else {
+                    sb.append(charAt);
+                }
+            }
+        }
+        if (sb.length() > 0) {
+            r.add(sb.toString());
+        }
+
+        return r.toArray(new String[r.size()]);
+    }
+    public static final OptionDescr DSC_TEST_COMMAND =
+            new OptionDescr("command", new String[]{"comm", "cmd"}, "Exec commands",
+            OptionDescr.VAL_SINGLE, "Command running tests over instrumented classes or using JCov agent. Use quotes for arguments: -command \"./tests.sh -arg1 -arg2 arg3\"");
+    public static final OptionDescr DSC_COMMAND_DIR =
+            new OptionDescr("command.dir", new String[]{"dir"}, "", OptionDescr.VAL_SINGLE, "Specify directory to run the command");
+    public static final OptionDescr DSC_TEST_COMMANDS =
+            new OptionDescr("commands", new String[]{"comms", "cmds"}, "",
+            OptionDescr.VAL_ALL, "Command running tests over instrumented classes or using JCov agent. Use quotes for arguments: -command \"./tests.sh -arg1 -arg2 arg3\"");
+    public final static OptionDescr DSC_REDIRECT_OUT =
+            new OptionDescr("out.file", new String[]{"out", "log.command"}, "", OptionDescr.VAL_SINGLE, "Redirect command output to a file");
+    public final static OptionDescr DSC_REDIRECT_ERR =
+            new OptionDescr("err.file", new String[]{"err"}, "", OptionDescr.VAL_SINGLE, "Redirect command error output to a file");
+    public final static OptionDescr DSC_GRABBER_REDIRECT =
+            new OptionDescr("log.grabber", "", OptionDescr.VAL_SINGLE, "Redirect grabber output to a file");
+    public final static OptionDescr DSC_REPORT =
+            new OptionDescr("report", "", OptionDescr.VAL_SINGLE, "");
+
+    public static class Redirector implements Runnable {
+
+        private final OutputStream out;
+        private final BufferedReader rin;
+        private final BufferedWriter rout;
+
+        public Redirector(InputStream in, OutputStream out) {
+            rin = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()));
+            this.out = out;
+            rout = new BufferedWriter(new OutputStreamWriter(out, Charset.defaultCharset()));
+        }
+
+        public void run() {
+            String s;
+
+            try {
+                while ((s = rin.readLine()) != null) {
+                    // out stream can be used in 2 threads simultaneously
+                    synchronized (out) {
+                        rout.write(s);
+                        rout.newLine();
+                        rout.flush();
+                    }
+                }
+            } catch (Throwable ex) {
+                ex.printStackTrace();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/Filter.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.filter.ConveyerFilter;
+import com.sun.tdk.jcov.filter.FilterSpi;
+import com.sun.tdk.jcov.filter.MemberFilter;
+import com.sun.tdk.jcov.instrument.DataRoot;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions.MERGE;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.io.ClassSignatureFilter;
+import com.sun.tdk.jcov.io.Reader;
+import com.sun.tdk.jcov.runtime.JCovXMLFileSaver;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.tools.SPIDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * The class that applies a custom filter to coverage data file. It has a
+ * command line entry point. It also can be used from other tools.
+ *
+ * Usage examples:
+ * <pre>
+ *   Filter filter = new Filter("plugin.myFilterSpi");
+ *   filter.applyFilter("result.xml", "new.xml");
+ * </pre> or from command line:
+ * <pre>
+ * # java com.sun.tdk.jcov.Filter -i com.sun.* -filter plugin.myFilterSpi result.xml new.xml
+ * </pre>
+ *
+ * @author Dmitry Fazunenko
+ * @author Sergey Borodin
+ */
+public class Filter extends JCovCMDTool {
+
+    private String inFile = null;
+    private String outFile = null;
+    // primitive filter by class name include/exclude
+    private ClassSignatureFilter readFilter;
+    private MemberFilter filter;
+    private String[] filterSpi = null;
+    private boolean synthetic = false;
+
+    /**
+     *
+     * Reads inFile, applies filter and writes result down to outFile.
+     *
+     * @param inFile - file to read
+     * @param outFile - file to create (if null, System.out will be used)
+     * @throws Exception
+     */
+    public void applyFilter(String inFile, String outFile)
+            throws Exception {
+        DataRoot root = Reader.readXML(inFile, true, readFilter);
+
+        if (synthetic) {
+            root.applyFilter(new RepGen.ANC_FILTER());
+        }
+
+        if (filter != null) {
+            root.applyFilter(filter);
+        }
+        root.getParams().setIncludes(readFilter.getIncludes());
+        root.getParams().setExcludes(readFilter.getExcludes());
+        if (readFilter.getModifs() != null && readFilter.getModifs().length > 0) {
+            root.getXMLHeadProperties().put("coverage.generator.modif", Arrays.toString(readFilter.getModifs()));
+        } else {
+            root.getXMLHeadProperties().remove("coverage.generator.modif");
+        }
+        if (filterSpi != null) {
+            String get = root.getXMLHeadProperties().get("coverage.filter.plugin");
+            root.getXMLHeadProperties().put("coverage.filter.plugin", Arrays.toString(filterSpi));
+        } else {
+            root.getXMLHeadProperties().remove("coverage.filter.plugin");
+        }
+        new JCovXMLFileSaver(root, MERGE.OVERWRITE).saveResults(outFile);
+        root.destroy();
+    }
+
+    /**
+     * Entry point for command line.
+     *
+     * @param args
+     */
+    public static void main(String args[]) {
+        Filter tool = new Filter();
+        try {
+            int res = tool.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+
+    protected String usageString() {
+        return "java " + Filter.class.getCanonicalName() + " [options] inFile outFile";
+    }
+
+    protected String exampleString() {
+        return "java -cp jcov.jar:filter_classes "
+                + Filter.class.getCanonicalName() + " -include java.lang.* "
+                + "-filter java.xml lang.xml";
+    }
+
+    protected String getDescr() {
+        return "filters out result data";
+    }
+
+///////// JCovTool implementation /////////
+    @Override
+    protected EnvHandler defineHandler() {
+        EnvHandler eh = new EnvHandler(new OptionDescr[]{
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FM,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FM_LIST,
+                    DSC_FILTER_PLUGIN,
+                    DSC_SYNTHETIC
+                }, this);
+        eh.registerSPI(new SPIDescr("filter", FilterSpi.class));
+        return eh;
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        String[] files = opts.getTail();
+        if (files == null || files.length == 0) {
+            throw new EnvHandlingException("no input file specified");
+        } else if (files.length == 1) {
+            throw new EnvHandlingException("no output file specified");
+        } else if (files.length == 2) {
+            inFile = files[0];
+            Utils.checkFileNotNull(inFile, "input JCov datafile", Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_CANREAD);
+            outFile = files[1];
+            Utils.checkFileNotNull(outFile, "output JCov datafile", Utils.CheckOptions.FILE_NOTEXISTS, Utils.CheckOptions.FILE_CANWRITE);
+        } else if (files.length > 2) {
+            throw new EnvHandlingException("too many files specified");
+        }
+
+        synthetic = opts.isSet(DSC_SYNTHETIC);
+
+        String[] exclude = InstrumentationOptions.handleExclude(opts);
+        String[] include = InstrumentationOptions.handleInclude(opts);
+        String[] fm = InstrumentationOptions.handleFM(opts);
+
+        readFilter = new ClassSignatureFilter(include, exclude, fm);
+
+        ArrayList<FilterSpi> filters = opts.getSPIs(FilterSpi.class);
+        if (filters == null || filters.isEmpty()) {
+            filter = null;
+        } else if (filters.size() == 1) {
+            filter = filters.get(0).getFilter();
+        } else {
+            ConveyerFilter cf = new ConveyerFilter();
+            for (FilterSpi spi : filters) {
+                cf.add(spi.getFilter());
+            }
+            filter = cf;
+        }
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected int run() throws Exception {
+        try {
+            applyFilter(inFile, outFile);
+        } catch (Exception e) {
+            if (filter != null) {
+                if (e instanceof NullPointerException) {
+                    e.printStackTrace();
+                }
+                throw new Exception("Cannot apply filter '" + filter + "' to " + inFile + " - exception occured: " + e.getMessage(), e);
+            } else {
+                throw new Exception("Mishap:", e);
+            }
+        }
+        return SUCCESS_EXIT_CODE;
+    }
+
+    /**
+     * @return ClassSignatureFilter used by this Filter to read the DataRoot
+     */
+    public ClassSignatureFilter getReadingFilter() {
+        return readFilter;
+    }
+
+    /**
+     * Set the ClassSignatureFilter used by this Filter to read the DataRoot
+     *
+     * @param acceptor
+     */
+    public void setReadingFilter(ClassSignatureFilter acceptor) {
+        this.readFilter = acceptor;
+    }
+
+    /**
+     * @return MemberFilter used by this Filter
+     */
+    public MemberFilter getFilter() {
+        return filter;
+    }
+
+    /**
+     * Set MemberFilter used by this Filter
+     *
+     * @param filter
+     */
+    public void setFilter(MemberFilter filter) {
+        this.filter = filter;
+    }
+    final static OptionDescr DSC_FILTER_PLUGIN =
+            new OptionDescr("filter", "", OptionDescr.VAL_MULTI,
+            "Custom filtering plugin class");
+    public final static OptionDescr DSC_SYNTHETIC =
+            new OptionDescr("synthetic", "Additional filtering", "Remove coverage for synthetic methods");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/Grabber.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,1940 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.constants.MiscConstants;
+import com.sun.tdk.jcov.data.FileFormatException;
+import com.sun.tdk.jcov.data.Result;
+import com.sun.tdk.jcov.instrument.DataRoot;
+import com.sun.tdk.jcov.instrument.DataRoot.CompatibilityCheckResult;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions.MERGE;
+import com.sun.tdk.jcov.runtime.Collect;
+import com.sun.tdk.jcov.runtime.FileSaver;
+import com.sun.tdk.jcov.runtime.PropertyFinder;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.JcovVersion;
+import com.sun.tdk.jcov.tools.LoggingFormatter;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.RuntimeUtils;
+import com.sun.tdk.jcov.util.Utils;
+import com.sun.tdk.jcov.util.Utils.Pair;
+import java.io.*;
+import java.net.*;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Properties;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Class Client serves to collect data from remote client. isWorking() should
+ * return true until all data is processed by Server.
+ *
+ * @author Dmitry Fazunenko
+ * @author Alexey Fedorchenko
+ *
+ * @see #isWorking()
+ */
+class Client extends Thread {
+
+    private final Socket socket;                    // socket
+    private final Server server;                    // server
+    private final int clientNumber;                 // number of this client
+    private boolean working = true;                 // set to false when data is read
+    public static int unknownTestNumber = 0;       // number of unknown tests got from clients
+    // runtime data - got from client side
+    private String testName;                        // test name
+    private String testerName;                      // tester name
+    private String productName;                     // product name
+    private int slotNumber;                         // slots number (in static mode)
+    private static final int MAX_SLOTS = Collect.MAX_SLOTS;
+    public static final String UNKNOWN = "<unknown>";
+
+    /**
+     * Constructor for Client class Sets "Client{clientNumber}Thread" as the
+     * name of the thread
+     *
+     * @param server Server object this client is connected to
+     * @param socket Remote clients socket
+     * @param clientNumber ID of this client
+     */
+    public Client(Server server, Socket socket, int clientNumber) {
+        super();
+
+        setName("Client" + clientNumber + "Thread");
+        setDaemon(false);
+        this.socket = socket;
+        this.server = server;
+        this.clientNumber = clientNumber;
+    }
+
+    /**
+     * <p> Main method. Use start() to launch in new thread. </p> <p> Reads data
+     * as long[] or as DataRoot and forwards it to the Server </p> <p> Sets
+     * accepted test name, tester name and product name. Use getTestName(),
+     * getTesterName() and getProductName() to read them. </p>
+     */
+    @Override
+    public void run() {
+        executeClient();
+    }
+
+    public void executeClient() {
+        DataInputStream IN = null;
+        byte buff[];
+        boolean legacy = false, dynamic = false;
+        int version;
+
+        long reserve = 0L;
+        try {
+            // synchronized (Server.STOP_THE_WORLD_LOCK) {
+            //     server.checkFreeMemory(30000000L); // 30mb for a client seems to be OK
+               /*     while (server.isDumping()) { // locks the thread when is dumping
+             Server.STOP_THE_WORLD_LOCK.wait();
+             }*/
+            IN = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
+            buff = new byte[4];
+            for (int i = 0; i < buff.length; ++i) {
+                buff[i] = IN.readByte();
+            }
+
+            if (!new String(buff, "UTF-8").equals("JCOV")) {
+                reserve = 10000000L; // 10mb
+                legacy = true;
+            } else {
+                version = IN.read();
+                testerName = IN.readUTF();
+                testName = IN.readUTF();
+                productName = IN.readUTF();
+                if ("".equals(testName)) {
+                    testName = testName + " " + testerName + " " + productName + "_test" + ++unknownTestNumber;
+                }
+                Grabber.logger.log(Level.FINE, "Got test data: name {0}, tester {1}, product {2}", new Object[]{testName, testerName, productName});
+                dynamic = IN.readBoolean();
+                if (dynamic) {
+                    reserve = 50000000L; // 50mb
+                } else {
+                    reserve = Collect.SLOTS * 10;
+                }
+            }
+            //server.increaseReserved(reserve);
+            //} // release synchronized (Server.STOP_THE_WORLD_LOCK)
+
+            if (legacy) {
+                Grabber.logger.log(Level.FINE, "Header missmatch from client N{0}: '{1}'. Reading as legacy", new Object[]{clientNumber + "", new String(buff, "UTF-8")});
+                // reading in old format - not to forget include first 8 bytes
+                // in old format we can't get less or more than 1bill longs - so reading them all in long[1000000]
+                testerName = UNKNOWN;
+                testName = "test" + ++unknownTestNumber;
+                productName = UNKNOWN;
+                long ids[] = new long[1000000];
+                // copying the first long to the result array
+                for (int j = 0; j < 4; ++j) {
+                    ids[0] += ((long) buff[j] & 0xffL) << (8 * j);
+                }
+                int i = 1;
+                try {
+                    for (; i < 1000000; ++i) {
+                        ids[i] = IN.readLong();
+                    }
+                } catch (EOFException e) {
+                    Grabber.logger.log(Level.SEVERE, "Got incorrect number of longs from legacy-format client N{0}: {1}, expected 1 000 000", new Object[]{clientNumber + "", i});
+                    return;
+                }
+                IN.close();
+                IN = null;
+
+                Grabber.logger.log(Level.FINER, "Got legacy-format static data from client N{0}", clientNumber + "");
+                saveResults(ids);
+
+                return;
+            } else {
+                if (dynamic) {
+                    DataRoot root = new DataRoot(IN);
+                    slotNumber = root.getCount();
+                    IN.close();
+                    IN = null;
+                    saveResults(root);
+                } else {
+                    String templateHash = IN.readUTF();
+                    slotNumber = IN.readInt();
+                    int lastIndex = IN.readInt();
+                    long ids[] = new long[lastIndex + 1];
+                    int i = 0;
+                    try {
+                        for (; i < slotNumber; ++i) {
+                            int index = IN.readInt();
+                            ids[index] = IN.readLong();
+                        }
+                    } catch (EOFException e) {
+                        Grabber.logger.log(Level.SEVERE, "Got incorrect number of longs from static client N{0}: found {1}, expected {2}", new Object[]{clientNumber + "", i, slotNumber});
+                    } catch (IOException ioe) {
+                        Grabber.logger.log(Level.WARNING, "Got incorrect number of longs from static client N{0}: found {1}, expected {2}", new Object[]{clientNumber + "", i, slotNumber});
+                    }
+                    IN.close();
+                    IN = null;
+                    socket.close();
+
+                    Grabber.logger.log(Level.FINER, "Got new-format static data from client N{0}", clientNumber + "");
+                    saveResults(ids);
+                }
+            }
+        } catch (Exception e) {
+            Grabber.logger.log(Level.SEVERE, "e =" + e);
+            Grabber.logger.log(Level.SEVERE, "Error while receiving data from client N" + clientNumber, e);
+            //            e.printStackTrace();
+        } finally {
+            server.decreaseReserved(reserve);
+            // java7+try-with-resources rules
+            if (IN != null) {
+                try {
+                    IN.close();
+                } catch (IOException ex) {
+                }
+            }
+            if (!socket.isClosed()) {
+                try {
+                    socket.close();
+                } catch (IOException ex) {
+                }
+            }
+            --server.aliveClients;
+            working = false; // all is done
+            Grabber.logger.log(Level.FINE, "Client N{0} done", clientNumber + "");
+        }
+    }
+
+    /**
+     * Handle got data. Translates data to the Server.
+     *
+     * @param ids Data in long[] form transferred from client
+     */
+    private void saveResults(long[] ids) {
+        server.handleData(ids, this);
+    }
+
+    /**
+     * Handle got data. Translates data to the Server.
+     *
+     * @param root Data in DataRoot transferred from client
+     */
+    private void saveResults(DataRoot root) {
+        server.handleData(root, this);
+    }
+
+    /**
+     * Returns client address
+     *
+     * @return Client address formatted as 'host:port'
+     * @see #Client(com.sun.tdk.jcov.Server, java.net.Socket, int)
+     */
+    public String getClientAddress() {
+        return socket.getInetAddress().getHostName() + ":" + socket.getLocalPort();
+    }
+
+    /**
+     * Returns ID assigned to this Client
+     *
+     * @return ID assigned to this Client
+     * @see #Client(com.sun.tdk.jcov.Server, java.net.Socket, int)
+     */
+    public int getClientNumber() {
+        return clientNumber;
+    }
+
+    /**
+     * Answers whether Client have finished transactions.
+     *
+     * @return false when all transactions are finished (data received and
+     * translated to Server or Exception occurred)
+     */
+    public boolean isWorking() {
+        return working;
+    }
+
+    /**
+     * @return test name accepted from client side or generated test name
+     */
+    public String getTestName() {
+        return testName;
+    }
+
+    /**
+     * @return tester name accepted from client side or &lt;unknown&gt;
+     */
+    public String getTesterName() {
+        return testerName;
+    }
+
+    /**
+     * @return product name accepted from client side or &lt;unknown&gt;
+     */
+    public String getProductName() {
+        return productName;
+    }
+} // ############# Client
+
+/**
+ * Server class manages incoming client connections and handles coverage data
+ * coming from them.
+ *
+ * @see #handleData(long[], com.sun.tdk.jcov.Client)
+ */
+class Server extends Thread {
+    // config data
+
+    private String fileName;                // output file
+    private final String templateName;      // template file
+    private final int port;                 // port to listen for client connections
+    private int saveCount;                  // maximum connections to manage. Infinite if 0
+    private boolean saveAtReceive;          // true = save data when it's coming from Client
+    private final String hostName;          // host to connect in 'once' mode
+    private final boolean once;             // enables 'once' mode
+    private String outTestList;             // output testlist file
+    private boolean genscale;               // allows to generate scales without outtestlist
+    private String saveBadData;             // path to save bad agent data to
+    // status data
+    private boolean working = true;         // thread is working (not dead). Set to false at kill() method and when exiting main loop.
+    private boolean listening = true;       // thread is listening for new connections. Set to false when exiting main loop.
+    private boolean dataSaved = false;      // no notsaved data. Set to true when accepting new data, set to false when writing data.
+    private boolean started = false;        // set to true when all initialization is done.
+    // runtime data
+    private ServerSocket ss = null;         // socket listener
+    private long[] data = null;             // data got from clients. Not used in SaveAtReceive mode
+    private DataRoot dataRoot = null;       // data got from clients. Not used in SaveAtReceive mode
+    private int totalConnections;           // connections occurred
+    int aliveClients = 0;                   // increased on client.start(), decreased in client
+    private LinkedList<String> tests;       // accepted testnames
+    private static int MAX_TIMEOUT = 90000; // maximum time to wait for Client working
+    static boolean showMemoryChecks = false; // show memory checks
+    final static Runtime rt = Runtime.getRuntime(); // runtime
+    private long reservedMemory = 0;        // memory reserved by alive clients - not used yet
+    private int dumpCount = 0;              // number of dumped files
+    private boolean dumping = false;        // dumping proccess is going on
+    static final Object STOP_THE_WORLD_LOCK = new Object(); // LOCK
+    private boolean veryLowMemoryRun = false; // very low memory mode - minimal memory lock, no merge
+    private boolean lowMemoryRun = false; // low memory mode - minimal memory lock, merge
+    private boolean normalMemoryRun = false; // normal memory mode - 45% memory lock, merge
+    private boolean mergeByTestNames = false;// generate scales based on test names (test name identifies test)
+    private ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+
+    /**
+     * <p> Constructor for Server class. Warning: ServerSocket will be opened in
+     * constructor so port will be binded. </p> <p> Sets "ServerThread" as the
+     * name of the thread. </p>
+     *
+     * @param port port to listen for client connections
+     * @param once enables 'once' mode - Server connects to hname:port and
+     * receives data from there
+     * @param template template file
+     * @param host host to connect in 'once' mode
+     * @param saveAtReceive true = save data when it's coming from Client
+     * @param genscale allows to generate scales without outtestlist
+     * @throws BindException
+     * @throws IOException
+     */
+    public Server(int port, boolean once, String template, String host, boolean saveAtRecieve, boolean genscale, boolean mergeByTestNames) throws BindException, IOException {
+        super();
+
+        setName("ServerThread");
+        setDaemon(false);
+
+        totalConnections = 0;
+
+        this.once = once;
+        this.templateName = template;
+        this.hostName = host;
+        this.saveAtReceive = saveAtRecieve;
+        this.genscale = genscale;
+        this.mergeByTestNames = mergeByTestNames;
+
+
+        if (this.genscale) {
+            tests = new LinkedList<String>();
+        }
+
+        if (this.once) {
+            this.port = port;
+            if (this.port == 0) {
+                throw new IllegalArgumentException("Port should be specified in 'once' mode");
+            }
+        } else {
+            try {
+                initDataRoot();
+
+                // checking available memory
+                if (genscale) {
+                    rt.gc();
+                    if (rt.totalMemory() > rt.maxMemory() / 1.2) {
+                        Grabber.logger.log(Level.WARNING, "Server started with very low memory: it''s recomended at least {0}M max memory for this template. Server will not write data dumps on low memory and can fail in OutOfMemoryError.", rt.totalMemory() * 3 / 1000000);
+                        veryLowMemoryRun = true;
+                    } else if (rt.totalMemory() > rt.maxMemory() / 2.5) {
+                        if (showMemoryChecks) {
+                            Grabber.logger.log(Level.WARNING, "Server started with low memory: it''s recomended at least {0}M max memory for this template. Server will write data dumps on low memory and will try to merge them on exit.", rt.totalMemory() * 2.5 / 1000000);
+                        }
+                        lowMemoryRun = true;
+                    } else {
+                        if (showMemoryChecks) {
+                            Grabber.logger.log(Level.INFO, "Server will write data dumps on 45% free memory and will try to merge them on exit.");
+                        }
+                        normalMemoryRun = true;
+                    }
+                }
+            } catch (FileFormatException ex) {
+                throw new IllegalArgumentException("Bad template: " + templateName, ex);
+            }
+            this.ss = new ServerSocket(port);
+            this.port = ss.getLocalPort(); // updating port number - when port == 0 ServerSocket will take any free port
+        }
+    }
+
+    /**
+     * <p> Constructor for Server class. Warning: ServerSocket will be opened in
+     * constructor so port will be binded. </p> <p> Sets "ServerThread" as the
+     * name of the thread </p>
+     *
+     * @param port port to listen for client connections
+     * @param once enables 'once' mode - Server connects to hname:port and
+     * receives data from there
+     * @param template template file
+     * @param output output file
+     * @param outTestList file to write testlist to
+     * @param host host to connect in 'once' mode
+     * @param maxCount maximum connections to manage. Infinite if 0
+     * @param saveAtReceive true = save data when it's coming from Client
+     * @param genscale allows to generate scales without outtestlist
+     * @throws BindException
+     * @throws IOException
+     */
+    Server(int port, boolean once, String template, String output, String outTestList, String host, int maxCount,
+            boolean saveAtReceive, boolean genscale, boolean mergeByTestNames) throws BindException, IOException {
+        this(port, once, template, host, saveAtReceive, genscale || outTestList != null, mergeByTestNames);
+
+        this.fileName = output;
+        this.outTestList = outTestList;
+        this.saveCount = maxCount;
+    }
+
+    @Override
+    public synchronized void start() {
+        super.start();
+    }
+
+    /**
+     * Main method. Use start() to launch in new thread. 1. In 'once' mode
+     * Server opens Socket to hostName:port, creates a Client object for this
+     * Socket and receives data from it (all in one thread). Then it dies. 2. In
+     * other case Server starts accepting client connections by ServerSocket.
+     * For every connected client Server creates Client object and starts it in
+     * new thread waiting for next connection. Client receives data and
+     * translates it back to Server. Server will stop when maxCount connections
+     * will be received or at system shutdown (eg Ctrl-C) or when kill() method
+     * will be called (eg by CommandListener)
+     */
+    @Override
+    public void run() {
+        if (once) { // 'client' mode - single data connection to hostName:port
+            started = true;
+            InetAddress adr;
+            try {
+                if (hostName == null) {
+                    adr = InetAddress.getLocalHost();
+                } else {
+                    adr = InetAddress.getByName(hostName);
+                }
+
+                Socket s = new Socket(adr, port);
+
+                Grabber.logger.log(Level.INFO, "Connection established with {0}:{1}", new Object[]{adr, port});
+                ++aliveClients;
+                new Client(this, s, 0).executeClient(); // running client in local thread
+                saveData();
+                Grabber.logger.log(Level.FINE, "Server stopped");
+            } catch (Exception e) {
+                Grabber.logger.log(Level.SEVERE, "Jcov grabber failed", e);
+            } finally {
+                working = false;
+            }
+        } else { // server mode - receive data saveCount times (or any if 0)
+            started = true;
+            try {
+                int n = 0;
+                do {
+                    Grabber.logger.log(Level.FINE, "Waiting for new connection");
+
+                    Socket s = ss.accept();
+                    Client c = new Client(this, s, ++n);
+                    Grabber.logger.log(Level.INFO, "Connection N{0} received from {1}:{2}",
+                            new Object[]{n + "", s.getInetAddress(), s.getLocalPort() + ""}); // s.getLocalPort() + "" - to avoid int formatting
+                    Grabber.logger.log(Level.FINE, "Alive connections: {0}; total connections: {1}", new Object[]{aliveClients + 1 + "", totalConnections + 1 + ""});
+                    ++aliveClients;
+                    executor.execute(c);
+                    //c.start();
+
+                    ++totalConnections;
+                } while (--saveCount != 0 && listening); // kill() sets listening to false to break the loop
+                if (saveCount == 0 && listening) { // normal exit
+                    kill(false); // ServerSocket will not produce exception on ss.close() as accept() is not waiting
+                }
+            } catch (Throwable e) {
+                if (listening) { // loop was broken without kill() -> real exception. When kill() is called - ServerSocket.close() is called so IOException is thrown
+                    LoggingFormatter.printStackTrace = true;
+                    Grabber.logger.log(Level.SEVERE, "JCov grabber failed", e);
+                    working = false;
+                }
+            } finally {
+                int timeout = 0;
+                while (working && timeout < MAX_TIMEOUT * 2) { // if working == true -> kill(false) was called when some client are still alive. Server waits for their ending.
+                    try {
+                        Thread.sleep(500);
+                        timeout += 500;
+                    } catch (InterruptedException ex) {
+                    }
+                }
+                ss = null;
+                working = false;
+                listening = false;
+                Grabber.logger.log(Level.FINE, "Server stopped");
+            }
+        }
+    }
+
+    /**
+     * Stop listening socket for new client connections and close socket
+     * listener. IOException will be thrown in run() method from ServerSocket as
+     * it will be closed.
+     *
+     * @param force when false - server will wait till all alive clients will
+     * end transmitting data
+     */
+    public void kill(boolean force) {
+        listening = false;
+        if (ss != null && !ss.isClosed()) {
+            try {
+                ss.close(); // killing socket listener
+            } catch (Exception ex) {
+                Grabber.logger.log(Level.SEVERE, "Error while closing socket", ex);
+            }
+        }
+
+        if (!force && clientsAlive()) {
+            Grabber.logger.log(Level.INFO, "Awaiting for finishing data transmission from {0} clients. Max timeout time: {1}ms", new Object[]{getAliveConnectionCount() + "", MAX_TIMEOUT});
+            waitForAliveClients(); // getting data from alive connected clients
+        }
+
+        if (!force) {
+            if (!dataSaved) {
+                saveData();
+                if (dataRoot != null) { // started without template
+                    dataRoot.destroy();
+                }
+                if (dumpCount > 0) {
+                    if (veryLowMemoryRun) { // not merging at very low - it's not possible
+                    } else {
+                        Grabber.logger.log(Level.WARNING, "Server is merging dumped data: {0} files should be merged", dumpCount + "");
+                        // merge datafiles
+                        Merger merger = new Merger();
+                        Result res[] = new Result[dumpCount + 1];
+                        try {
+                            res[0] = new Result(fileName, outTestList);
+                            for (int i = 0; i < dumpCount; ++i) {
+                                if (outTestList != null) {
+                                    res[i + 1] = new Result(fileName, outTestList + i);
+                                }
+                            }
+                        } catch (IOException ignore) {
+                        }
+                        System.out.println("Server.this.templateName = " + Server.this.templateName);
+                        Merger.Merge m = new Merger.Merge(res, Server.this.templateName);
+                        try {
+                            merger.mergeAndWrite(m, outTestList, fileName, null);
+                        } catch (OutOfMemoryError e) {
+                            Grabber.logger.log(Level.SEVERE, "OutOfMemoryError while merging dumped files. Please merge them manually: 'java -jar jcov.jar merger -o {0} -outTestList {1} {0}%{1} {0}0%{1}) {0}1%{1}1 ...'.", new Object[]{fileName, outTestList});
+                        } catch (Throwable ignore) {
+                        }
+
+                        for (int i = 0; i < dumpCount; ++i) {
+                            new File(fileName + dumpCount).delete();
+                            new File(outTestList + dumpCount).delete();
+                        }
+                        Grabber.logger.log(Level.INFO, "Merging done");
+                    }
+                }
+            }
+        }
+
+        executor.shutdown();
+        working = false;
+    }
+
+    /**
+     * Read template from templateName and initializes template
+     *
+     * @throws FileFormatException
+     */
+    private void initDataRoot() throws FileFormatException {
+        if (templateName != null) {
+            Grabber.logger.log(Level.FINE, "Server is reading template {0}", templateName);
+
+            this.dataRoot = DataRoot.read(templateName, false, null);
+            this.dataRoot.makeAttached();
+            Collect.SLOTS = this.dataRoot.getCount();
+            Collect.enableCounts();
+            this.data = Collect.counts();
+
+            if (genscale) { // creating empty scales
+                this.dataRoot.getScaleOpts().setReadScales(true);
+                if (outTestList != null) {
+                    this.dataRoot.getScaleOpts().setOutTestList(outTestList);
+                }
+                this.dataRoot.cleanScales(); // making zero scales
+            }
+
+            Grabber.logger.log(Level.FINER, "Server finished reading template", templateName);
+        } else {
+            // DataRoot will be created at first data accept
+        }
+    }
+
+    /**
+     * Receives data from Client and handles it. In SaveAtReceive mode Server
+     * saves data instantly by calling saveData(data) method. In SaveAtExit mode
+     * Server stores clients data and merges it in memory. Real saving will
+     * occur as saveData() method calls. Sets dataSaved to false
+     *
+     * @param data received clients data to handle
+     * @param client client received the data (used for logging)
+     * @see #saveData()
+     */
+    public synchronized void handleData(long[] data, Client client) {
+        if (!working) {
+            return;
+        }
+        if (templateName == null) {
+            Grabber.logger.log(Level.SEVERE, "Server can't accept static data - started without template");
+            return;
+        }
+        dataSaved = false;
+        Grabber.logger.log(Level.INFO, "Server got data from client N{0}", client.getClientNumber() + "");
+
+        if (saveAtReceive) {
+            Grabber.logger.log(Level.FINE, "Server is saving data from client N{0}", client.getClientNumber() + "");
+
+            for (int i = 0; i < data.length && i < dataRoot.getCount(); ++i) {
+                this.data[i] += data[i];
+            }
+
+            saveData(data);
+        } else {
+            if (this.data == null) {
+                // no need for Collect.enableCounts as it's performed in Collect.clinit
+                this.data = Collect.counts();
+            }
+
+            for (int i = 0; i < data.length && i < dataRoot.getCount(); ++i) {
+                this.data[i] += data[i];
+            }
+            if (genscale) {
+                dataRoot.addScales();
+                dataRoot.update();
+
+                boolean merged = false;
+                if (mergeByTestNames) {
+                    for (int i = 0; i < tests.size(); i++) {
+                        if (tests.get(i).equals(client.getTestName())) {
+
+                            ArrayList<Pair> list = new ArrayList<Pair>();
+                            list.add(new Utils.Pair(i, tests.size()));
+                            this.dataRoot.illuminateDuplicatesInScales(list);
+                            merged = true;
+                            break;
+                        }
+                    }
+                }
+
+                if (!merged) {
+                    tests.add(client.getTestName());
+                }
+
+            }
+        }
+
+        Grabber.logger.log(Level.FINEST, "Data from client N{0} saved", client.getClientNumber() + "");
+    }
+
+    /**
+     * Receives data from Client and handles it. In SaveAtReceive mode Server
+     * saves data instantly by calling saveData(data) method. In SaveAtExit mode
+     * Server stores clients data and merges it in memory. Real saving will
+     * occur as saveData() method calls. Sets dataSaved to false
+     *
+     * @param data received clients data to handle
+     * @param client client received the data (used for logging)
+     * @see #saveData()
+     */
+    public synchronized void handleData(DataRoot root, Client client) {
+        boolean merged = false;
+        try {
+            if (!working) {
+                return;
+            }
+            dataSaved = false;
+            Grabber.logger.log(Level.FINER, "Server got dynamic data from client N{0}", client.getClientNumber() + "");
+
+            if (saveAtReceive) {
+                try {
+                    root.write(fileName, MERGE.MERGE);
+                } catch (Exception ex) {
+                    Grabber.logger.log(Level.SEVERE, "Server can't save data from client N{0} to file '{1}' on recieve: {2}", new Object[]{client.getClientNumber() + "", fileName, ex.getMessage()});
+                }
+            } else {
+                Grabber.logger.log(Level.FINE, "Server is merging dynamic data from client N{0}", client.getClientNumber() + "");
+                root.getScaleOpts().setScaleSize(root.getScaleOpts().getScaleSize() + 1);
+                // dynamic data mode only - template == null. In this mode should save first data got as dataRoot
+                if (templateName == null && dataRoot == null) {
+                    dataRoot = root;
+                    dataRoot.attach();
+                    if (genscale && (dataRoot.getScaleOpts() == null || !dataRoot.getScaleOpts().needReadScales())) {
+                        dataRoot.getScaleOpts().setReadScales(true);
+                        dataRoot.getScaleOpts().setOutTestList(outTestList);
+                        dataRoot.getScaleOpts().setScaleSize(1);
+                        tests.add(client.getTestName());
+                    }
+                } else {
+                    // merging. First of all - checking compatibility.
+                    int severity = 0;
+                    // merge with template - different data is allowed - only template data will be merged
+                    if (templateName != null) {
+                        severity = 3;
+                    }
+                    CompatibilityCheckResult cc = this.dataRoot.checkCompatibility(root, severity, true);
+                    if (cc.errors == 0) {
+                        // merge + scale if needed
+                        if (genscale && mergeByTestNames) {
+                            for (int i = 0; i < tests.size(); i++) {
+                                if (tests.get(i).equals(client.getTestName())) {
+                                    this.dataRoot.merge(root, templateName == null);
+
+                                    ArrayList<Pair> list = new ArrayList<Pair>();
+                                    list.add(new Utils.Pair(i, tests.size()));
+                                    this.dataRoot.illuminateDuplicatesInScales(list);
+
+                                    merged = true;
+                                    break;
+                                }
+                            }
+
+                        }
+                        if (genscale && !merged) {
+                            this.dataRoot.merge(root, templateName == null);
+                            tests.add(client.getTestName());
+                            merged = true;
+                        }
+                        if (!genscale) {
+                            this.dataRoot.merge(root, templateName == null);
+                            merged = true;
+                        }
+
+                        Grabber.logger.log(Level.FINER, "Server finished merging dynamic data from client N{0}", client.getClientNumber() + "");
+                    } else {
+                        // data not compatible - flushing to file if selected
+                        if (saveBadData != null) {
+                            String filename = saveBadData + "_" + client.getTestName() + ".xml";
+                            Grabber.logger.log(Level.INFO, "Malformed data from client N{0}: saving data to '{1}'", new Object[]{client.getClientNumber() + "", filename});
+                            try {
+                                root.write(filename, MERGE.GEN_SUFF);
+                            } catch (Exception ex) {
+                                Grabber.logger.log(Level.SEVERE, "Can't save malformed data from client N{0} to file '{1}': {2}", new Object[]{client.getClientNumber() + "", filename, ex.getMessage()});
+                            }
+                        } else {
+                            Grabber.logger.log(Level.SEVERE, "Malformed data from client N{0}: not saving", client.getClientNumber() + "");
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (dataRoot != root && !merged) {
+                // destroy not needed if already merged - "root" was destroyed in DataRoot.merge
+                root.destroy();
+            }
+        }
+    }
+
+    /**
+     * Save data to file if it's needed (if dataSaved == false)
+     */
+    public synchronized void saveData() {
+        if (dataSaved == true) {
+            Grabber.logger.log(Level.FINE, "No new data received - nothing to save");
+            return; // nothing to do - received data is already saved
+        }
+
+        Grabber.logger.log(Level.INFO, "Server is saving cached data to {0}", fileName);
+        saveData(data);
+        Grabber.logger.log(Level.FINE, "Saving done");
+    }
+
+    /**
+     * Save data to file. In SaveAtExit mode file will always be rewritten. Sets
+     * dataSaved to true
+     *
+     * @param data data to save
+     */
+    private synchronized void saveData(long[] data) {
+        try {
+            if (!saveAtReceive) {
+                File file = new File(fileName);
+                if (file.exists()) {
+                    if (!file.delete()) {
+                        File nfile = getUnexistingFile(file);
+                        Grabber.logger.log(Level.SEVERE, "Error: given file '{0}' exists, cannot overwrite it. Data saved to '{1}'", new Object[]{file.getPath(), nfile.getPath()});
+                        file = nfile;
+                        fileName = nfile.getPath();
+                    }
+                }
+            }
+
+            if (dataRoot != null) {
+                if (templateName != null) {
+                    //do not need it at all
+                    dataRoot.update();
+                }
+                FileSaver fs = FileSaver.getFileSaver(dataRoot, fileName, templateName, MERGE.OVERWRITE, false, true);
+                fs.saveResults(fileName);
+            } else if (templateName != null) {
+                Utils.copyFile(new File(templateName), new File(fileName));
+            }
+
+            if (outTestList != null) {
+                Utils.writeLines(outTestList, tests.toArray(new String[tests.size()]));
+            }
+            dataSaved = true;
+        } catch (Exception ex) {
+            Grabber.logger.log(Level.SEVERE, "Error while saving data", ex);
+//            ex.printStackTrace();
+        }
+    }
+
+    void increaseReserved(long reserve) {
+        reservedMemory += reserve;
+    }
+
+    void decreaseReserved(long reserve) {
+        reservedMemory -= reserve;
+    }
+
+    synchronized boolean checkFreeMemory(long minMemory) throws Exception {
+        if (genscale) {
+            long mem = rt.freeMemory() + (rt.maxMemory() - rt.totalMemory()); // mem - current total free memory (free + non-allocated)
+            if (showMemoryChecks) {
+                Grabber.logger.log(Level.FINER, "Memory check routine. Total: {0}; max: {1}; free: {2}; max-total {3}", new Object[]{rt.totalMemory(), rt.maxMemory(), rt.freeMemory(), rt.maxMemory() - rt.totalMemory()});
+            }
+            if (((veryLowMemoryRun || lowMemoryRun) && mem < minMemory) || (normalMemoryRun && (rt.totalMemory() - rt.freeMemory()) > rt.maxMemory() / 3)) {
+                //        if (rt.totalMemory() > rt.maxMemory() / 2.1) {
+                Grabber.logger.log(Level.INFO, "Server is at low memory: trying to clean memory...");
+                rt.gc();
+                Thread.yield();
+                mem = rt.freeMemory() + (rt.maxMemory() - rt.totalMemory());
+                if ((lowMemoryRun && mem < minMemory && !dumping) || (normalMemoryRun && (rt.totalMemory() - rt.freeMemory()) > rt.maxMemory() * 0.45)) {
+//                if (rt.totalMemory() > rt.maxMemory() / 1.5 && totalConnections > 10 && !dumping) {
+                    Grabber.logger.log(Level.WARNING, "Server is at low memory: cleaning memory didn''t help - {0}mb free memory left. Dumping data to the file...", mem);
+                    dumpAndResetData();
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    boolean isDumping() {
+        synchronized (STOP_THE_WORLD_LOCK) {
+            return dumping;
+        }
+    }
+
+    private synchronized void dumpAndResetData() throws Exception {
+        dumping = true;
+        Thread t = new Thread() {
+            @Override
+            public void run() {
+                synchronized (STOP_THE_WORLD_LOCK) {
+                    try {
+                        dataRoot.update();
+                        FileSaver fs = FileSaver.getFileSaver(dataRoot, fileName + dumpCount, templateName, MERGE.OVERWRITE, false, true);
+                        fs.saveResults(fileName + dumpCount);
+                        if (outTestList != null) {
+                            Utils.writeLines(outTestList + dumpCount, tests.toArray(new String[tests.size()]));
+                            tests.clear();
+                        }
+                        dataRoot.cleanScales();
+                        for (int i = 0; i < data.length; ++i) {
+                            data[i] = 0;
+                        }
+                        dataRoot.update(); // needed?
+                    } catch (Exception ex) {
+                        throw new RuntimeException(ex);
+                    } finally {
+                        ++dumpCount;
+                        dumping = false;
+                        STOP_THE_WORLD_LOCK.notifyAll();
+                    }
+                }
+            }
+        };
+        t.setName("DumpingThread" + dumpCount);
+        t.setPriority(Thread.MAX_PRIORITY);
+        t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+            public void uncaughtException(Thread t, Throwable e) {
+            }
+        });
+        t.start();
+    }
+
+    private static File getUnexistingFile(File file) {
+        File temp = null;
+        do {
+            String suffix = RuntimeUtils.genSuffix();
+            temp = new File(file.getPath() + suffix);
+
+        } while (temp.exists());
+        return temp;
+    }
+
+    /**
+     * Wait until all alive clients will finish working
+     */
+    private void waitForAliveClients() {
+        int timeout = 0;
+        while (timeout < MAX_TIMEOUT && clientsAlive()) {
+            try {
+                Thread.sleep(100);
+                timeout += 100;
+            } catch (InterruptedException e) {
+                return;
+            }
+        }
+    }
+
+    /**
+     * Checks that all clients have finish working Removes dead Client objects
+     * from clients list
+     *
+     * @return false when all clients have finish working
+     */
+    private boolean clientsAlive() {
+        return aliveClients > 0;
+    }
+
+    /**
+     * Server is working
+     *
+     * @return server is working
+     */
+    public boolean isWorking() {
+        return working;
+    }
+
+    public boolean isStarted() {
+        return started;
+    }
+
+    /**
+     * Returns count of all connections occurred
+     *
+     * @return count of all connections occurred
+     */
+    public int getConnectionsCount() {
+        return totalConnections;
+    }
+
+    /**
+     * Returns count of connections that are alive at the moment The number can
+     * change fastly
+     *
+     * @return count of connections that are alive at the moment
+     */
+    public int getAliveConnectionCount() {
+        return aliveClients;
+    }
+
+    /**
+     * Checks that there is no unsaved data
+     *
+     * @return true when all data is saved to file (or no data comed)
+     */
+    public boolean isDataSaved() {
+        return dataSaved;
+    }
+
+    /**
+     * Get output file name
+     *
+     * @return output file name
+     */
+    public String getFileName() {
+        return fileName;
+    }
+
+    /**
+     * Get data template file name
+     *
+     * @return data template file name
+     */
+    public String getTemplateName() {
+        return templateName;
+    }
+
+    /**
+     * Get listening port
+     *
+     * @return listening port, assigned to ServerSocket. If server was created
+     * with port == 0, then real listening port will be returned. -1 is returned
+     * if server is not working anymore of port is not binded.
+     */
+    public int getPort() {
+        if (port == 0) {
+            if (ss != null) {
+                return ss.getLocalPort();
+            } else {
+                if (!working) {
+                    return -1;
+                }
+            }
+        }
+
+        return port;
+    }
+
+    /**
+     * Check whether server is saving data on receive
+     *
+     * @return true if server is saving data on receive
+     */
+    public boolean isSaveOnReceive() {
+        return saveAtReceive;
+    }
+
+    /**
+     * Get maximum connections count
+     *
+     * @return maximum connections count
+     */
+    public int getMaxCount() {
+        return saveCount;
+    }
+
+    /**
+     * Set the maximum connection count that this Server will collect
+     *
+     * @param saveCount
+     */
+    public void setMaxCount(int saveCount) {
+        this.saveCount = saveCount;
+    }
+
+    /**
+     * Set maximum time in milliseconds to wait for alive clients when closing
+     * Server
+     *
+     * @param MAX_TIMEOUT
+     */
+    public static void setMaxTimeout(int MAX_TIMEOUT) {
+        Server.MAX_TIMEOUT = MAX_TIMEOUT;
+    }
+
+    /**
+     * Set output testlist file
+     *
+     * @param outTestList
+     */
+    public void setOutTestList(String outTestList) {
+        this.outTestList = outTestList;
+    }
+
+    /**
+     * Set output file
+     *
+     * @param fileName
+     */
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public String getSaveBadData() {
+        return saveBadData;
+    }
+
+    public void setSaveBadData(String saveBadData) {
+        this.saveBadData = saveBadData;
+    }
+} // ############# Server
+
+/**
+ * Class for server command listener. Receives commands from commandPort and
+ * controls the Server
+ */
+class CommandListener extends Thread {
+
+    private final int commandPort;              // port to listen for commands
+    private final Server server;                // server to control
+    private String runCommand;                  // command the Server was started with
+    private ServerSocket serverSocket = null;   // socket listener
+    private String hostName;                    // name of the host running the Server
+    // runtime data
+    private boolean working = true;             // thread is working
+
+    /**
+     * Constructor for CommandListener class Sets "CommandListener" as the name
+     * of the thread
+     *
+     * @param commandPort port to listen for commands
+     * @param server Server to control
+     */
+    public CommandListener(int commandPort, Server server) throws BindException, IOException {
+        this(commandPort, server, "<unknown>", "<localhost>");
+    }
+
+    /**
+     * Constructor for CommandListener class Sets "CommandListener" as the name
+     * of the thread
+     *
+     * @param commandPort port to listen for commands
+     * @param server Server to control
+     * @param runCommand command this Server was runned with
+     */
+    public CommandListener(int commandPort, Server server, String runCommand, String hostName) throws BindException, IOException {
+        super();
+
+        setDaemon(false);
+        setName("CommandListener");
+        this.server = server;
+        this.runCommand = runCommand;
+
+        serverSocket = new ServerSocket(commandPort);
+        this.commandPort = serverSocket.getLocalPort();
+        this.hostName = hostName;
+    }
+
+    /**
+     * Main method. Use start() to launch in new thread. CommandListener listens
+     * commandPort with ServerSocket and receives commands from it. Every 5
+     * seconds ServerSocket is getting TimeoutException so ServerSocket.accept()
+     * releases the thread to check whether Server is still alive.
+     */
+    @Override
+    public void run() {
+        try {
+            Grabber.logger.log(Level.CONFIG, "Server is waiting for commands at port {0}", Integer.toString(commandPort));
+            outer:
+            while (working && server.isWorking() && serverSocket != null) {
+                Socket socket = null;
+                try {
+                    serverSocket.setSoTimeout(5000);
+                    socket = serverSocket.accept();
+                    InputStream in = socket.getInputStream();
+                    int command = in.read();
+                    Grabber.logger.log(Level.FINE, "Server received command {0} from {1}", new Object[]{Integer.toString(command), socket.getInetAddress().getHostAddress()});
+                    switch (command) {
+                        case MiscConstants.GRABBER_KILL_COMMAND:
+                            Grabber.logger.log(Level.INFO, "Server received kill command.");
+                            server.kill(false);
+//                            server.saveData();
+                            socket.getOutputStream().write(1);
+                            socket.getOutputStream().close();
+                            in.close();
+                            break outer;
+                        case MiscConstants.GRABBER_FORCE_KILL_COMMAND:
+                            socket.getOutputStream().write(1);
+                            socket.getOutputStream().close();
+                            in.close();
+                            Grabber.logger.log(Level.SEVERE, "Server received forced kill command. Exiting.");
+                            server.kill(true);
+                            break outer;
+                        case MiscConstants.GRABBER_SAVE_COMMAND:
+                            socket.getOutputStream().write(1);
+                            socket.getOutputStream().close();
+                            in.close();
+                            Grabber.logger.log(Level.FINE, "Server received save command.");
+                            server.saveData();
+                            break;
+                        case MiscConstants.GRABBER_STATUS_COMMAND:
+                            OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream(), Charset.forName("UTF-8"));
+                            String message = String.format("%b;%d;%d;%b;%s;%s;%s;%s", server.isWorking(), server.getConnectionsCount(), server.getAliveConnectionCount(),
+                                    server.isDataSaved(), runCommand, new File(".").getCanonicalPath(), server.getTemplateName(), server.getFileName());
+                            Grabber.logger.log(Level.FINEST, "Sending status '{0}'", message);
+                            out.write(message, 0, message.length());
+                            out.flush();
+                            in.close();
+                            out.close();
+                            break;
+                        case MiscConstants.GRABBER_WAIT_COMMAND:
+                            out = new OutputStreamWriter(socket.getOutputStream(), Charset.forName("UTF-8"));
+                            if (server.isStarted()) {
+                                message = String.format("%b;%s;%d;%s", true, hostName, server.getPort(), server.getTemplateName());
+                                Grabber.logger.log(Level.FINEST, "Sending started status: {0}", message);
+                            } else {
+                                message = String.format("%b;%s;%d;%s", false, hostName, server.getPort(), server.getTemplateName());
+                            }
+                            out.write(message, 0, message.length());
+                            out.flush();
+                            in.close();
+                            out.close();
+                            break;
+                        default:
+                            Grabber.logger.log(Level.WARNING, "Unknown message '{0}' came from {0}", new Object[]{Integer.toString(command), socket.getInetAddress().getHostAddress()});
+                            break;
+                    }
+                } catch (SocketTimeoutException ex) {
+                    if (!server.isWorking()) {
+                        break;
+                    }
+                } catch (IOException ex) {
+                    if (serverSocket != null && !serverSocket.isClosed()) { // means that kill() was called
+                        Grabber.logger.log(Level.SEVERE, "Exception occurred while processing command", ex);
+                    }
+                } finally {
+                    if (socket != null && !socket.isClosed()) {
+                        try {
+                            socket.close();
+                        } catch (IOException ex) {
+                        }
+                    }
+                }
+
+            }
+        } finally {
+            if (serverSocket != null && !serverSocket.isClosed()) {
+                try {
+                    serverSocket.close();
+                } catch (IOException ex) {
+                }
+            }
+            serverSocket = null;
+            working = false;
+        }
+    }
+
+    /**
+     * Stop listening port for commands IOException will be thrown in run()
+     * method from ServerSocket as it will be closed.
+     */
+    public void kill() {
+        working = false;
+        if (serverSocket != null) {
+            try {
+                serverSocket.close();
+            } catch (IOException ex) {
+            }
+        }
+        serverSocket = null;
+    }
+
+    /**
+     * Sets the command used for running the Grabber
+     *
+     * @param runCommand the command used for running the Grabber
+     */
+    public void setRunCommand(String runCommand) {
+        if (runCommand == null || runCommand.trim().equals("")) {
+            this.runCommand = "<empty>";
+        } else {
+            this.runCommand = runCommand;
+        }
+    }
+
+    /**
+     * Get command the server was runned with
+     *
+     * @return command the server was runned with
+     */
+    public String getRunCommand() {
+        return runCommand;
+    }
+
+    /**
+     * Get port Command Listener is listening
+     *
+     * @return port Command Listener is listening
+     */
+    public int getPort() {
+        if (commandPort == 0) {
+            if (serverSocket != null) {
+                return serverSocket.getLocalPort();
+            } else {
+                if (!working) {
+                    return -1;
+
+                }
+            }
+        }
+
+        return commandPort;
+    }
+} // ############# CommandListener
+
+//              ##############################################################################
+//              ##############################################################################
+//              ##############################################################################
+//              ################################# MAIN CLASS #################################
+//              ##############################################################################
+//              ##############################################################################
+//              ##############################################################################
+/**
+ * <p> Grabber is a class that collects coverage data in both static and dynamic
+ * formats from remote clients. RT clients send their data through
+ * JCovSocketSavers or Grabber connects to ServerSocketSavers set up by RT
+ * clients. </p> <p> Grabber class manages 2 classes: Server and
+ * CommandListener. First is listening for client connections and the second is
+ * listening for commands sent from GrabberManager tool. </p>
+ *
+ * @see GrabberManager
+ * @author andrey
+ */
+public class Grabber extends JCovCMDTool {
+
+    static final Logger logger;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(Grabber.class.getName());
+    }
+    public static final String COMMAND_PORT_PORPERTY = "jcov.grabber.commandPort";
+    public static final String MAX_COUNT_PROPERTY = "jcov.grabber.maxCount";
+    public static final String OUTPUTFILE_PROPERTY = "jcov.grabber.outputfile";
+    public static final String PORT_PROPERTY = "jcov.grabber.port";
+    public static final String RUN_LINE_PROPERTY = "jcov.grabber.runLine";
+    public static final String SAVE_ON_RECEIVE_PROPERTY = "jcov.grabber.saveOnReceive";
+    public static final String SAVE_IN_SHUTDOWN_HOOK_PROPERTY = "jcov.grabber.shutdownHookInstalled";
+    public static final String TEMPLATE_PROPERTY = "jcov.grabber.template";
+    public static final String SERVER_VERSION_PROPERTY = "jcov.grabber.version";
+    public static final String SERVER_LOCAL_HOSTNAME_PROPERTY = "jcov.grabber.localhostname";
+    private boolean saveInShutdownHook = false;     // true -> data would be automatically saved in shutdown hook
+    private String propfile;                        // propfile to write
+    private String hostName;                        // host running the Server
+    private int maxCount;                           // maximum connections that would be processed by Server
+    private Thread shutdownHook = null;             // shutdown hook which will save data if saveInShutdownHook is set to true
+    // CommandListener configuration
+    private boolean noCommand;                      // do not start CommandListener
+    private CommandListener commandListener = null; // commandListener instance
+    private int commandPort;                        // port to listen for commands from GrabberManager
+    private String runCommand;                      // command this Grabber was started with
+    // Server configuration
+    private int port;                               // port to launch Server and listen for clients connections
+    private Server server = null;                   // server instance
+    private boolean once;                           // "once" mode when Server connects to the client
+    private String template;                        // template to read (is passed to Server)
+    private String filename;                        // output filename
+    private boolean saveOnReceive;                  // save data on recieve (true) or on exit (false)
+    private String onceHostToConnect;               // host to connect in "once" mode
+    private String outTestList;                     // file to write testlist to
+    private boolean genscale;                       // allows to generate scales without outtestlist
+    private String baddata;                         // directory to write bad data to
+    private String messageFormat;
+    private boolean mergeByTestNames = false;       // generate scales based on test names (test name identifies test)
+
+    /**
+     * Get Properties object initialized with info about Server, Command
+     * Listener and shutdown hook Properties object is initialized with:
+     * jcov.grabber.port - Server's port jcov.grabber.commandPort - Command
+     * Listener's port if set (otherwise this property is not set)
+     * jcov.grabber.saveOnReceive - is the Server saving data on receive
+     * jcov.grabber.shutdownHookInstalled - is the shutdown hook installed
+     * jcov.grabber.maxCount - maximum Server connections allowed
+     * jcov.grabber.template - template file jcov.grabber.outputfile - output
+     * file jcov.grabber.runLine - line the Server is runned with (<unknown>
+     * when not set and <empty> when there are no arguments)
+     *
+     * @return initialized Properties object
+     */
+    public Properties getProperties() {
+        Properties ps = new Properties();
+
+        ps.setProperty(PORT_PROPERTY, Integer.toString(server.getPort()));
+        if (commandListener != null) {
+            ps.setProperty(COMMAND_PORT_PORPERTY, Integer.toString(commandListener.getPort()));
+            ps.setProperty(RUN_LINE_PROPERTY, commandListener.getRunCommand());
+        }
+        ps.setProperty(SAVE_ON_RECEIVE_PROPERTY, Boolean.toString(server.isSaveOnReceive()));
+        ps.setProperty(SAVE_IN_SHUTDOWN_HOOK_PROPERTY, Boolean.toString(saveInShutdownHook));
+        ps.setProperty(MAX_COUNT_PROPERTY, Integer.toString(server.getMaxCount()));
+        if (server != null) {
+            ps.setProperty(TEMPLATE_PROPERTY, server.getTemplateName());
+            ps.setProperty(OUTPUTFILE_PROPERTY, server.getFileName());
+        }
+        ps.setProperty(SERVER_VERSION_PROPERTY, JcovVersion.jcovVersion + "-" + JcovVersion.jcovBuildNumber);
+        try {
+            ps.setProperty(SERVER_LOCAL_HOSTNAME_PROPERTY, InetAddress.getLocalHost().toString());
+        } catch (UnknownHostException ex) {
+        }
+
+        return ps;
+    }
+
+    /**
+     * Write properties to a file. Properties.store() is used so
+     * Properties.load() can be used to retrieve data
+     *
+     * @param file file to write properites
+     */
+    public void writePropfile(String file) throws IOException {
+        Properties ps = getProperties();
+
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(file);
+            ps.store(out, null);
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    /**
+     * Wait untill Server and CommandListener will stop
+     *
+     * @throws InterruptedException
+     */
+    public void waitForStopping() throws InterruptedException {
+        if (server != null) {
+            server.join();
+        }
+        if (commandListener != null) {
+            commandListener.kill();
+            commandListener.join();
+        }
+    }
+
+    /**
+     *
+     * @throws BindException
+     * @throws IOException
+     */
+    public void start(boolean startCommandListener) throws BindException, IOException {
+        this.saveInShutdownHook = true;
+        createServer();
+        if (startCommandListener) {
+            startCommandListener(commandPort);
+        }
+
+        installShutdownHook();
+        startServer();
+    }
+
+    public void createServer() throws BindException, IOException {
+        server = new Server(port, once, template, filename, outTestList, hostName, maxCount, saveOnReceive, genscale, mergeByTestNames);
+        server.setSaveBadData(baddata);
+    }
+
+    /**
+     * Start Server object
+     */
+    public void startServer() {
+        server.start();
+    }
+
+    /**
+     * Start CommandListener object
+     *
+     * @param commandPort
+     */
+    public void startCommandListener(int commandPort) throws BindException, IOException {
+        if (server == null) {
+            throw new IllegalStateException("Server is not created");
+        }
+        if (commandListener != null && commandListener.isAlive()) {
+            return;
+        }
+
+        commandListener = new CommandListener(commandPort, server);
+        commandListener.start();
+    }
+
+    /**
+     * Start CommandListener object
+     *
+     * @param commandPort
+     * @param runCommand command this Serever was runned with (used to send
+     * 'info' command responce)
+     */
+    public void startCommandListener(int commandPort, String runCommand) throws BindException, IOException {
+        if (server == null) {
+            throw new IllegalStateException("Server is not created");
+        }
+        if (commandListener != null && commandListener.isAlive()) {
+            return;
+        }
+
+        commandListener = new CommandListener(commandPort, server, runCommand, hostName);
+        commandListener.start();
+    }
+
+    /**
+     * Install shutdown hook that will save data and stop CommandListener and
+     * Server
+     */
+    public void installShutdownHook() {
+        if (shutdownHook == null) { // do this only when hook is not installed
+            shutdownHook = new Thread() {
+                @Override
+                public void run() {
+                    logger.log(Level.CONFIG, "Shutdownhook fired");
+                    if (commandListener != null) {
+                        commandListener.kill();
+                    }
+                    if (server != null && server.isWorking()) { // in normal exit server should return false
+                        if (saveInShutdownHook) {
+                            server.saveData();
+                        }
+                        server.kill(false);
+                    }
+                    logger.log(Level.FINE, "Shutdownhook done");
+                }
+            };
+            Runtime.getRuntime().addShutdownHook(shutdownHook);
+            logger.log(Level.FINE, "Shutdownhook installed");
+        }
+    }
+
+    /**
+     * Stop the Server. CommandListener will also be stopped.
+     *
+     * @param force when false - server will wait till all alive clients will
+     * end transmitting data
+     */
+    public void stopServer(boolean force) {
+        if (server != null) {
+            server.kill(force);
+            server = null;
+        }
+        if (commandListener != null) {
+            stopCommandListener();
+        }
+    }
+
+    /**
+     * Stop the CommandListener
+     */
+    public void stopCommandListener() {
+        commandListener.kill();
+        commandListener = null;
+    }
+
+    /**
+     * Set run command to the Command Listener (wrapper method)
+     *
+     * @param command run command
+     * @return true if runcommand was set, false otherwise (when Command
+     * Listener is not created)
+     */
+    public boolean setRunCommand(String command) {
+        if (commandListener != null) {
+            commandListener.setRunCommand(command);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * main
+     */
+    public static void main(String args[]) {
+        Grabber tool = new Grabber();
+        try {
+            int res = tool.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+
+    public String usageString() {
+        return "java com.sun.tdk.jcov.Grabber [-option value]";
+    }
+
+    public String exampleString() {
+        return "java -cp jcov.jar com.sun.tdk.jcov.Grabber -output grabbed.xml -port 3000 -once -merge";
+    }
+
+    protected String getDescr() {
+        return "gathers information from JCov runtime via sockets";
+    }
+
+    public int getServerPort() {
+        if (server != null) {
+            return server.getPort();
+        }
+
+        return -1;
+    }
+
+    public int getCommandListenerPort() {
+        if (commandListener != null) {
+            return commandListener.getPort();
+        }
+
+        return -1;
+    }
+
+    @Override
+    public EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    DSC_OUTPUT,
+                    DSC_VERBOSE,
+                    DSC_VERBOSEST,
+                    DSC_SHOW_MEMORY_CHECKS,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TEMPLATE,
+                    DSC_HOSTNAME,
+                    DSC_ONCE,
+                    DSC_PORT,
+                    DSC_COUNT,
+                    DSC_SAVE_MODE,
+                    DSC_COMMAND_PORT,
+                    DSC_PROPFILE,
+                    Merger.DSC_OUTPUT_TEST_LIST,
+                    Merger.DSC_SCALE,
+                    DSC_BADDATA,
+                    DSC_MESSAGE_FORMAT,
+                    DSC_SCALE_BY_NAME
+                }, this);
+    }
+
+    @Override
+    public int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        template = opts.getValue(InstrumentationOptions.DSC_TEMPLATE);
+        try {
+            File t = Utils.checkFileNotNull(template, "template filename", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_CANREAD);
+
+            long recomendedMemory = Math.max(1000000000, t.length() * 16 + 150000000); // 12 = 4 (template in memory) * 4; 150000000 = 10mb (for each client) * 15 (number of concurrent/agent clients)
+            long minMemory = Math.max(468000000, t.length() * 6 + 50000000); // 6 = 4 * 1.5; 50000000 = 10mb * 5
+            if (Runtime.getRuntime().maxMemory() < minMemory) { // < 0.8gb
+                logger.log(Level.WARNING, "Grabber started with {0}M max memory. Minimal requirement for this template is {1}M, recomended {2}M. ", new Object[]{Runtime.getRuntime().maxMemory() / 1000000, minMemory / 1000000, recomendedMemory / 1000000});
+            } else if (Runtime.getRuntime().maxMemory() < recomendedMemory) {
+                logger.log(Level.WARNING, "Grabber started with {0}M max memory. It''s recomended to have at least {1}M max memory. ", new Object[]{Runtime.getRuntime().maxMemory() / 1000000, recomendedMemory / 1000000});
+            }
+        } catch (EnvHandlingException ex) {
+            if (opts.isSet(InstrumentationOptions.DSC_TEMPLATE)) {
+                throw ex;
+            }
+            logger.log(Level.WARNING, "Grabber started without template, accepting only dynamic data");
+            template = null;
+        }
+
+        try {
+            InetAddress adr = InetAddress.getLocalHost();
+            hostName = adr.getHostName();
+        } catch (Exception ee) {
+            logger.log(Level.WARNING, "Can't get real local host name, using '<localhost>'");
+            hostName = "<localhost>";
+        }
+
+        if (opts.isSet(DSC_VERBOSE)) {
+            Utils.setLoggingLevel(Level.INFO);
+        }
+        if (opts.isSet(DSC_VERBOSEST)) {
+            Utils.setLoggingLevel(Level.ALL);
+        }
+        if (opts.isSet(DSC_SHOW_MEMORY_CHECKS)) {
+            Server.showMemoryChecks = true;
+        }
+
+        once = opts.isSet(DSC_ONCE);
+
+        port = opts.isSet(DSC_PORT)
+                ? Utils.checkedToInt(opts.getValue(DSC_PORT), "port number", Utils.CheckOptions.INT_NONNEGATIVE)
+                : (once ? MiscConstants.JcovOncePortNumber : MiscConstants.JcovPortNumber);
+
+        maxCount = Utils.checkedToInt(opts.getValue(DSC_COUNT), "max connections count");
+
+        filename = opts.isSet(DSC_OUTPUT) ? opts.getValue(DSC_OUTPUT) : MiscConstants.JcovSaveFileNameXML;
+        Utils.checkFileNotNull(filename, "output filename", Utils.CheckOptions.FILE_PARENTEXISTS, Utils.CheckOptions.FILE_NOTISDIR);
+
+        saveOnReceive = "receive".equalsIgnoreCase(opts.getValue(DSC_SAVE_MODE));
+
+        if (once) {
+            onceHostToConnect = opts.getValue(DSC_HOSTNAME);
+            Utils.checkHostCanBeNull(onceHostToConnect, "testing host");
+        }
+
+        String commandPortStr = opts.getValue(DSC_COMMAND_PORT);
+        if (once) {
+            commandPort = 0;
+        } else if (commandPortStr == null) {
+            commandPort = MiscConstants.JcovGrabberCommandPort;
+        } else {
+            commandPort = Utils.checkedToInt(commandPortStr, "command port number");
+        }
+        noCommand = opts.isSet(DSC_NO_COMMAND);
+        propfile = opts.getValue(DSC_PROPFILE);
+        Utils.checkFileCanBeNull(propfile, "property filename", Utils.CheckOptions.FILE_NOTISDIR, Utils.CheckOptions.FILE_PARENTEXISTS);
+
+        runCommand = opts.unParse();
+
+        outTestList = opts.getValue(Merger.DSC_OUTPUT_TEST_LIST);
+        Utils.checkFileCanBeNull(outTestList, "output testlist filename", Utils.CheckOptions.FILE_NOTISDIR, Utils.CheckOptions.FILE_PARENTEXISTS);
+
+        genscale = opts.isSet(Merger.DSC_SCALE);
+
+        baddata = opts.getValue(DSC_BADDATA);
+        Utils.checkFileCanBeNull(baddata, "directory for bad datafiles", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_ISDIR);
+
+        messageFormat = opts.getCleanValue(DSC_MESSAGE_FORMAT);
+
+        mergeByTestNames = opts.isSet(DSC_SCALE_BY_NAME);
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    public int run() throws Exception {
+        if (!saveOnReceive) {
+            logger.log(Level.INFO, "Installing shutdown hook");
+        }
+        saveInShutdownHook = !saveOnReceive;
+
+        // Trying to create server. It can fail in case port is incorrect or is already binded
+        try {
+            createServer();
+            int returnValue = 0;
+
+            boolean shouldStartCL = !(once) || noCommand;
+            if (shouldStartCL) {
+                try { // Trying to create and start command listener. It can fail in case port is incorrect or is already binded
+                    startCommandListener(commandPort, runCommand);
+                } catch (BindException ex) {
+                    Grabber.logger.log(Level.SEVERE, "Cannot bind CommandListener to {0}: {1}", new Object[]{
+                                commandPort == 0 ? "any free port" : "port " + String.valueOf(commandPort),
+                                ex.getMessage()});
+                    returnValue = 6;
+                } catch (IOException ex) {
+                    Grabber.logger.log(Level.SEVERE, "Cannot create CommandListener", ex);
+                    returnValue = 6;
+                }
+            }
+
+            if (!shouldStartCL || commandListener != null) { // run Server only if CommandListener was started or should not to be started
+                installShutdownHook();
+                startServer();
+
+                char[] c = new char[]{'p', 'h', 'c', 't', 'C', 'o', 'O', 's', 'S'};
+                String[] s = new String[]{Integer.toString(server.getPort()), hostName, Integer.toString(commandListener.getPort()), template, Integer.toString(maxCount), filename, outTestList, Boolean.toString(genscale), saveOnReceive ? "receive" : "exit"};
+                System.out.println(PropertyFinder.processMacroString(messageFormat, c, s));
+
+                String file = propfile;
+                if (file != null) {
+                    try {
+                        writePropfile(file);
+                    } catch (IOException ex) {
+                        logger.log(Level.SEVERE, "Error while trying to store properties file: ", ex);
+                    }
+                }
+            } else {
+                server.kill(true); // CommandListener failed to start
+            }
+
+            logger.log(Level.INFO, "Waiting for closing all processes");
+            waitForStopping();
+            logger.log(Level.INFO, "Server is stopped");
+
+            return returnValue;
+        } catch (BindException ex) {
+            Grabber.logger.log(Level.SEVERE, "Cannot bind Server to {0}: {1}", new Object[]{
+                        port == 0 ? "any free port" : "port " + String.valueOf(commandPort),
+                        ex.getMessage()});
+            return 5;
+        } catch (IOException ex) {
+            Grabber.logger.log(Level.SEVERE, "Cannot create Server", ex);
+            return 5;
+        }
+    }
+
+    public int getCommandPort() {
+        return commandPort;
+    }
+
+    public void setCommandPort(int commandPort) {
+        this.commandPort = commandPort;
+    }
+
+    public String getOutputFilename() {
+        return filename;
+    }
+
+    public void setOutputFilename(String filename) {
+        this.filename = filename;
+    }
+
+    public boolean isGenscale() {
+        return genscale;
+    }
+
+    public void setGenscale(boolean genscale) {
+        this.genscale = genscale;
+    }
+
+    public String getHostName() {
+        return hostName;
+    }
+
+    public void setHostName(String hostName) {
+        this.hostName = hostName;
+    }
+
+    public int getMaxCount() {
+        return maxCount;
+    }
+
+    public void setMaxCount(int maxCount) {
+        this.maxCount = maxCount;
+    }
+
+    public boolean isNoCommand() {
+        return noCommand;
+    }
+
+    public void setNoCommand(boolean noCommand) {
+        this.noCommand = noCommand;
+    }
+
+    public boolean isOnce() {
+        return once;
+    }
+
+    public void setOnce(boolean once) {
+        this.once = once;
+    }
+
+    public String getHostToConnect() {
+        return onceHostToConnect;
+    }
+
+    public void setHostToConnect(String onceHostToConnect) {
+        this.onceHostToConnect = onceHostToConnect;
+    }
+
+    public String getOutTestList() {
+        return outTestList;
+    }
+
+    public void setOutTestList(String outTestList) {
+        this.outTestList = outTestList;
+    }
+
+    public void setMergeByTestNames(boolean mergeByTestNames) {
+        this.mergeByTestNames = mergeByTestNames;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public boolean isSaveInShutdownHook() {
+        return saveInShutdownHook;
+    }
+
+    public void setSaveInShutdownHook(boolean saveInShutdownHook) {
+        this.saveInShutdownHook = saveInShutdownHook;
+    }
+
+    public boolean isSaveOnReceive() {
+        return saveOnReceive;
+    }
+
+    public void setSaveOnReceive(boolean saveOnReceive) {
+        this.saveOnReceive = saveOnReceive;
+    }
+
+    public String getTemplate() {
+        return template;
+    }
+
+    public void setTemplate(String template) {
+        this.template = template;
+    }
+
+    public String getMessageString() {
+        return messageFormat;
+    }
+
+    public void setMessageString(String message) {
+        this.messageFormat = message;
+    }
+    public final static OptionDescr DSC_SAVE_MODE =
+            new OptionDescr("save", "", OptionDescr.VAL_SINGLE, new String[][]{
+                {"receive", "Save data to a file on receiving and then merge into it"},
+                {"exit", "Save data on exit merging it in memory (faster but more memory used)"}
+            }, "Specify when incoming data should be merged and saved.", "exit");
+    public final static OptionDescr DSC_OUTPUT =
+            new OptionDescr("grabber.output", new String[]{"output", "o"}, "Output parameters definition", OptionDescr.VAL_SINGLE,
+            "Specifies output file.", "result.xml for xml format or java.jcov for legacy");
+    public final static OptionDescr DSC_VERBOSE =
+            new OptionDescr("verbose", new String[]{"v"}, "Verbosity level", "Show more messages.");
+    public final static OptionDescr DSC_VERBOSEST =
+            new OptionDescr("verbosemore", new String[]{"vv"}, "", "Show all messages.");
+    public final static OptionDescr DSC_SHOW_MEMORY_CHECKS =
+            new OptionDescr("showmemory", "", "Show memory checks.");
+    public final static OptionDescr DSC_HOSTNAME =
+            new OptionDescr("hostname", "Connection parameters", OptionDescr.VAL_SINGLE,
+            "Specify host to connect when client mode is used.", "localhost");
+    public final static OptionDescr DSC_ONCE =
+            new OptionDescr("once", new String[]{"client"}, "",
+            "Specify client mode.");
+    //true - client, false - server
+    public final static OptionDescr DSC_PORT =
+            new OptionDescr("port", "", OptionDescr.VAL_SINGLE,
+            "Specify port. Use -port 0 to use any free port for server (only for server mode)",
+            MiscConstants.JcovPortNumber + " for server mode, or " + MiscConstants.JcovOncePortNumber + " for client mode.");
+    public final static OptionDescr DSC_COUNT =
+            new OptionDescr("count", "", OptionDescr.VAL_SINGLE,
+            "Specify maximum times of receiving data. 0 corresponds to unlimited.", "0");
+    public final static OptionDescr DSC_COMMAND_PORT =
+            new OptionDescr("command_port", new String[]{"command"}, "", OptionDescr.VAL_SINGLE,
+            "Set port to listen commands. Use -command 0 to use any free port for command listener.", Integer.toString(MiscConstants.JcovGrabberCommandPort));
+    public final static OptionDescr DSC_NO_COMMAND =
+            new OptionDescr("nocommand", new String[]{"nc"}, "", OptionDescr.VAL_NONE,
+            "Use to not run command listener");
+    public final static OptionDescr DSC_PROPFILE =
+            new OptionDescr("grabber.props", "Properties file", OptionDescr.VAL_SINGLE,
+            "Write properties to a file to use them in the manager.");
+    public final static OptionDescr DSC_BADDATA =
+            new OptionDescr("baddatato", "manage bad data", OptionDescr.VAL_SINGLE,
+            "Directory to write data that can't be merged with the template.");
+    public final static OptionDescr DSC_MESSAGE_FORMAT =
+            new OptionDescr("message", "welcome message format", OptionDescr.VAL_SINGLE,
+            "Specify format for output welcome message. %p% - port, %c% - command port, %h% - running host, %t% - used template, %C% - maximum connection count (0 == unlimited), %o% - output file, %O% - output testlist file, %s% - generate scales, %S% - save at receive or exit",
+            "Server started on %h%:%p%. Command listener at port %c%. Used template '%t%'.");
+    public final static OptionDescr DSC_SCALE_BY_NAME =
+            new OptionDescr("mergebyname", "process/generate test scales",
+            "test name identifies the test. tests with same name will be automatically merged");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/GrabberManager.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.constants.MiscConstants;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p> Tool to control Grabber through socket requests </p>
+ *
+ * @author Andrey Titov
+ */
+public class GrabberManager extends JCovCMDTool {
+
+    private LinkedList<ServerCommand> commands;
+    private int waittime = 5;
+
+    @Override
+    protected int run() throws Exception {
+        if (commands == null || commands.size() == 0) {
+            throw new Exception("No commands specified");
+        }
+        try {
+            Iterator<ServerCommand> comm = commands.iterator();
+            while (comm.hasNext()) {
+                ServerCommand command = comm.next();
+                if (command == COMM_WAIT) {
+                    String gotstatus = sendWaitCommand();
+                    if (gotstatus == null) {
+                        throw new Exception("Server didn't respond");
+                    }
+                    String[] split = gotstatus.split(";", -1);
+                    if (split.length != 4) {
+                        throw new Exception("Server sent malformed status: " + gotstatus);
+                    }
+                    String status = "Server started on " + split[1] + ":" + split[2] + ". Command listener at port " + port + ". Used template " + split[3] + ".";
+                    System.out.println(status);
+                } else if (command == COMM_STATUS) {
+                    String gotstatus = sendStatusCommand();
+                    String[] split = gotstatus.split(";", -1);
+                    if (split.length != 8) {
+                        throw new Exception("Got malformed status from the server: " + gotstatus);
+                    }
+                    String status = "Server " + (Boolean.parseBoolean(split[0]) ? "is working. Got "
+                            + Integer.parseInt(split[1]) + " connections, " + Integer.parseInt(split[2]) + " are alive. "
+                            + (Boolean.parseBoolean(split[3]) ? "No unsaved data. " : "Data is not saved. ") : "is not working. ")
+                            + "Server was started with options '" + split[4] + "' \n"
+                            + "        working directory: " + split[5] + "\n"
+                            + "        current template used: " + split[6] + "\n"
+                            + "        output file to be created on exit: " + split[7] + "\n";
+                    System.out.println("Status: " + status);
+                } else if (command == COMM_SAVE) {
+                    sendSaveCommand();
+                    System.out.println("Save: OK");
+                } else if (command == COMM_KILL_FORCE) {
+                    sendForceKillCommand();
+                    System.out.println("Forced kill: OK");
+                } else if (command == COMM_KILL) {
+                    sendKillCommand();
+                    System.out.println("Kill: OK");
+                }
+            }
+        } catch (UnknownHostException e) {
+            throw new Exception("Can't resolve hostname '" + host + "'");
+        } catch (IOException e) {
+            if ("Connection refused".equals(e.getMessage())) {
+                throw new Exception("Server not responding on command port " + port);
+            } else {
+                throw e;
+            }
+        }
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    DSC_HOSTNAME,
+                    DSC_PORT,
+                    DSC_FILE,
+                    DSC_WAITTIME,
+                    COMM_KILL,
+                    COMM_KILL_FORCE,
+                    COMM_SAVE,
+                    COMM_STATUS,
+                    COMM_WAIT
+                }, this);
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        String file = opts.getValue(DSC_FILE);
+        Utils.checkFileCanBeNull(file, "properties filename", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_CANREAD);
+        if (file != null) {
+            try {
+                initPortFromFile(file);
+            } catch (IOException ex) {
+                throw new EnvHandlingException("Error while reading properties file: ", ex);
+            }
+        } else {
+            setPort(Utils.checkedToInt(opts.getValue(DSC_PORT), "port number"));
+        }
+
+        host = opts.getValue(DSC_HOSTNAME);
+        Utils.checkHostCanBeNull(host, "grabber host");
+
+        commands = new LinkedList<ServerCommand>();
+        if (opts.isSet(COMM_WAIT)) {
+            commands.add(COMM_WAIT);
+        }
+        if (opts.isSet(COMM_STATUS)) {
+            commands.add(COMM_STATUS);
+        }
+        if (opts.isSet(COMM_SAVE)) {
+            commands.add(COMM_SAVE);
+        }
+        if (opts.isSet(COMM_KILL_FORCE)) {
+            commands.add(COMM_KILL_FORCE);
+        }
+        if (opts.isSet(COMM_KILL)) {
+            commands.add(COMM_KILL);
+        }
+        if (commands.size() == 0) {
+            throw new EnvHandlingException("Command was not specified");
+        }
+
+        if (opts.isSet(DSC_WAITTIME)) {
+            waittime = Utils.checkedToInt(opts.getValue(DSC_WAITTIME), "time to wait value");
+        }
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    static class ServerCommand extends OptionDescr {
+
+        private int commandCode;
+
+        ServerCommand(String name, String[] aliases, String titile, int values, String usage, int code) {
+            super(name, aliases, titile, values, usage);
+            this.commandCode = code;
+        }
+
+        public int getCommandCode() {
+            return commandCode;
+        }
+    }
+    final static OptionDescr DSC_HOSTNAME =
+            new OptionDescr("host", new String[]{"hostname"}, "Connection parameters", OptionDescr.VAL_SINGLE,
+            "Specify servers host to connect.", "localhost");
+    final static OptionDescr DSC_PORT =
+            new OptionDescr("command_port", new String[]{"port"}, "", OptionDescr.VAL_SINGLE, "Specify servers command port.",
+            Integer.toString(MiscConstants.JcovGrabberCommandPort));
+    final static OptionDescr DSC_FILE =
+            new OptionDescr("grabber.props", "", OptionDescr.VAL_SINGLE, "Read server properties from a file. Host should be specified explicitly.");
+    final static OptionDescr DSC_WAITTIME =
+            new OptionDescr("waittime", new String[]{"time", "t"}, "",
+            OptionDescr.VAL_SINGLE, "Max time in seconds to wait for Grabber startup. Note that Manager will do 4 attempts to connect the Grabber");
+    final static ServerCommand COMM_KILL =
+            new ServerCommand("kill", new String[]{"stop"}, "Manage running server",
+            OptionDescr.VAL_NONE, "Stop running server saving data and waining for all connections close.", MiscConstants.GRABBER_KILL_COMMAND);
+    final static ServerCommand COMM_KILL_FORCE =
+            new ServerCommand("fkill", new String[]{"fstop"}, "", OptionDescr.VAL_NONE,
+            "Stop running server not saving data and not waining for all connections close.", MiscConstants.GRABBER_FORCE_KILL_COMMAND);
+    final static ServerCommand COMM_SAVE =
+            new ServerCommand("save", new String[]{"flush"}, "", OptionDescr.VAL_NONE, "Save data to file.", MiscConstants.GRABBER_SAVE_COMMAND);
+    final static ServerCommand COMM_STATUS =
+            new ServerCommand("status", null, "", OptionDescr.VAL_NONE, "Print server status.", MiscConstants.GRABBER_STATUS_COMMAND);
+    final static ServerCommand COMM_WAIT =
+            new ServerCommand("wait", null, "", OptionDescr.VAL_NONE, "Wait server for starting.", MiscConstants.GRABBER_WAIT_COMMAND);
+    static final Logger logger;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(GrabberManager.class.getName());
+    }
+
+    public static void main(String args[]) {
+        GrabberManager tool = new GrabberManager();
+        try {
+            int res = tool.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+    private int port;
+    private String host;
+
+    public GrabberManager() {
+        this(MiscConstants.JcovGrabberCommandPort, "localhost");
+    }
+
+    public GrabberManager(int port, String host) {
+        this.port = port;
+        this.host = host;
+    }
+
+    private void sendCode(int code) throws IOException {
+        Socket socket = null;
+        try {
+            socket = new Socket(host, port);
+            OutputStream out = socket.getOutputStream();
+            out.write(code);
+            socket.getInputStream().read();
+            socket.getInputStream().close();
+            out.close();
+        } finally {
+            if (socket != null) {
+                socket.close();
+            }
+        }
+    }
+
+    private String recieveCode(int code) throws IOException {
+        String data = null;
+        Socket socket = null;
+        try {
+            socket = new Socket(host, port);
+            OutputStream out = socket.getOutputStream();
+            out.write(code);
+
+            InputStream in = socket.getInputStream();
+            BufferedReader inReader = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()));
+            data = inReader.readLine();
+
+            out.close();
+            in.close();
+        } finally {
+            if (socket != null) {
+                socket.close();
+            }
+        }
+        return data;
+    }
+
+    /**
+     * Send KILL command. Port and Host should be both set.
+     *
+     * @throws IOException
+     */
+    public void sendKillCommand() throws IOException {
+        sendCode(COMM_KILL.getCommandCode());
+    }
+
+    /**
+     * Send forced KILL command. Port and Host should be both set.
+     *
+     * @throws IOException
+     */
+    public void sendForceKillCommand() throws IOException {
+        sendCode(COMM_KILL_FORCE.getCommandCode());
+    }
+
+    /**
+     * Send SAVE command. Port and Host should be both set.
+     *
+     * @throws IOException
+     */
+    public void sendSaveCommand() throws IOException {
+        sendCode(COMM_SAVE.getCommandCode());
+    }
+
+    /**
+     * Send STATUS command and recieve responce. Port and Host should be both
+     * set.
+     *
+     * @throws IOException
+     */
+    public String sendStatusCommand() throws IOException {
+        return recieveCode(COMM_STATUS.getCommandCode());
+    }
+
+    /**
+     * Send WAIT command and recieve responce. Port and Host should be both set.
+     *
+     * @throws IOException
+     */
+    public String sendWaitCommand() throws IOException {
+        String ret = null;
+        for (int i = 0; i < 4; ++i) {
+            try {
+                ret = recieveCode(COMM_WAIT.getCommandCode());
+                String[] split = ret.split(";");
+                if (Boolean.parseBoolean(split[0])) {
+                    break;
+                }
+            } catch (IOException e) {
+                try {
+                    Thread.sleep(waittime * 1000);
+                } catch (InterruptedException ex) {
+                }
+            }
+        }
+        return ret;
+    }
+
+    @Override
+    protected String getDescr() {
+        return "control commands to the Grabber server";
+    }
+
+    @Override
+    protected String usageString() {
+        return "java -jar jcov.jar GrabberManager [-option value]";
+    }
+
+    @Override
+    protected String exampleString() {
+        return "java -jar jcov.jar GrabberManager -port 3336 -status";
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public int initPortFromFile(String file) throws IOException {
+        Properties ps = new Properties();
+        InputStream in = null;
+        in = new FileInputStream(file);
+        ps.load(in);
+        in.close();
+
+        String portStr = ps.getProperty(Grabber.COMMAND_PORT_PORPERTY);
+        if (portStr == null) {
+            logger.log(Level.SEVERE, "Command Listeners port is not set in properties file '{0}'. Cannot work.", file);
+            return 1;
+        }
+
+        port = 0;
+        try {
+            port = Integer.parseInt(portStr);
+        } catch (NumberFormatException ex) {
+            logger.log(Level.SEVERE, "Malformed port number '{0}' in properties file '{1}'. Cannot work.", new Object[]{portStr, file});
+            return 1;
+        }
+
+        if (port == 0) {
+            String runLine = ps.getProperty(Grabber.RUN_LINE_PROPERTY);
+            if (runLine != null) {
+                logger.log(Level.SEVERE, "Command listener is not running on server (port = 0). Server was run with arguments '{0}'.", runLine);
+            } else {
+                logger.log(Level.SEVERE, "Command listener is not running on server (port = 0). Servers run line is unknown (not set in properties file).");
+            }
+            return 1;
+        }
+
+        checkVersion(ps.getProperty(Grabber.SERVER_VERSION_PROPERTY));
+        return 0;
+    }
+
+    private void checkVersion(String version) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/Helper.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.JCovTool;
+import com.sun.tdk.jcov.tools.JcovVersion;
+
+/**
+ * @author Dmitry Fazunenko
+ * @author Alexey Fedorchenko
+ */
+public class Helper {
+
+    public static void main(String[] args) {
+        if (args.length > 0) {
+            String arg = args[0];
+            if ("-version".equals(arg)) {
+                System.out.println(String.format("JCov %s-%s", JcovVersion.jcovVersion, JcovVersion.jcovBuildNumber, JcovVersion.jcovMilestone) + ("beta".equals(JcovVersion.jcovMilestone) ? " beta" : ""));
+                System.exit(0);
+            } else {
+                for (int i = 0; i < JCovTool.allToolsList.size(); i++) {
+                    // 17 = "com.sun.tdk.jcov."
+                    if (JCovTool.allToolsList.get(i).toLowerCase().substring(17).equals(arg.toLowerCase())) {
+                        try {
+                            Class c = Class.forName(JCovTool.allToolsList.get(i));
+                            if (JCovCMDTool.class.isAssignableFrom(c)) {
+                                JCovCMDTool tool = (JCovCMDTool) c.newInstance();
+//                                String[] newArgs = Arrays.copyOfRange(args, 1, args.length);
+                                String[] newArgs = new String[args.length - 1];
+                                System.arraycopy(args, 1, newArgs, 0, args.length - 1); // jdk1.5 support
+                                System.exit(tool.run(newArgs));
+                            } else if (JCovTool.class.isAssignableFrom(c)) {
+                                JCovTool.printHelp((JCovTool) c.newInstance(), args);
+                                System.exit(1);
+                            } else {
+                                System.out.println("INTERNAL ERROR! Specified tool ('" + arg + "') is not a jcovtool. ");
+                                System.exit(1);
+                            }
+                        } catch (ClassNotFoundException e) {
+                            System.out.println("INTERNAL ERROR! Specified tool was not found in classpath. ");
+                            System.exit(1);
+                        } catch (Exception e) {
+                            System.out.println("INTERNAL ERROR! " + e.getMessage());
+                            e.printStackTrace();
+                            System.exit(1);
+                        }
+                    }
+                }
+            }
+        }
+
+        JCovTool.printHelp();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/Instr.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.insert.AbstractUniversalInstrumenter;
+import com.sun.tdk.jcov.instrument.ClassMorph;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions.InstrumentationMode;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions.MERGE;
+import com.sun.tdk.jcov.instrument.InstrumentationParams;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.LoggingFormatter;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p> A tool to statically instrument classfiles to collect coverage. </p> <p>
+ * There are 2 coverage collection modes: static and dynamic. In static mode
+ * JCov reads and modifies classes bytecode inserting there some instructions
+ * which will use JCov RT libraries. In dynamic mode (aka Agent mode) a VM agent
+ * is used ("java -javaagent") that instruments bytecode just at loadtime. </p>
+ *
+ * @author Andrey Titov
+ * @author Dmitry Fazunenko
+ * @author Alexey Fedorchenko
+ */
+public class Instr extends JCovCMDTool {
+
+    private String[] include = new String[]{".*"};
+    private String[] exclude = new String[]{""};
+    private String[] callerInclude;
+    private String[] callerExclude;
+    private String[] save_beg = null;
+    private String[] save_end = null;
+    private boolean genabstract = false;
+    private boolean gennative = false;
+    private boolean genfield = false;
+    private boolean gensynthetic = true;
+    private boolean genanonymous = true;
+    private boolean innerinvocations = true;
+    private String[] srcs;
+    private File outDir;
+    private String include_rt;
+    private String template;
+    private String flushPath;
+    private boolean subsequentInstr = false;
+    private boolean recurse;
+    private InstrumentationMode mode = InstrumentationMode.BRANCH;
+    private AbstractUniversalInstrumenter instrumenter;
+    private ClassMorph morph;
+    private ClassLoader cl = null;
+    private static final Logger logger;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(Instr.class.getName());
+    }
+
+    /**
+     * <p> Instrument specified files (directories with classfiles and jars) and
+     * write template. Output template will be merged with input template (if
+     * any) or will be written to specified path. Instrumented file will be
+     * written to
+     * <code>outDir</code>. </p>
+     *
+     * @param files Files to be instrumented. Can contain both directoried with
+     * classfiles and jars
+     * @param outDir Directory to write instrumented data to. Be careful - all
+     * instrumented data will be written to the root of outDir. When null -
+     * instrumented data will owerwrite source binaries
+     * @param includeRTJar Runtime jar path to be implanted into instrumented
+     * data. For jar files - rt will be integrated into result jar. For
+     * directories - rt will be unpacked into outDir. Null if nothing should be
+     * implanted
+     * @throws IOException
+     * @see #setTemplate(java.lang.String)
+     */
+    public void instrumentAll(File[] files, File outDir, String includeRTJar) throws IOException {
+        instrumentFiles(files, outDir, includeRTJar);
+        instrumenter.finishWork();
+    }
+
+    /**
+     * <p> Instrument several files (classfiles or jars) to some directory.
+     * Instrumenter should be set. Instrumented file will be written to
+     * <code>outDir</code>. </p>
+     *
+     * @param files files to instrument
+     * @param outDir can be null. Initial file will be overwritten in such case.
+     * @throws IOException
+     * @see
+     * #setInstrumenter(com.sun.tdk.jcov.insert.AbstractUniversalInstrumenter)
+     * @see #setDefaultInstrumenter(java.io.File)
+     */
+    public void instrumentAll(File[] files, File outDir) throws IOException {
+        instrumentAll(files, outDir, null);
+    }
+
+    /**
+     * <p> Instrument one file using default instrumenter. This method doesn't
+     * write output template.xml - use
+     * <code>finishWork()</code> method. Instrumented file will be written to
+     * <code>outDir</code>. </p>
+     *
+     * @param file
+     * @param outDir can be null. Initial file will be overwritten in such case.
+     * @param includeRTJar
+     * @throws IOException
+     * @see
+     * #setInstrumenter(com.sun.tdk.jcov.insert.AbstractUniversalInstrumenter)
+     * @see #setDefaultInstrumenter(java.io.File)
+     * @see #finishWork()
+     */
+    public void instrumentFile(File file, File outDir, String includeRTJar) throws IOException {
+        setDefaultInstrumenter();
+        instrumenter.instrument(file, outDir, includeRTJar, recurse);
+    }
+
+    /**
+     * <p> Instrument one file using default instrumenter. This method doesn't
+     * write output template.xml - use
+     * <code>finishWork()</code> method. Instrumented file will be written to
+     * <code>outDir</code>. </p>
+     *
+     * @param file
+     * @param outDir can be null. Initial file will be overwritten in such case.
+     * @param includeRTJar
+     * @throws IOException
+     * @see
+     * #setInstrumenter(com.sun.tdk.jcov.insert.AbstractUniversalInstrumenter)
+     * @see #setDefaultInstrumenter(java.io.File)
+     * @see #finishWork()
+     */
+    public void instrumentFile(String file, File outDir, String includeRTJar) throws IOException {
+        instrumentFile(new File(file), outDir, includeRTJar);
+    }
+
+    /**
+     * <p> This method instruments a bunch of files using default instrumenter.
+     * This method doesn't write output template.xml - use
+     * <code>finishWork()</code> method. Instrumented file will be written to
+     * <code>outDir</code>. </p>
+     *
+     * @param files
+     * @param outDir can be null. Initial file will be overwritten in such case.
+     * @param implantRT
+     */
+    public void instrumentFiles(File[] files, File outDir, String implantRT) throws IOException {
+        setDefaultInstrumenter();
+        for (File file : files) {
+            instrumenter.instrument(file, outDir, implantRT, recurse);
+        }
+    }
+
+    /**
+     * <p> This method instruments a bunch of files using default instrumenter.
+     * This method doesn't write output template.xml - use
+     * <code>finishWork()</code> method. Instrumented file will be written to
+     * <code>outDir</code>. </p>
+     *
+     * @param files
+     * @param outDir can be null. Initial file will be overwritten in such case.
+     * @param implantRT
+     */
+    public void instrumentFiles(String[] files, File outDir, String implantRT) throws IOException {
+        setDefaultInstrumenter();
+        for (String file : files) {
+            instrumenter.instrument(new File(file), outDir, implantRT, recurse);
+        }
+    }
+
+    /**
+     * Begin instrumentation in semi-automatic mode
+     *
+     * @see Instr#finishWork()
+     */
+    public void startWorking() {
+        setDefaultInstrumenter();
+    }
+
+    /**
+     * Set default instrumenter
+     */
+    private void setDefaultInstrumenter() {
+
+        if (morph == null) {
+            InstrumentationParams params = new InstrumentationParams(gennative, genfield, genabstract, include, exclude, callerInclude, callerExclude, mode, save_beg, save_end)
+                    .setInstrumentSynthetic(gensynthetic)
+                    .setInstrumentAnonymous(genanonymous)
+                    .setInnerInvocations(innerinvocations);
+            if (subsequentInstr) {
+                morph = new ClassMorph(params, template);
+            } else {
+                morph = new ClassMorph(params, null);
+            }
+        }
+        if (instrumenter == null) {
+            instrumenter = new AbstractUniversalInstrumenter(true) {
+                protected byte[] instrument(byte[] classData, int classLen) throws IOException {
+                    return morph.morph(classData, cl, flushPath);
+                }
+
+                public void finishWork() {
+                    if (subsequentInstr) {
+                        morph.saveData(MERGE.MERGE); // template should be initialized
+                    } else {
+                        morph.saveData(template, null, MERGE.OVERWRITE); // template should be initialized
+                    }
+                }
+            };
+        }
+    }
+
+    /**
+     * Set instrumenter
+     *
+     * @param instrumenter instrumenter used to instrument data
+     */
+    public void setInstrumenter(AbstractUniversalInstrumenter instrumenter) {
+        this.instrumenter = instrumenter;
+    }
+
+    /**
+     * Finish instrumentation and write template. If template already exists -
+     * it will be merged. <p> Template is written to the place Instrumenter was
+     * created with
+     */
+    public void finishWork() {
+        if (instrumenter != null) {
+            instrumenter.finishWork();
+            // destroy instrumenter & morph?
+        }
+    }
+
+    /**
+     * Finish instrumentation and write template. If template already exists -
+     * it will be merged.
+     *
+     * @param outTemplate template path
+     */
+    public void finishWork(String outTemplate) {
+        if (instrumenter != null) {
+            if (subsequentInstr) {
+                morph.saveData(outTemplate, MERGE.MERGE); // template should be initialized
+            } else {
+                morph.saveData(outTemplate, null, MERGE.OVERWRITE); // template should be initialized
+            }
+        }
+    }
+
+    /**
+     * Legacy CLI entry point.
+     */
+    public static void main(String[] args) {
+        Instr tool = new Instr();
+        try {
+            int res = tool.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+
+    protected String usageString() {
+        return "java com.sun.tdk.jcov.Instr [-option value] target";
+    }
+
+    protected String exampleString() {
+        return "java -cp jcov.jar com.sun.tdk.jcov.Instr -include java.lang.* -type block -output classes_instrumented classes";
+    }
+
+    protected String getDescr() {
+        return "instruments classfiles and creates template.xml";
+    }
+
+    /**
+     * public configuration interface
+     *
+     * @param b true when logger should be verbose
+     */
+    public void setVerbose(boolean b) {
+        if (b) {
+            logger.setLevel(Level.INFO);
+        } else {
+            logger.setLevel(Level.WARNING);
+        }
+    }
+
+    public void resetDefaults() {
+        try {
+            handleEnv_(defineHandler());
+        } catch (EnvHandlingException ex) {
+            // should not happen
+        }
+    }
+
+    public boolean isGenAbstract() {
+        return genabstract;
+    }
+
+    public void setGenAbstract(boolean abstact) {
+        this.genabstract = abstact;
+    }
+
+    public String[] getExclude() {
+        return exclude;
+    }
+
+    public void setExclude(String[] exclude) {
+        this.exclude = exclude;
+    }
+
+    public boolean isGenField() {
+        return genfield;
+    }
+
+    public void setGenField(boolean field) {
+        this.genfield = field;
+    }
+
+    public boolean isGenNative() {
+        return gennative;
+    }
+
+    public void setGenNative(boolean gennative) {
+        this.gennative = gennative;
+    }
+
+    public String[] getInclude() {
+        return include;
+    }
+
+    public void setInclude(String[] include) {
+        this.include = include;
+    }
+
+    public void setCallerInclude(String[] callerInclude) {
+        this.callerInclude = callerInclude;
+    }
+
+    public void setCallerExclude(String[] callerExclude) {
+        this.callerExclude = callerExclude;
+    }
+
+    public void setFilter(String[] include, String exclude[]) {
+        this.include = include;
+        this.exclude = exclude;
+    }
+
+    public String[] getSave_beg() {
+        return save_beg;
+    }
+
+    public void setSave_beg(String[] save_beg) {
+        this.save_beg = save_beg;
+    }
+
+    public String[] getSave_end() {
+        return save_end;
+    }
+
+    public void setSave_end(String[] save_end) {
+        this.save_end = save_end;
+    }
+
+    public void setMode(InstrumentationMode mode) {
+        this.mode = mode;
+    }
+
+    public InstrumentationMode getMode() {
+        return mode;
+    }
+
+    public void config(boolean genabstract, boolean genfield, boolean gennative, String[] saveBegin, String saveEnd[]) {
+        setGenNative(gennative);
+        setGenAbstract(genabstract);
+        setGenField(genfield);
+        setSave_beg(save_beg);
+        setSave_end(save_end);
+    }
+
+    public void setTemplate(String template) {
+        this.template = template;
+    }
+
+    public String getTemplate() {
+        return template;
+    }
+
+    public boolean isSubsequentInstr() {
+        return subsequentInstr;
+    }
+
+    public void setSubsequentInstr(boolean subsequentInstr) {
+        this.subsequentInstr = subsequentInstr;
+    }
+
+    public void setFlushPath(String flushPath) {
+        this.flushPath = flushPath;
+    }
+
+    public String getFlushPath() {
+        return flushPath;
+    }
+
+    public void setClassLoader(ClassLoader cl) {
+        this.cl = cl;
+    }
+
+    @Override
+    protected int run() throws Exception {
+        Utils.addToClasspath(srcs);
+        try {
+            instrumentFiles(srcs, outDir, include_rt);
+            finishWork(template);
+        } catch (IOException ex) {
+            LoggingFormatter.printStackTrace = true;
+            throw new Exception("Critical exception: ", ex);
+        }
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    DSC_OUTPUT,
+                    DSC_VERBOSE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TYPE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CALLER_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CALLER_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SAVE_BEGIN,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SAVE_AT_END,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TEMPLATE,
+                    DSC_SUBSEQUENT,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ABSTRACT,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_NATIVE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FIELD,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SYNTHETIC,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ANONYM,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INNERINVOCATION,
+                    ClassMorph.DSC_FLUSH_CLASSES,
+                    DSC_INCLUDE_RT,
+                    DSC_RECURSE,}, this);
+    }
+
+    private int handleEnv_(EnvHandler opts) throws EnvHandlingException {
+        if (opts.isSet(DSC_VERBOSE)) {
+            setVerbose(true);
+        }
+
+        outDir = null;
+        if (opts.isSet(DSC_OUTPUT)) { // compatibility
+            outDir = new File(opts.getValue(DSC_OUTPUT));
+            Utils.checkFile(outDir, "output directory", Utils.CheckOptions.FILE_NOTISFILE);
+            if (!outDir.exists()) {
+                outDir.mkdirs();
+                logger.log(Level.INFO, "The directory {0} was created.", outDir.getAbsolutePath());
+            }
+        }
+
+        save_beg = opts.getValues(InstrumentationOptions.DSC_SAVE_BEGIN);
+        save_end = opts.getValues(InstrumentationOptions.DSC_SAVE_AT_END);
+
+        String abstractValue = opts.getValue(InstrumentationOptions.DSC_ABSTRACT);
+        if (abstractValue.equals("off")) {
+            genabstract = false;
+        } else if (abstractValue.equals("on")) {
+            genabstract = true;
+        } else {
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_ABSTRACT.name + "' parameter value error: expected 'on' or 'off'; found: '" + abstractValue + "'");
+        }
+
+        String nativeValue = opts.getValue(InstrumentationOptions.DSC_NATIVE);
+        if (nativeValue.equals("on")) {
+            gennative = true;
+        } else if (nativeValue.equals("off")) {
+            gennative = false;
+        } else {
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_NATIVE.name + "' parameter value error: expected 'on' or 'off'; found: '" + nativeValue + "'");
+        }
+
+        String fieldValue = opts.getValue(InstrumentationOptions.DSC_FIELD);
+        if (fieldValue.equals("on")) {
+            genfield = true;
+        } else if (fieldValue.equals("off")) {
+            genfield = false;
+        } else {
+            // can't happen - check is in EnvHandler
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_FIELD.name + "' parameter value error: expected 'on' or 'off'; found: '" + fieldValue + "'");
+        }
+
+        String anonym = opts.getValue(InstrumentationOptions.DSC_ANONYM);
+        if (anonym.equals("on")) {
+            genanonymous = true;
+        } else { // off
+            genanonymous = false;
+        }
+
+        String synthetic = opts.getValue(InstrumentationOptions.DSC_SYNTHETIC);
+        if (synthetic.equals("on")) {
+            gensynthetic = true;
+        } else { // if (fieldValue.equals("off"))
+            gensynthetic = false;
+        }
+
+        String innerInvocation = opts.getValue(InstrumentationOptions.DSC_INNERINVOCATION);
+        if ("off".equals(innerInvocation)) {
+            innerinvocations = false;
+        } else {
+            innerinvocations = true;
+        }
+
+        callerInclude = opts.getValues(InstrumentationOptions.DSC_CALLER_INCLUDE);
+        callerExclude = opts.getValues(InstrumentationOptions.DSC_CALLER_EXCLUDE);
+
+        recurse = opts.isSet(DSC_RECURSE);
+
+        mode = InstrumentationOptions.InstrumentationMode.fromString(opts.getValue(InstrumentationOptions.DSC_TYPE));
+        template = opts.getValue(InstrumentationOptions.DSC_TEMPLATE);
+        Utils.checkFileNotNull(template, "template filename", Utils.CheckOptions.FILE_NOTISDIR, Utils.CheckOptions.FILE_PARENTEXISTS);
+
+        subsequentInstr = opts.isSet(DSC_SUBSEQUENT);
+
+        include = InstrumentationOptions.handleInclude(opts);
+        exclude = InstrumentationOptions.handleExclude(opts);
+
+        flushPath = opts.getValue(ClassMorph.DSC_FLUSH_CLASSES);
+        if ("none".equals(flushPath)) {
+            flushPath = null;
+        }
+        include_rt = opts.getValue(DSC_INCLUDE_RT);
+        Utils.checkFileCanBeNull(include_rt, "JCovRT library jarfile", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_CANREAD);
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        srcs = opts.getTail();
+        if (srcs == null || srcs.length == 0) {
+            throw new EnvHandlingException("No sources specified");
+        }
+
+        return handleEnv_(opts);
+    }
+    final static OptionDescr DSC_OUTPUT =
+            new OptionDescr("instr.output", new String[]{"output", "o"}, "Output directory for instrumented classes",
+            OptionDescr.VAL_SINGLE,
+            "Specifies output directory, default directory is current. Instr command could process different dirs and different jars: \n "
+            + "all classes from input dirs and all jars will be placed in output directory.");
+    final static OptionDescr DSC_VERBOSE =
+            new OptionDescr("verbose", "Verbose mode", "Enable verbose mode.");
+    final static OptionDescr DSC_INCLUDE_RT =
+            new OptionDescr("implantrt", new String[]{"rt"}, "Runtime management", OptionDescr.VAL_SINGLE, "Allows to implant needed for runtime files into instrumented data: -includert jcov_rt.jar");
+    final static OptionDescr DSC_SUBSEQUENT =
+            new OptionDescr("subsequent", "", OptionDescr.VAL_NONE, "Template would be used to decide what should not be instrumented - all existing in template would be treated as already instrumented");
+    final static OptionDescr DSC_RECURSE =
+            new OptionDescr("recursive", "", OptionDescr.VAL_NONE, "Recurse through specified directories instrumenting everything inside. With -flush option it will be able to instrument duplicate classes. ");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/Instr2.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.insert.AbstractUniversalInstrumenter;
+import com.sun.tdk.jcov.instrument.ClassMorph;
+import com.sun.tdk.jcov.instrument.ClassMorph2;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.instrument.InstrumentationParams;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p> A tool to statically instrument classfiles to collect coverage. </p> <p>
+ * There are 2 coverage collection modes: static and dynamic. In static mode
+ * JCov reads and modifies classes bytecode inserting there some instructions
+ * which will use JCov RT libraries. In dynamic mode (aka Agent mode) a VM agent
+ * is used ("java -javaagent") that instruments bytecode just at loadtime. </p>
+ *
+ * @author Andrey Titov
+ */
+public class Instr2 extends JCovCMDTool {
+
+    private boolean genabstract;
+    private boolean genfield;
+    private boolean gennative;
+    private boolean gensynthetic;
+    private boolean genanonymous;
+    private String template;
+    private String flushPath;
+    private String[] include;
+    private String[] exclude;
+    private static final Logger logger;
+    private String[] srcs;
+    private File outDir;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(Instr2.class.getName());
+    }
+
+    /**
+     * entry point. Parses options, constructs appropriate context, class name
+     * filter, then invoces UniversalInstrumenter to do the job
+     */
+    public static void main(String[] args) {
+        Instr2 tool = new Instr2();
+        try {
+            int res = tool.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+
+    protected String usageString() {
+        return "java com.sun.tdk.jcov.Instr2 [-option [value]]";
+    }
+
+    protected String exampleString() {
+        return "java -cp jcov.jar com.sun.tdk.jcov.Instr2 -include java.lang.* -abstract on -native on -field on -template mytemplate.xml instrumented_classes";
+    }
+
+    protected String getDescr() {
+        return "instrumenter designed for abstract, native methods and fields";
+    }
+
+    @Override
+    protected int run() throws Exception {
+        Utils.addToClasspath(srcs);
+
+        AbstractUniversalInstrumenter instrumenter =
+                new AbstractUniversalInstrumenter(true) {
+                    ClassMorph2 morph = new ClassMorph2(
+                            new InstrumentationParams(gennative, genfield, genabstract, include, exclude, InstrumentationOptions.InstrumentationMode.BLOCK)
+                            .setInstrumentAnonymous(genanonymous)
+                            .setInstrumentSynthetic(gensynthetic), template);
+
+                    protected byte[] instrument(byte[] classData, int classLen) throws IOException {
+                        return morph.morph(classData, flushPath);
+                    }
+
+                    public void finishWork() {
+                    }
+                };
+
+        //instrumenter.setPrintStats(opts.isSet(DSC_STATS));
+//        com.sun.tdk.jcov.instrument.Options.instrumentAbstract = com.sun.tdk.jcov.instrument.Options.instrumentAbstract.NONE;
+
+        for (String root : srcs) {
+            instrumenter.instrument(new File(root), outDir);
+        }
+        /*
+         if ((opts.isSet(JcovInstrContext.OPT_SAVE_BEFORE) ||
+         opts.isSet(JcovInstrContext.OPT_SAVE_AFTER)  ||
+         opts.isSet(JcovInstrContext.OPT_SAVE_BEGIN)  ||
+         opts.isSet(JcovInstrContext.OPT_SAVE_AT_END)) &&
+         instrumenter.getSavePointCount() < 1) {
+
+         log.warning("no coverage data savepoints have been inserted");
+         }
+         */
+        instrumenter.finishWork();
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    DSC_OUTPUT,
+                    DSC_VERBOSE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TEMPLATE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ABSTRACT,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_NATIVE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FIELD,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SYNTHETIC,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ANONYM,
+                    ClassMorph.DSC_FLUSH_CLASSES
+                }, this);
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler envHandler) throws EnvHandlingException {
+        srcs = envHandler.getTail();
+        if (srcs == null) {
+            throw new EnvHandlingException("No input files specified");
+        }
+        if (envHandler.isSet(DSC_VERBOSE)) {
+            logger.setLevel(Level.INFO);
+        }
+
+        outDir = null;
+        if (envHandler.isSet(DSC_OUTPUT)) {
+            outDir = new File(envHandler.getValue(DSC_OUTPUT));
+            if (!outDir.exists()) {
+                outDir.mkdirs();
+                logger.log(Level.INFO, "The directory {0} was created.", outDir.getAbsolutePath());
+            }
+        }
+
+        String abstractValue = envHandler.getValue(InstrumentationOptions.DSC_ABSTRACT);
+        if (abstractValue.equals("off")) {
+            genabstract = false;
+        } else if (abstractValue.equals("on")) {
+            genabstract = true;
+        } else {
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_ABSTRACT.name + "' parameter value error: expected 'on' or 'off'; found: '" + abstractValue + "'");
+        }
+
+        String nativeValue = envHandler.getValue(InstrumentationOptions.DSC_NATIVE);
+        if (nativeValue.equals("on")) {
+            gennative = true;
+        } else if (nativeValue.equals("off")) {
+            gennative = false;
+        } else {
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_NATIVE.name + "' parameter value error: expected 'on' or 'off'; found: '" + nativeValue + "'");
+        }
+
+        String fieldValue = envHandler.getValue(InstrumentationOptions.DSC_FIELD);
+        if (fieldValue.equals("on")) {
+            genfield = true;
+        } else if (fieldValue.equals("off")) {
+            genfield = false;
+        } else {
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_FIELD.name + "' parameter value error: expected 'on' or 'off'; found: '" + fieldValue + "'");
+        }
+
+        String anonym = envHandler.getValue(InstrumentationOptions.DSC_ANONYM);
+        if (anonym.equals("on")) {
+            genanonymous = true;
+        } else { // off
+            genanonymous = false;
+        }
+
+        String syntheticField = envHandler.getValue(InstrumentationOptions.DSC_SYNTHETIC);
+        if (syntheticField.equals("on")) {
+            gensynthetic = true;
+        } else { // if (fieldValue.equals("off"))
+            gensynthetic = false;
+        }
+
+        template = envHandler.getValue(InstrumentationOptions.DSC_TEMPLATE);
+        Utils.checkFileNotNull(template, "template filename", Utils.CheckOptions.FILE_NOTISDIR, Utils.CheckOptions.FILE_PARENTEXISTS);
+
+        include = InstrumentationOptions.handleInclude(envHandler);
+        exclude = InstrumentationOptions.handleExclude(envHandler);
+
+        flushPath = envHandler.getValue(ClassMorph.DSC_FLUSH_CLASSES);
+        if ("none".equals(flushPath)) {
+            flushPath = null;
+        }
+
+        return SUCCESS_EXIT_CODE;
+    }
+    final static OptionDescr DSC_OUTPUT =
+            new OptionDescr("instr2.output", new String[]{"output", "o"}, "Output directory", OptionDescr.VAL_SINGLE,
+            "Specifies output file or directory, default directory is current.");
+    final static OptionDescr DSC_VERBOSE =
+            new OptionDescr("verbose", "Verbose mode", "Enable verbose mode.");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/JCov.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.constants.MiscConstants;
+import com.sun.tdk.jcov.data.Result;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.File;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * One command to get coverage on product by running user command.
+ *
+ * @author Alexey Fedorchenko
+ */
+public class JCov extends JCovCMDTool {
+
+    private String userOutput;
+    private String srcRootPath;
+    private String command;
+    private String template = MiscConstants.JcovTemplateFileNameXML;
+    private String result = "result.xml";
+    private String reportDirName = "report";
+    private int commandsPort = MiscConstants.JcovGrabberCommandPort;
+    private int testsPort = MiscConstants.JcovPortNumber;
+    private final String JCOV_NETWORK_JAR_NAME = "jcov_network_saver.jar";
+
+    // logger initialization
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(JCov.class.getName());
+    }
+    private final static Logger logger;
+    private final Object lock = new Object();
+
+    @Override
+    protected int run() throws Exception {
+
+        //instr
+        ProductInstr instr = new ProductInstr();
+        File productDir = new File(srcRootPath);
+
+        if (!productDir.exists()) {
+            logger.log(Level.SEVERE, "No product to get coverage");
+            return 1;
+        }
+
+        if (productDir.isFile()) {
+            logger.log(Level.SEVERE, "Set product directory, not file");
+            return 1;
+        }
+
+        File jcovJar = new File(JCov.class.getProtectionDomain().getCodeSource().getLocation().getPath());
+        File networkJar = new File(jcovJar.getParent() + File.separator + JCOV_NETWORK_JAR_NAME);
+        if (!networkJar.exists()) {
+            logger.log(Level.SEVERE, "Can not find " + JCOV_NETWORK_JAR_NAME + " in the jcov.jar location");
+            logger.log(Level.SEVERE, networkJar.getAbsolutePath());
+            return 1;
+        }
+
+        File parentProductDir = productDir.getParentFile();
+
+        //zip product
+        try {
+            Utils.zipFolder(productDir.getAbsolutePath(), parentProductDir.getAbsolutePath() + File.separator + productDir.getName() + ".zip");
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "Can not zip product", e);
+            return 1;
+        }
+
+        Utils.addToClasspath(new String[]{srcRootPath});
+        try {
+            instr.run(new String[]{"-product", srcRootPath, "-productOutput", parentProductDir.getAbsolutePath() + File.separator + "instr",
+                        "-rt", networkJar.getAbsolutePath()});
+        } catch (Exception ex) {
+            logger.log(Level.SEVERE, "Error while instrument product", ex);
+            return 1;
+        }
+
+        //reeplace product by instrumented one
+        Utils.deleteDirectory(productDir);
+        productDir = new File(productDir.getAbsolutePath());
+        productDir.mkdir();
+
+
+        File instrFiles = new File(parentProductDir.getAbsolutePath() + File.separator + "instr");
+        for (File file : instrFiles.listFiles()) {
+            if (file.isDirectory()) {
+                Utils.copyDirectory(file, new File(productDir, file.getName()));
+            } else {
+                Utils.copyFile(file, new File(productDir, file.getName()));
+            }
+
+        }
+
+        Utils.deleteDirectory(new File(parentProductDir.getAbsolutePath() + File.separator + "instr"));
+
+        GrabberThread grabberThread = new GrabberThread();
+        grabberThread.start();
+
+        synchronized (lock) {
+            while (!grabberThread.isStarted()) {
+                lock.wait();
+            }
+        }
+
+        //runcommand
+        try {
+            Process process = Runtime.getRuntime().exec(command);
+            process.waitFor();
+            if (process.exitValue() != 0) {
+                logger.log(Level.SEVERE, "wrong command for running tests.");
+            }
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "exception in process", e);
+        }
+
+        //stop grabber
+        GrabberManager grabberManager = new GrabberManager();
+        grabberManager.setPort(commandsPort);
+        grabberManager.sendKillCommand();
+
+        //repgen
+        RepGen rg = new RepGen();
+
+        File outputFile = networkJar.getParentFile();
+        if (userOutput != null && !userOutput.isEmpty()) {
+            outputFile = new File(userOutput);
+            if (!outputFile.exists()) {
+                outputFile.mkdirs();
+            }
+        }
+
+        try {
+            Result res = new Result(result);
+            rg.generateReport(rg.getDefaultReportGenerator(), outputFile.getAbsolutePath() + File.separator + reportDirName, res, srcRootPath);
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "error in report generation", e);
+        }
+
+        System.out.println("coverage report for product: " + outputFile.getAbsolutePath() + File.separator + reportDirName);
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    public static void main(String args[]) {
+        JCov tool = new JCov();
+        try {
+            int res = tool.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        EnvHandler envHandler = new EnvHandler(new OptionDescr[]{
+                    DSC_PRODUCT, DSC_RUN_COMMAND, DSC_OUTPUT,}, this);
+
+        return envHandler;
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
+
+        if (opts.isSet(DSC_PRODUCT)) {
+            srcRootPath = opts.getValue(DSC_PRODUCT);
+        }
+
+        if (opts.isSet(DSC_RUN_COMMAND)) {
+            command = opts.getValue(DSC_RUN_COMMAND);
+        } else {
+            throw new EnvHandlingException("command to run tests is not specified");
+        }
+
+        if (opts.isSet(DSC_OUTPUT)) {
+            userOutput = opts.getValue(DSC_OUTPUT);
+        }
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected String getDescr() {
+        return "gets product coverage with one command";
+    }
+
+    @Override
+    protected String usageString() {
+        return "java -jar jcov.jar JCov -pro productDirPath -command \"java -jar tests.jar\"";
+    }
+
+    @Override
+    protected String exampleString() {
+        return "java -jar jcov.jar JCov -pro productDirPath -command \"java -jar tests.jar\"";
+    }
+    public final static OptionDescr DSC_PRODUCT =
+            new OptionDescr("product", new String[]{"product", "pro"}, "Product files.", OptionDescr.VAL_SINGLE, "");
+    public final static OptionDescr DSC_RUN_COMMAND =
+            new OptionDescr("command", new String[]{"command", "cmd"}, "Command to run on product and get coverage", OptionDescr.VAL_SINGLE, "");
+    public final static OptionDescr DSC_OUTPUT =
+            new OptionDescr("output", new String[]{"output", "out", "o"}, "Output dir to create the result report directory", OptionDescr.VAL_SINGLE, "");
+
+    private class GrabberThread extends Thread {
+
+        private boolean started = false;
+
+        public GrabberThread() {
+            super();
+        }
+
+        public boolean isStarted() {
+            return started;
+        }
+
+        @Override
+        public void run() {
+            try {
+                //grabber
+                Grabber grabber = new Grabber();
+                grabber.setCommandPort(commandsPort);
+                grabber.setPort(testsPort);
+                grabber.setSaveOnReceive(false);
+                grabber.setTemplate(template);
+                grabber.setOutputFilename(result);
+                grabber.start(true);
+
+                synchronized (lock) {
+                    started = true;
+                    lock.notifyAll();
+                }
+
+                grabber.waitForStopping();
+
+            } catch (Exception e) {
+                logger.log(Level.SEVERE, "grabber exception", e);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/JREInstr.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.runtime.JCovSESocketSaver;
+import com.sun.tdk.jcov.runtime.JCovSocketSaver;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p> A tool to statically instrument JRE. </p> <p> There are 2 coverage
+ * collection modes: static and dynamic. In static mode JCov reads and modifies
+ * classes bytecode inserting there some instructions which will use JCov RT
+ * libraries. In dynamic mode (aka Agent mode) a VM agent is used ("java
+ * -javaagent") that instruments bytecode just at loadtime. </p>
+ *
+ * @author Andrey Titov
+ * @author Dmitry Fazunenko
+ * @author Alexey Fedorchenko
+ */
+public class JREInstr extends JCovCMDTool {
+
+    private Instr instr;
+    private File toInstrument;
+    private File[] addJars;
+    private File[] addTests;
+    private File implant;
+    private File javac;
+    private String[] callerInclude;
+    private String[] callerExclude;
+    private static final Logger logger;
+    private String host = null;
+    private Integer port = null;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(Instr.class.getName());
+    }
+
+    /**
+     * tries to find class in the specified jars
+     */
+    public class StaticJREInstrClassLoader extends URLClassLoader {
+
+        StaticJREInstrClassLoader(URL[] urls) {
+            super(urls);
+        }
+
+        @Override
+        public InputStream getResourceAsStream(String s) {
+            InputStream in = null;
+            try {
+                in = findResource(s).openStream();
+            } catch (IOException ex) {
+                //nothing to do
+            }
+            if (in != null) {
+                return in;
+            }
+            return super.getResourceAsStream(s);
+        }
+    }
+
+    @Override
+    protected int run() throws Exception {
+        final String[] toInstr = new String[]{toInstrument.getAbsolutePath()};
+        Utils.addToClasspath(toInstr);
+        instr.startWorking();
+
+        StaticJREInstrClassLoader cl = new StaticJREInstrClassLoader(new URL[]{toInstrument.toURI().toURL()});
+        instr.setClassLoader(cl);
+
+        instr.instrumentFile(toInstrument.getAbsolutePath(), null, implant.getAbsolutePath());
+
+        ArrayList<String> srcs = null;
+        if (addJars != null) {
+            srcs = new ArrayList<String>();
+            for (int i = 0; i < addJars.length; ++i) {
+                srcs.add(addJars[i].getAbsolutePath());
+            }
+        }
+
+        if (addTests != null) {
+            if (srcs == null) {
+                srcs = new ArrayList<String>();
+            }
+            for (int i = 0; i < addTests.length; ++i) {
+                srcs.add(addTests[i].getAbsolutePath());
+            }
+        }
+
+        if (srcs != null) {
+            Utils.addToClasspath(srcs.toArray(new String[0]));
+            instr.instrumentFiles(srcs.toArray(new String[0]), null, null);
+        }
+
+        instr.finishWork();
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        Instr.DSC_INCLUDE_RT.usage = "To run instrumented JRE you should implant JCov runtime library both into rt.jar and into 'lib/endorsed' directory.\nWhen instrumenting whole JRE dir with jreinstr tool - these 2 actions will be done automatically.";
+        return new EnvHandler(new OptionDescr[]{
+                    Instr.DSC_INCLUDE_RT,
+                    Instr.DSC_VERBOSE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TYPE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CALLER_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CALLER_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TEMPLATE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ABSTRACT,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_NATIVE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FIELD,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SYNTHETIC,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ANONYM,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INNERINVOCATION,
+                    Instr.DSC_SUBSEQUENT,
+                    DSC_JAVAC_HACK,
+                    DCS_ADD_JAR,
+                    DCS_ADD_TESTS,
+                    DSC_HOST,
+                    DSC_PORT
+                }, this);
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler envHandler) throws EnvHandlingException {
+        instr = new Instr();
+
+        String[] tail = envHandler.getTail();
+        if (tail == null || tail.length == 0) {
+            throw new EnvHandlingException("JRE dir is not specified");
+        }
+        if (tail.length > 1) {
+            logger.log(Level.WARNING, "Only first argument ({0}) will be used", tail[0]);
+        }
+
+        if (!envHandler.isSet(Instr.DSC_INCLUDE_RT)) {
+            throw new EnvHandlingException("Runtime should be always implanted when instrumenting rt.jar (e.g. '-rt jcov_j2se_rt.jar')");
+        }
+
+        implant = new File(envHandler.getValue(Instr.DSC_INCLUDE_RT));
+        Utils.checkFile(implant, "JCovRT library jarfile", Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_CANREAD);
+
+        if (envHandler.isSet(DCS_ADD_JAR)) {
+            String[] jars = envHandler.getValues(DCS_ADD_JAR);
+            addJars = new File[jars.length];
+            for (int i = 0; i < addJars.length; ++i) {
+                addJars[i] = new File(jars[i]);
+                if (!addJars[i].exists()) {
+                    throw new EnvHandlingException("Additional jar " + jars[i] + " doesn't exist");
+                }
+                if (!addJars[i].canRead()) {
+                    throw new EnvHandlingException("Can't read additional jar " + jars[i]);
+                }
+            }
+        }
+
+        if (envHandler.isSet(DCS_ADD_TESTS)) {
+            String[] files = envHandler.getValues(DCS_ADD_TESTS);
+            addTests = new File[files.length];
+            for (int i = 0; i < addTests.length; ++i) {
+                addTests[i] = new File(files[i]);
+                if (!addTests[i].exists()) {
+                    throw new EnvHandlingException("Test file " + files[i] + " doesn't exist");
+                }
+                if (!addTests[i].canRead()) {
+                    throw new EnvHandlingException("Can't read test file " + files[i]);
+                }
+            }
+        }
+
+        File f = new File(tail[0]);
+        Utils.checkFile(f, "JRE directory", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_ISDIR, Utils.CheckOptions.FILE_CANREAD);
+
+        if (envHandler.isSet(DSC_HOST)) {
+            host = envHandler.getValue(DSC_HOST);
+        }
+
+        if (envHandler.isSet(DSC_PORT)) {
+            try {
+                port = Integer.valueOf(envHandler.getValue(DSC_PORT));
+            } catch (NumberFormatException nfe) {
+                throw new EnvHandlingException("Specify correct port number");
+            }
+        }
+
+        if (envHandler.isSet(DSC_JAVAC_HACK)) {
+            String javacPath = envHandler.getValue(DSC_JAVAC_HACK);
+            javac = new File(javacPath);
+
+            File newJavac = null;
+
+            boolean isWin = false;
+            if (javacPath.endsWith(".exe") && System.getProperty("os.name").toLowerCase().indexOf("win") >= 0) {
+                newJavac = new File(javac.getParent() + File.separator + "javac_real.exe");
+                isWin = true;
+            } else {
+                newJavac = new File(javac.getParent() + File.separator + "javac_real");
+            }
+
+
+            if (newJavac.exists()) {
+                if (javac.exists()) {
+                    logger.log(Level.INFO, "javac seems to be already hacked: {0} exists", newJavac.getPath());
+                } else {
+                    try {
+                        if (!isWin) {
+                            File newFile = new File(javacPath);
+                            OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(newFile), Charset.forName("UTF-8"));
+                            out.write(newJavac.getAbsolutePath() + " -J-Xms30m \"$@\"");
+                            out.flush();
+                            out.close();
+                            try {
+                                newFile.setExecutable(true);
+                            } catch (Exception e) {
+                                logger.log(Level.WARNING, "Can't make new hacked javac file executable: {0}", e.getMessage());
+                            }
+                        } else {
+                            File newFile = new File(javacPath.replaceAll(".exe", ".bat"));
+                            OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(newFile), Charset.forName("UTF-8"));
+                            out.write(newJavac.getAbsolutePath() + " -J-Xms30m %*");
+                            out.flush();
+                            out.close();
+                        }
+                    } catch (IOException ex) {
+                        Logger.getLogger(JREInstr.class.getName()).log(Level.SEVERE, null, ex);
+                    }
+                }
+            } else {
+                if (!javac.exists()) {
+                    throw new EnvHandlingException("Specified javac doesn't exist (" + javacPath + ")");
+                }
+
+                if (!javac.isFile()) {
+                    throw new EnvHandlingException("Specified javac is not a file (" + javacPath + ")");
+                }
+
+                if (!javac.canWrite()) {
+                    throw new EnvHandlingException("Can't modify specified javac (" + javacPath + ")");
+                }
+
+                if (!javac.renameTo(newJavac)) {
+                    throw new EnvHandlingException("Can't move specified javac to new location (" + javacPath + " to " + newJavac.getPath() + ")");
+                }
+                try {
+                    if (!isWin) {
+                        File newFile = new File(javacPath);
+                        OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(newFile), Charset.forName("UTF-8"));
+                        out.write(newJavac.getAbsolutePath() + " $@ -J-Xms30m");
+                        out.flush();
+                        out.close();
+                        try {
+                            newFile.setExecutable(true);
+                        } catch (Exception e) {
+                            logger.log(Level.WARNING, "Can't make new hacked javac file executable: {0}", e.getMessage());
+                        }
+                    } else {
+                        File newFile = new File(javacPath.replaceAll(".exe", ".bat"));
+                        OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(newFile), Charset.forName("UTF-8"));
+                        out.write(newJavac.getAbsolutePath() + " -J-Xms30m %*");
+                        out.flush();
+                        out.close();
+                    }
+                } catch (IOException ex) {
+                    Logger.getLogger(JREInstr.class.getName()).log(Level.SEVERE, null, ex);
+                }
+            }
+        }
+
+        if (!f.isDirectory()) {
+            throw new EnvHandlingException("Specified JRE is not a directory: " + f.getPath());
+        } else {
+            // instrumenting JRE dir - check ${JRE}/lib/rt.jar, move ${JRE}/lib/rt.jar to ${JRE}/lib/rt.jar.bak (if not exist), add copy ${JRE}/lib/endorsed/${implant.jar}
+            File lib = new File(f, "lib");
+            if (!lib.exists()) {
+                throw new EnvHandlingException("lib directory was not found in JRE directory");
+            }
+            toInstrument = new File(lib, "rt.jar");
+            if (!toInstrument.exists()) {
+                throw new EnvHandlingException("rt.jar directory was not found in lib directory");
+            }
+            if (!toInstrument.isFile() || !toInstrument.canRead() || !toInstrument.canWrite()) {
+                throw new EnvHandlingException("Can't read/write rt.jar (or not a file)");
+            }
+            File bak = new File(lib, "rt.jar.bak");
+            if (!bak.exists()) {
+                try {
+                    Utils.copyFile(toInstrument, bak);
+                } catch (FileNotFoundException ex) {
+                    throw new EnvHandlingException("Error while backuping rt.jar: file not found", ex);
+                } catch (IOException ex) {
+                    throw new EnvHandlingException("Error while backuping rt.jar", ex);
+                }
+            } else {
+                if (!envHandler.isSet(Instr.DSC_SUBSEQUENT)) {
+                    throw new EnvHandlingException("Backup rt.jar.bak file exisit. It can mean that JRE is already instrumented - nothing to do. Restore initial rt.jar or delete bak file.");
+                }
+            }
+
+            File endorsed = new File(lib, "endorsed");
+            if (!endorsed.exists()) {
+                endorsed.mkdir();
+            } else {
+                if (!endorsed.isDirectory()) {
+                    throw new EnvHandlingException("JRE/lib/endorsed is not a directory");
+                }
+            }
+            File implantcopy = new File(endorsed, implant.getName());
+            try {
+                // copy rt to endorsed dir
+                Utils.copyFile(implant, implantcopy);
+
+                if (host != null || port != null) {
+                    Properties prop = new Properties();
+                    try {
+                        if (host != null) {
+                            prop.setProperty(JCovSESocketSaver.HOST_PROPERTIES_NAME, host);
+                        }
+                        if (port != null) {
+                            prop.setProperty(JCovSESocketSaver.PORT_PROPERTIES_NAME, Integer.toString(port));
+                        }
+                        prop.store(new FileOutputStream(endorsed.getAbsolutePath() + File.separator + JCovSESocketSaver.NETWORK_DEF_PROPERTIES_FILENAME), null);
+
+                    } catch (IOException ex) {
+                        logger.log(Level.WARNING, "Cannot create property file to save host and port: {0}", ex);
+                    }
+                }
+
+
+            } catch (FileNotFoundException ex) {
+                throw new EnvHandlingException("Error while copying implant file to endorsed dir: file not found", ex);
+            } catch (IOException ex) {
+                throw new EnvHandlingException("Error while copying implant file to endorsed dir", ex);
+            }
+        }
+
+        // check that java/lang/Shutdown is not excluded
+        String[] excludes = com.sun.tdk.jcov.instrument.InstrumentationOptions.handleExclude(envHandler);
+        String[] includes = com.sun.tdk.jcov.instrument.InstrumentationOptions.handleInclude(envHandler);
+        Utils.Pattern pats[] = Utils.concatFilters(includes, excludes);
+
+        callerInclude = envHandler.getValues(com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CALLER_INCLUDE);
+        callerExclude = envHandler.getValues(com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_CALLER_EXCLUDE);
+
+        if (!Utils.accept(pats, null, "/java/lang/Shutdown", null)) {
+            // Shutdown was excluded with some filtering mechanism. No need to remove it from excludes as inclusion has more priority
+            logger.log(Level.WARNING, "java.lang.Shutdown automatically included to instrumentation (it can't be excluded in jreinstr)");
+            if (includes.length > 0) { // if something else is already included - just including Shutdown
+                includes = Utils.copyOf(includes, includes.length + 1);
+                includes[includes.length - 1] = "/java/lang/Shutdown";
+            } else {
+                includes = new String[]{"/java/lang/Shutdown", "/*"};
+            }
+        }
+
+        int ret = instr.handleEnv(envHandler);
+        instr.setSave_end(new String[]{"java/lang/Shutdown.runHooks"});
+        instr.setInclude(includes);
+        instr.setExclude(excludes);
+
+        instr.setCallerInclude(callerInclude);
+        instr.setCallerExclude(callerExclude);
+
+        return ret;
+    }
+
+    @Override
+    protected String getDescr() {
+        return "instrumenter designed for instumenting rt.jar";
+    }
+
+    @Override
+    protected String usageString() {
+        return "java -jar jcov.jar jreinstr -implantrt <runtime_to_implant> <jre_dir>";
+    }
+
+    @Override
+    protected String exampleString() {
+        return "To instrument JRE: \"java -jar jcov.jar jreinstr -implantrt jcov_j2se_rt.jar JDK1.7.0/jre\"";
+    }
+    public static final OptionDescr DSC_JAVAC_HACK = new OptionDescr("javac", "hack javac", OptionDescr.VAL_SINGLE, "Hack javac to increase minimum VM memory used on initialization. Should be used on Solaris platform or should be done manually. ");
+    public static final OptionDescr DCS_ADD_JAR = new OptionDescr("addjar", new String[]{"add"}, "instrument additional jars", OptionDescr.VAL_MULTI, "Instrument additional jars within JRE or JDK. Only jar files are allowed.");
+    public static final OptionDescr DCS_ADD_TESTS = new OptionDescr("addtests", new String[]{"tests"}, "instrument tests", OptionDescr.VAL_MULTI, "Instrument tests files (classes and jars).");
+    final static OptionDescr DSC_HOST =
+            new OptionDescr("host", new String[]{"host"}, "sets default host", OptionDescr.VAL_SINGLE, "set the default host for sending jcov data. needed only in network mode");
+    final static OptionDescr DSC_PORT =
+            new OptionDescr("port", new String[]{"port"}, "sets default port", OptionDescr.VAL_SINGLE, "set the default port for sending jcov data. needed only in network mode");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/Merger.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,1096 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.data.FileFormatException;
+import com.sun.tdk.jcov.data.Result;
+import com.sun.tdk.jcov.data.ScaleOptions;
+import com.sun.tdk.jcov.instrument.DataRoot;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions.MERGE;
+import com.sun.tdk.jcov.io.ClassSignatureFilter;
+import com.sun.tdk.jcov.io.Reader;
+import com.sun.tdk.jcov.runtime.FileSaver;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.tools.ScaleCompressor;
+import com.sun.tdk.jcov.tools.SimpleScaleCompressor;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p> This tool allows to create one merged XML file from many ones </p> <p>
+ * Merger can create "scaled" coverage. </p>
+ *
+ * @author Andrey Titov
+ */
+public class Merger extends JCovCMDTool {
+
+    /**
+     * Do not ignore any error
+     */
+    public final static String LOOSE_0 = "0";
+    /**
+     * Ignore access and fields size errors
+     */
+    public final static String LOOSE_1 = "1";
+    /**
+     * Ignore signature and methods size errors (+ LOOSE_1)
+     */
+    public final static String LOOSE_2 = "2";
+    /**
+     * Ignore checksum mismatch (+ LOOSE_2)
+     */
+    public final static String LOOSE_3 = "3";
+    public final static String LOOSE_BLOCKS = "blocks";
+    private String outTestList;
+    private String output;
+    private String skippedPath;
+    private String template;
+    private String[] srcs;
+    private BreakOnError boe = BreakOnError.NONE;
+    private boolean read_scales = false;
+    private ClassSignatureFilter readFilter = null;
+    private int loose_lvl = 0;
+    private String[] include = new String[]{".*"};
+    private String[] exclude = new String[]{""};
+    private String[] fm = null;
+    private boolean compress;
+    private boolean sigmerge = false;
+    private boolean addMissing = true;
+    private boolean warningCritical = false;
+    private static ScaleCompressor compressor = new SimpleScaleCompressor();
+    private final static Logger logger;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(Merger.class.getName());
+    }
+
+    /**
+     * <p> There could be a number of errors in XML which JCov Merger can not
+     * resolve:
+     *
+     * <ul> <li>empty, malformed (non-XML) or damaged (not full) file </li>
+     * <li>coverage type in a file is different from previous file (e.g. in
+     * first it was Method coverage and in the second - Block) </li> <li>a file
+     * contains coverage data of another product version or another product
+     * </li> </ul> </p> <p> BreakOnError.FILE means than Merger will find all
+     * the problems in the first malformed file and then will stop merging. </p>
+     * <p> BreakOnError.ERROR means that Merger will stop at the very first
+     * occurred error. </p> <p> BreakOnError.NONE means that Merger will stop
+     * only when all files would be processed regardless on errors. </p> <p>
+     * BreakOnError.SKIP means that merger will not stop on malformed data but
+     * will skip such files. The list of skipped files would be saved in Merge
+     * object. </p>
+     *
+     * @see Merge
+     * @see #setBreakOnError(com.sun.tdk.jcov.Merger.BreakOnError)
+     */
+    public static enum BreakOnError {
+
+        FILE("file"), ERROR("error"), TEST("test"), NONE("none", "default"), SKIP("skip");
+        private String[] aliases;
+
+        private BreakOnError(String... str) {
+            this.aliases = str;
+        }
+
+        public static BreakOnError fromString(String name) {
+            for (BreakOnError boe : values()) {
+                for (String s : boe.aliases) {
+                    if (s.equalsIgnoreCase(name)) {
+                        return boe;
+                    }
+                }
+            }
+            return null;
+        }
+
+        public static BreakOnError getDefault() {
+            return NONE;
+        }
+    }
+
+    /**
+     * Merging info. Contains JCov results and template (if needed). After
+     * merging contains result, skipped files list, error number and testlist
+     */
+    public static class Merge {
+
+        private final Result[] jcovFiles;
+        private final String template;
+        private DataRoot result;
+        private List<String> skippedFiles;
+        private int errors;
+        private int warnings;
+        private String[] resultTestList;
+
+        public Merge(Result[] files, String template) {
+            this.jcovFiles = files;
+            this.template = template;
+        }
+
+        /**
+         * <p> Creates Merge object from a number of XML files, their testlists
+         * filenames and template. </p>
+         *
+         * @param files Files to merge
+         * @param testlists Testlists assigned to each file. Can be null. Some
+         * elements of the array can be null. Length of <b>testlists</b> can be
+         * lesser than <b>files</b>
+         * @param template Template to merge these files with. If template is
+         * set - Merger will merge only those elements which exist in the
+         * template. <p>Can be null.</p>
+         * @throws IOException
+         */
+        public Merge(String[] files, String[] testlists, String template) throws IOException {
+            this.jcovFiles = new Result[files.length];
+            for (int i = 0; i < files.length; ++i) {
+                if (testlists.length > i && testlists[i] != null) {
+                    jcovFiles[i] = new Result(files[i], testlists[i]);
+                } else {
+                    jcovFiles[i] = new Result(files[i]);
+                }
+            }
+            this.template = template;
+        }
+
+        /**
+         * <p> When Merger finds a problem during merge (e.g. empty or damaged
+         * file) it can skip it if BreakOnError is set to SKIP. Names of skipped
+         * files are stored in a list and can be accessed after the merge
+         * finishes. </p>
+         *
+         * @return List of files which were skipped during the merge routine.
+         * Returns null if merge was not started yet or no files were skipped
+         * @see BreakOnError
+         * @see Merger#setBreakOnError(com.sun.tdk.jcov.Merger.BreakOnError)
+         *
+         */
+        public List<String> getSkippedFiles() {
+            return skippedFiles;
+        }
+
+        /**
+         * @return Result test list of merged data. Note that outTestList should
+         * be passed to Merger in order to generate testlist.
+         */
+        public String[] getResultTestList() {
+            return resultTestList;
+        }
+
+        /**
+         * <p> When Merger finds a problem during merge (e.g. empty or damaged
+         * file) it can skip it if BreakOnError is set to SKIP. Names of skipped
+         * files are stored in a list and can be accessed after the merge
+         * finishes. </p>
+         *
+         * @return Count of files skipped during merge routine.
+         * @see BreakOnError
+         * @see Merger#setBreakOnError(com.sun.tdk.jcov.Merger.BreakOnError)
+         * @see #getSkippedFiles()
+         */
+        public int getSkippedCount() {
+            if (skippedFiles == null) {
+                return 0;
+            }
+            return skippedFiles.size();
+        }
+
+        /**
+         * <p> There could be a number of errors in XML which JCov Merger can
+         * not resolve:
+         *
+         * <ul> <li>empty, malformed (non-XML) or damaged (not full) file </li>
+         * <li>coverage type in a file is different from previous file (e.g. in
+         * first it was Method coverage and in the second - Block) </li> <li>a
+         * file contains coverage data of another product version or another
+         * product </li> </ul>
+         *
+         * To control Merger behavior on error occurrence use BreakOnError </p>
+         *
+         * @return Number of critical errors found during the merge.
+         * @see BreakOnError
+         * @see Merger#setBreakOnError(com.sun.tdk.jcov.Merger.BreakOnError)
+         */
+        public int getErrors() {
+            return errors;
+        }
+
+        /**
+         * <p> When Merger founds that a file contains coverage data collected
+         * on another Java version it prints a warning. This warning can be
+         * turned to error with <b>setWarningCritical()</b> method to prevent
+         * JCov merge coverage collected on different Java platforms. </p>
+         *
+         * @return Number of warnings occurred during merge.
+         * @see Merger#setWarningCritical(boolean)
+         * @see BreakOnError
+         * @see Merger#setBreakOnError(com.sun.tdk.jcov.Merger.BreakOnError)
+         */
+        public int getWarnings() {
+            return warnings;
+        }
+
+        void addSkippedFile(String file) {
+            if (skippedFiles == null) {
+                skippedFiles = new LinkedList();
+            }
+            skippedFiles.add(file);
+        }
+
+        /**
+         * @return Merge result as DataRoot object or null if Merger was not
+         * started or failed (e.g. only one file found)
+         */
+        public DataRoot getResult() {
+            return result;
+        }
+    }
+
+    /**
+     * legacy entry point
+     *
+     * @param args
+     * @param logStream
+     * @throws Exception
+     */
+    @Deprecated
+    public static void innerMain(String args[], PrintStream logStream) throws Exception {
+        Merger merger = new Merger();
+        merger.run(args);
+    }
+
+    /**
+     * CLI entry point. Do not use it as API entry point - System.exit is called
+     * here
+     *
+     * @param args
+     */
+    public static void main(String args[]) {
+        Merger merger = new Merger();
+        try {
+            int res = merger.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+
+///////// BODY /////////
+    /**
+     * Write merged data to a file
+     *
+     * @param merge merged data
+     * @param outputPath path to write
+     * @param outputTestList path to write testlist to
+     * @param skippedPath path to write skipped files list to
+     */
+    public void write(Merge merge, String outputPath, String outputTestList, String skippedPath) throws IOException {
+        FileSaver saver = FileSaver.getFileSaver(merge.result, outputPath, template, MERGE.MERGE, false, read_scales);
+        if (compress) {
+            merge.result.getScaleOpts().setScalesCompressed(true);
+        }
+
+        try {
+            logger.log(Level.INFO, "- Writing result to {0}", outputPath);
+            saver.saveResults(outputPath);
+        } catch (Exception ex) {
+            throw new IOException("Can't write result file", ex);
+        }
+
+        try {
+            if (merge.resultTestList != null) {
+                Utils.writeLines(outputTestList, merge.resultTestList);
+            }
+        } catch (IOException ex) {
+            throw new IOException("Cannot create resulting test list: " + outputTestList + ": ", ex);
+        }
+
+        try {
+            if (skippedPath != null && merge.getSkippedCount() > 0) {
+                Utils.writeLines(skippedPath, merge.getSkippedFiles().toArray(new String[merge.getSkippedFiles().size()]));
+            }
+        } catch (IOException ex) {
+            throw new IOException("Cannot create skipped files list: " + skippedPath + ": ", ex);
+        }
+    }
+
+    /**
+     * Merge and write data (api entry point)
+     *
+     * @param jcovFiles files to merge
+     * @param outTestList path to write testlist to
+     * @param output path to write
+     * @param template template to use for merging (golden data)
+     * @param skippedPath path to write skipped files list to
+     * @return merged and written data in Merge object
+     * @throws IOException
+     */
+    public Merge mergeAndWrite(String[] jcovFiles, String outTestList, String output, String template, String skippedPath) throws IOException {
+        this.output = output;
+        this.outTestList = outTestList;
+        this.template = template;
+        this.srcs = jcovFiles;
+        this.skippedPath = skippedPath;
+
+        logger.log(Level.INFO, "- Reading test lists");
+        Result results[];
+        try {
+            results = initResults(jcovFiles, read_scales);
+        } catch (IOException ex) {
+            throw new IOException("Can't read test lists", ex);
+        }
+        Merge merge = new Merge(results, template);
+
+        return mergeAndWrite(merge, outTestList, output, skippedPath);
+    }
+
+    /**
+     * Merge and write data
+     *
+     * @param merge merging data
+     * @param outTestList path to write testlist to
+     * @param output to write
+     * @param skippedPath path to write skipped files list to
+     * @return merged data
+     */
+    public Merge mergeAndWrite(Merge merge, String outTestList, String output, String skippedPath) throws IOException {
+        merge(merge, outTestList, false);
+
+        // merge.result == null when errors occurred or BOE.TEST is set
+        if (merge.result != null && output != null && !"".equals(output)) {
+            write(merge, output, outTestList, skippedPath);
+        }
+        return merge;
+    }
+
+    /**
+     * Merge data
+     *
+     * @param merge merging data
+     * @param outTestList path to write testlist to (testlist data will not be
+     * collected if null, this method does NOT write the file)
+     */
+    public void merge(Merge merge, String outTestList) {
+        merge(merge, outTestList, false);
+    }
+
+    /**
+     * Merge data
+     *
+     * @param merge merging data
+     * @param outTestList path to write testlist to (testlist data will not be
+     * collected if null, this method does NOT write the file)
+     * @param ingoreOriginalScales files will be merged ignoring scales from
+     * these files
+     */
+    public void merge(Merge merge, String outTestList, boolean ignoreOriginalScales) {
+
+        readFilter = new ClassSignatureFilter(include, exclude, fm);
+        DataRoot merged = null;
+        DataRoot rNext = null;
+        logger.log(Level.INFO, "- Merging started");
+        int filesMerged = 0;
+        String mergingRes;
+        for (int i = 0; i < merge.jcovFiles.length; i++) {
+            mergingRes = merge.jcovFiles[i].getResultPath();
+            try {
+                if (i == 0 && merge.template != null) {
+                    logger.log(Level.INFO, "-- Reading jcov template {0}", merge.template);
+                    merged = Reader.readXML(merge.template, read_scales, readFilter); // template should not contain scales
+                    merged.getScaleOpts().setScaleSize(0); // template should not be counted in scales
+                    if (sigmerge) {
+                        merged.truncateToMethods(); // leaving only methods information in source XML
+                    }
+
+                    // scales are set to "0" 4 lines before
+//                    if (merged.getScaleOpts().getScaleSize() > 1) {
+//                        logger.log(Level.SEVERE, "Template {0} has not null scale size: found {1}; expected 1", new Object[]{merge.template, merged.getScaleOpts().getScaleSize()});
+//                        merge.errors++;
+//                        break;
+//                    }
+                    if (merged.getParams().isDynamicCollect()) {
+                        logger.log(Level.SEVERE, "File {0} is dynamic collected coverage data and can't be used as template", merge.template);
+                        merge.errors++;
+                        break;
+                    }
+                    if (outTestList != null && read_scales && merged.getScaleOpts().getScaleSize() == 0) {
+                        merged.createScales();
+                    }
+
+                    logger.log(Level.INFO, "-- Merging all with template {0}. Java version: {1}, generated {2}", new Object[]{merge.template, merged.getXMLHeadProperties().get("java.runtime.version"), merged.getXMLHeadProperties().get("coverage.created.date")});
+                    filesMerged++; // allowing to merge 1 data file with template
+                }
+
+                logger.log(Level.FINE, "-- Reading jcov file {0}", mergingRes);
+
+                rNext = Reader.readXML(mergingRes, read_scales, readFilter);
+
+                if (outTestList != null) {
+                    logger.log(Level.FINE, "-- Reading testlist for jcov file {0}", mergingRes);
+
+                    if (ignoreOriginalScales) {
+                        rNext.cleanScales();
+                        rNext.createScales();
+                    }
+
+                    ScaleOptions scaleOpts = rNext.getScaleOpts();
+                    String[] tlist = merge.jcovFiles[i].getTestList();
+
+                    if (scaleOpts.getScaleSize() != tlist.length) {
+                        logger.log(Level.SEVERE, "Inconsistent scale sizes: in file {0}: {1}; expected: {2}", new Object[]{mergingRes, scaleOpts.getScaleSize(), tlist.length});
+                        if (boe == BreakOnError.SKIP) {
+                            merge.addSkippedFile(mergingRes);
+                            continue;
+                        }
+                        merge.errors++;
+                        if (boe == BreakOnError.ERROR || boe == BreakOnError.FILE) {
+                            break;
+                        }
+                    }
+                    scaleOpts.setTestList(tlist);
+                    scaleOpts.setOutTestList(outTestList);
+                }
+
+                if (merged == null) {
+                    merged = rNext;
+                    logger.log(Level.INFO, "-- Merging all with {0}. Java version: {1}, generated {2}", new Object[]{mergingRes, merged.getXMLHeadProperties().get("java.runtime.version"), merged.getXMLHeadProperties().get("coverage.created.date")});
+                    if (sigmerge) {
+                        merged.truncateToMethods(); // leaving only methods information in source XML
+                    }
+
+                    // we will lost fist test scales without creating them manually after reading file
+                    if (read_scales && merged.getScaleOpts().getScaleSize() == 0) {
+                        merged.createScales();
+                    }
+
+                    filesMerged++;
+                    continue;
+                }
+
+                logger.log(Level.INFO, "-- Merging {0}", mergingRes);
+
+                if (sigmerge) {
+                    merged.mergeOnSignatures(rNext, addMissing);
+                } else {
+                    DataRoot.CompatibilityCheckResult localErrors;
+                    localErrors = merged.checkCompatibility(rNext, loose_lvl, boe == BreakOnError.ERROR);
+                    merge.errors += localErrors.errors;
+                    merge.warnings += localErrors.warnings;
+                    if (warningCritical) {
+                        localErrors.errors += localErrors.warnings;
+                    }
+
+                    if (localErrors.errors != 0) {
+                        if (boe == BreakOnError.SKIP) {
+                            logger.log(Level.INFO, "-- File {0} has {1} critical error(s) and will be skipped", new Object[]{mergingRes, localErrors.errors});
+                            merge.addSkippedFile(mergingRes);
+                            continue; // just skip file without merging
+                        }
+
+                        if (boe == BreakOnError.FILE || boe == BreakOnError.ERROR) {
+                            logger.log(Level.SEVERE, "-- File {0} has {1} critical error(s). Stopping merging process (break on error set)", new Object[]{mergingRes, localErrors.errors});
+                            break;
+                        }
+
+                        logger.log(Level.INFO, "-- File {0} has {1} critical errors", new Object[]{mergingRes, localErrors.errors});
+                    }
+
+                    // all OK - merging
+                    merged.merge(rNext, addMissing);
+                }
+                filesMerged++;
+            } catch (FileFormatException ex) {
+                merge.errors++;
+                if (boe == BreakOnError.SKIP) {
+                    logger.log(Level.SEVERE, "Skipping malformed xml file {0}: " + ex.getMessage(), new Object[]{mergingRes});
+                    merge.addSkippedFile(mergingRes);
+                    continue;
+                }
+
+                if (boe == BreakOnError.FILE || boe == BreakOnError.ERROR) {
+                    if (i == 0 && merge.template != null) {
+                        logger.log(Level.SEVERE, "Stopping on malformed xml template {0}: {1}", new Object[]{merge.template, ex.getMessage()});
+                        break;
+                    } else {
+                        logger.log(Level.SEVERE, "Stopping on malformed xml file {0}: {1}", new Object[]{mergingRes, ex.getMessage()});
+                        break;
+                    }
+                }
+
+                logger.log(Level.SEVERE, "Malformed xml file: " + ex.getMessage(), mergingRes);
+            } catch (Throwable th) {
+                logger.log(Level.SEVERE, "Critical error while merging file " + mergingRes, th);
+                th.printStackTrace();
+                merge.errors++;
+                return;
+            }
+        }
+
+
+        if (outTestList != null) {
+            if (merge.errors == 0 || boe == BreakOnError.SKIP) {
+                logger.log(Level.INFO, "- Generating result testlist");
+                if (merge.getSkippedCount() > 0) {
+                    Result[] newres = new Result[merge.jcovFiles.length - merge.getSkippedCount()];
+                    int newresI = 0;
+                    outer:
+                    for (int i = 0; i < merge.jcovFiles.length; ++i) {
+                        for (int j = 0; j < merge.getSkippedCount(); ++j) {
+                            if (merge.jcovFiles[i].getResultPath().equals(merge.getSkippedFiles().get(j))) {
+                                continue outer;
+                            }
+                        }
+                        newres[newresI++] = merge.jcovFiles[i];
+                    }
+                    merge.resultTestList = generateTestList(outTestList, newres, merged, merge.template != null);
+                } else {
+                    merge.resultTestList = generateTestList(outTestList, merge.jcovFiles, merged, merge.template != null);
+                }
+            } else {
+                logger.log(Level.SEVERE, "Don't generating result testlist - errors occurred");
+            }
+        }
+
+        if ((merge.errors > 0 && boe != BreakOnError.SKIP) || boe == BreakOnError.TEST) {
+            logger.log(Level.SEVERE, "- Merging failed. Use \"-boe skip\" option to ignore bad files.");
+            return; // DataRoot is not set to Merge
+        }
+
+        logger.log(Level.INFO, "- Merging complete");
+        if (merge.getSkippedCount() > 0) {
+            logger.log(Level.SEVERE, "- {0} files were skipped: ", merge.getSkippedCount());
+            int i = 1;
+            for (String skipped : merge.getSkippedFiles()) {
+                logger.log(Level.SEVERE, "-- {0}", skipped);
+            }
+        }
+
+        if (filesMerged < 2) {
+            if (merge.getSkippedCount() > 0) {
+                logger.log(Level.SEVERE, "- Not enough correct files to perform merging. Found {0} correct files and {1} were skipped due to errors while needed at least 2 correct files", new Object[]{filesMerged, merge.getSkippedCount()});
+            } else {
+                logger.log(Level.SEVERE, "- Not enough correct files to perform merging. Found {0} correct files while needed at least 2", filesMerged);
+            }
+            return;
+        }
+
+        merge.result = merged;
+    }
+
+    protected int run() throws Exception {
+        Result results[];
+        try {
+            results = initResults(srcs, read_scales);
+        } catch (IOException ex) {
+            throw new IOException("Can't read test lists", ex);
+        }
+
+        Merge merge = new Merge(results, template);
+        mergeAndWrite(merge, outTestList, output, skippedPath);
+
+        if (boe == BreakOnError.TEST || warningCritical) {
+            return merge.errors + merge.warnings;
+        } else {
+            return merge.errors;
+        }
+    }
+
+    protected String usageString() {
+        return "java com.sun.tdk.jcov.Merger [options] <filenames>";
+    }
+
+    protected String exampleString() {
+        return "java -cp jcov.jar com.sun.tdk.jcov.Merger -include java.lang.* -scale -output merged.xml test1.xml test2.xml";
+    }
+
+    protected String getDescr() {
+        return "merges several jcov data files";
+    }
+
+    private String[] generateTestList(String outTestList, Result[] results,
+            Object root, boolean cleanTmpl) {
+
+        // list of unique test names to be written to output file
+        ArrayList testList = new ArrayList();
+
+        // pairs of duplicate tests
+        ArrayList pairs = new ArrayList();
+
+        int cur = 0;
+        int length = cleanTmpl ? results.length + 1 : results.length;
+        for (int i = 0; i < length; i++) {
+
+            String[] tlist;
+            if (cleanTmpl) {
+                if (i == 0) {
+                    tlist = new String[]{results[0].getTestList()[0]};
+                } else {
+                    tlist = results[i - 1].getTestList();
+                }
+            } else {
+                tlist = results[i].getTestList();
+            }
+            for (int j = 0; j < tlist.length; j++) {
+                int found = testList.indexOf(tlist[j]);
+                if (found < 0) {
+                    testList.add(tlist[j]);
+                } else {
+                    pairs.add(new Utils.Pair(found, cur));
+                }
+                cur++;
+            }
+        }
+        if (root instanceof DataRoot) {
+            ((DataRoot) root).illuminateDuplicatesInScales(pairs);
+        }
+        return (String[]) testList.toArray(new String[testList.size()]);
+    }
+
+    /**
+     * Decodes list of jcov file names. File name may be in one of three forms:
+     * <ul> <li> a.jcov#test_a - jcov for the test "test_a" </li> <li>
+     * b.jcov%test.lst - jcov for the tests listed in the file "test.lst"</li>
+     * <li> c.jcov - jcov for the test "c.jcov"</li> </ul> Method removes "#..."
+     * and "%..." from the names of jcov files. Returned structure will contain
+     * test list for each passed jcov file.<br> Example: <br>
+     * <code>
+     * initTestList({"a.jcov#test_a", "b.jcov%test.lst", "c.jcov"})
+     * will return:
+     * {
+     *   {"test_a"},
+     *   {"t1, t2, t3, t4"}, // lines of test.lst file
+     *   {"c.jcov"},
+     * }
+     * passed array will be transformed to {"a.jcov", "b.jcov", "c.jcov"}
+     * </code>
+     *
+     * @param jcov_files - jcov file lists
+     * @return array of String arrays, where i-th row is comprised of tests for
+     * jcov_files[i]
+     */
+    public static Result[] initResults(String[] jcov_files, boolean initTestlists) throws IOException {
+
+        Result[] results = new Result[jcov_files.length];
+        for (int i = 0; i < jcov_files.length; i++) {
+            results[i] = new Result();
+            String str = jcov_files[i];
+            int k = str.indexOf('%');
+            if (k < 0) {
+                k = str.indexOf('#');
+                if (k < 0) {
+                    results[i].setResultPath(str);
+                    if (initTestlists) {
+                        results[i].setDefaultName();
+                    }
+                } else {
+                    results[i].setResultPath(str.substring(0, k));
+                    if (initTestlists) {
+                        results[i].setTestName(str.substring(k + 1));
+                    }
+                }
+            } else {
+                results[i].setResultPath(str.substring(0, k));
+                if (initTestlists) {
+                    results[i].readTestList(str.substring(k + 1));
+                }
+            }
+        }
+        return results;
+    }
+
+    /**
+     *
+     * @param path Read testlist from file
+     * @return read lines as array
+     * @throws IOException
+     */
+    public static String[] initTestList(String path) throws IOException {
+        return Utils.readLines(path);
+    }
+
+    /**
+     * Read testlist from file using first and last as borders in the files
+     *
+     * @param path file to read
+     * @param first the first line to read
+     * @param last the last line to read
+     * @return read lines as array
+     * @throws IOException
+     */
+    public static String[] initTestList(String path, int first, int last) throws IOException {
+        return Utils.readLines(path, first, last);
+    }
+
+    /**
+     * JCov Merger allows to specify source files among with testlist (eg
+     * result.xml%testlist) or testname (eg result.xml#test1).
+     *
+     * @param jcov_file filename to parse
+     * @return Initialized Result object
+     * @throws IOException
+     * @see Result
+     */
+    public static Result parseResultFromString(String jcov_file) throws IOException {
+        Result res;
+        int k = jcov_file.indexOf('%'); // testlist file separator
+        if (k < 0) {
+            k = jcov_file.indexOf('#'); // testname separator
+            if (k > 0) {
+                res = new Result(jcov_file.substring(0, k), new String[]{jcov_file.substring(k + 1)});
+            } else {
+                res = new Result(jcov_file);
+            }
+        } else {
+            res = new Result(jcov_file.substring(0, k), jcov_file.substring(k + 1));
+        }
+
+        return res;
+    }
+
+///////// public API getters & setters /////////
+    /**
+     * Sets default (ClassSignatureAcceptor) acceptor using include, exclude and
+     * fm.
+     *
+     * @param include
+     * @param exclude
+     * @param fm
+     */
+    public void setDefaultReadingFilter(String[] include, String[] exclude, String[] fm) {
+        this.include = include;
+        this.exclude = exclude;
+        this.fm = fm;
+        this.readFilter = new ClassSignatureFilter(include, exclude, fm);
+    }
+
+    /**
+     * Set ClassSignatureFilter used by this Merger to read the DataRoot
+     *
+     * @param filter
+     */
+    public void setReadingFilter(ClassSignatureFilter filter) {
+        this.readFilter = filter;
+    }
+
+    public BreakOnError getBreakOnError() {
+        return boe;
+    }
+
+    public void setBreakOnError(BreakOnError boe) {
+        this.boe = boe;
+    }
+
+    public static ScaleCompressor getCompressor() {
+        return compressor;
+    }
+
+    public static void setCompressor(ScaleCompressor compressor) {
+        Merger.compressor = compressor;
+    }
+
+    public int getLoose_lvl() {
+        return loose_lvl;
+    }
+
+    public void setLoose_lvl(int loose_lvl) {
+        this.loose_lvl = loose_lvl;
+    }
+
+    public boolean isRead_scales() {
+        return read_scales;
+    }
+
+    public void setRead_scales(boolean read_scales) {
+        this.read_scales = read_scales;
+    }
+
+    public boolean isCompress() {
+        return compress;
+    }
+
+    public void setCompress(boolean compress) {
+        this.compress = compress;
+    }
+
+    public void setFilters(String[] include, String[] exclude, String[] classModifiers) {
+        if (include == null) {
+            include = new String[]{".*"};
+        }
+        this.include = include;
+        if (exclude == null) {
+            exclude = new String[]{""};
+        }
+        this.exclude = exclude;
+        this.fm = classModifiers;
+    }
+
+    public void setClassModifiers(String[] fm) {
+        this.fm = fm;
+    }
+
+    public void resetDefaults() {
+        try {
+            handleEnv_(defineHandler());
+            setBreakOnError(BreakOnError.NONE);
+            setRead_scales(false);
+            setReadingFilter(null);
+            setLoose_lvl(0);
+            setCompress(false);
+            warningCritical = false;
+        } catch (EnvHandlingException ex) {
+            // should not happen
+        }
+    }
+
+    public String[] getExclude() {
+        return exclude;
+    }
+
+    public String[] getInclude() {
+        return include;
+    }
+
+    public String[] getFm() {
+        return fm;
+    }
+
+    public boolean isAddMissing() {
+        return addMissing;
+    }
+
+    public void setAddMissing(boolean addMissing) {
+        this.addMissing = addMissing;
+    }
+
+    public void setSigmerge(boolean sigmerge) {
+        this.sigmerge = sigmerge;
+    }
+
+    public boolean isSigmerge() {
+        return sigmerge;
+    }
+
+    public boolean isWarningCritical() {
+        return warningCritical;
+    }
+
+    public void setWarningCritical(boolean warningCritical) {
+        this.warningCritical = warningCritical;
+    }
+
+///////// JCovTool implementation /////////
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    DSC_OUTPUT,
+                    DSC_FILELIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FM,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FM_LIST,
+                    DSC_SCALE,
+                    DSC_OUTPUT_TEST_LIST,
+                    DSC_VERBOSE,
+                    DSC_LOOSE,
+                    DSC_COMPRESS,
+                    DSC_BREAKERR,
+                    DSC_WARNINGS,
+                    DSC_TEMPLATE,
+                    DSC_SKIPPED
+                }, this);
+    }
+
+    private int handleEnv_(EnvHandler opts) throws EnvHandlingException {
+        if (opts.isSet(DSC_FILELIST)) {
+            String val = opts.getValue(DSC_FILELIST);
+            int start = -1;
+            int end = -1;
+            String file = val;
+            int ind = val.indexOf(",");
+            if (ind != -1) {
+                file = val.substring(0, ind);
+                String rest = val.substring(ind + 1);
+                int second_ind = rest.indexOf(",");
+                if (second_ind == -1) {
+                    start = Integer.parseInt(rest);
+                } else {
+                    start = Integer.parseInt(rest.substring(0, second_ind));
+                    end = Integer.parseInt(rest.substring(second_ind + 1, rest.length()));
+                }
+            }
+            Utils.checkFileNotNull(file, "filelist filename", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_CANREAD, Utils.CheckOptions.FILE_ISFILE);
+            try {
+                srcs = Utils.readLines(file, start, end);
+            } catch (IOException ex) {
+                throw new EnvHandlingException("Can't read filelist " + val, ex);
+            }
+        }
+
+        include = InstrumentationOptions.handleInclude(opts);
+        exclude = InstrumentationOptions.handleExclude(opts);
+        fm = InstrumentationOptions.handleFM(opts);
+
+        read_scales = opts.isSet(DSC_SCALE);
+        if (opts.isSet(DSC_LOOSE)) {
+            String loose = opts.getValue(DSC_LOOSE);
+            if (LOOSE_BLOCKS.equals(loose)) {
+                sigmerge = true;
+            } else {
+                try {
+                    loose_lvl = Utils.checkedToInt(loose, "loose level", Utils.CheckOptions.INT_POSITIVE);
+                } catch (NumberFormatException nfe) {
+                    throw new EnvHandlingException("Can't parse loose level " + loose, nfe);
+                }
+            }
+        }
+
+        outTestList = null;
+        if (opts.isSet(DSC_OUTPUT_TEST_LIST)) {
+            outTestList = opts.getValue(DSC_OUTPUT_TEST_LIST);
+            Utils.checkFileNotNull(outTestList, "output testlist filename", Utils.CheckOptions.FILE_CANWRITE, Utils.CheckOptions.FILE_NOTISDIR, Utils.CheckOptions.FILE_PARENTEXISTS);
+            read_scales = true;
+        }
+        skippedPath = opts.getValue(DSC_SKIPPED);
+
+        String boestr = opts.getValue(DSC_BREAKERR);
+        if ("error".equalsIgnoreCase(boestr)) {
+            boe = BreakOnError.ERROR;
+        } else if ("test".equalsIgnoreCase(boestr)) {
+            boe = BreakOnError.TEST;
+        } else if ("file".equalsIgnoreCase(boestr)) {
+            boe = BreakOnError.FILE;
+        } else if ("skip".equalsIgnoreCase(boestr)) {
+            boe = BreakOnError.SKIP;
+        } else {
+            boe = BreakOnError.NONE;
+        }
+
+        template = opts.getValue(DSC_TEMPLATE);
+        Utils.checkFileCanBeNull(template, "template filename", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_CANREAD);
+
+        addMissing = template == null;
+
+        output = opts.getValue(DSC_OUTPUT);
+        Utils.checkFileNotNull(output, "output filename", Utils.CheckOptions.FILE_NOTISDIR, Utils.CheckOptions.FILE_CANWRITE, Utils.CheckOptions.FILE_PARENTEXISTS);
+
+        compress = opts.isSet(DSC_COMPRESS);
+        if (opts.isSet(DSC_VERBOSE)) {
+            logger.setLevel(Level.INFO);
+        }
+
+        warningCritical = opts.isSet(DSC_WARNINGS);
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        srcs = opts.getTail();
+
+        int code = handleEnv_(opts);
+
+        if (srcs == null || srcs.length == 0) { // srcs could be set from filelist
+            throw new EnvHandlingException("No input files specified");
+        }
+        return code;
+    }
+    final static OptionDescr DSC_SKIPPED =
+            new OptionDescr("outSkipped", "path for list of skipped files", OptionDescr.VAL_SINGLE,
+            "Sets path to the list of files that were skipped during the merge by '-boe skip' option");
+    final static OptionDescr DSC_TEMPLATE =
+            new OptionDescr("template", new String[]{"tmpl", "t"}, "template path", OptionDescr.VAL_SINGLE,
+            "Sets path to the template used for merging. Only data in template will be merged");
+    final static OptionDescr DSC_BREAKERR =
+            new OptionDescr("breakonerror", new String[]{"boe", "onerror"}, "break on error",
+            new String[][]{{"file", "break when finished merging first file with errors"},
+                {"error", "break on the first occurred error"},
+                {"test", "don't break on any error and don't write result file"},
+                {"skip", "merge all correct files. Attention: first jcov file is always considered to be correct. Use '-template' option to ensure correct results."},
+                {"none", "don't break on any error and write result file if all passed. Neither result file neither testlist would be written if any error will occur."}},
+            "Sets type of handling errors in merging process. Can be 'none', 'test', 'error', 'skip' and 'file'", "file");
+    final static OptionDescr DSC_OUTPUT =
+            new OptionDescr("merger.output", new String[]{"output", "o"}, "output file", OptionDescr.VAL_SINGLE,
+            "Output file for generating new profiler data file.", "merged.xml");
+    final static OptionDescr DSC_FILELIST =
+            new OptionDescr("filelist", "file to read jcov input files from", OptionDescr.VAL_SINGLE,
+            "Text file to read jcov data files for merge from. One file name per line.\n"
+            + "The option allows to specify a range of lines to be read:\n"
+            + "-filelist=<file>,first_line,last_line");
+    final static OptionDescr DSC_SCALE =
+            new OptionDescr("scale", "process/generate test scales",
+            "Process/generate test scale that lets find tests covering specific\n"
+            + "code sections (no scale by default)");
+    final static OptionDescr DSC_COMPRESS =
+            new OptionDescr("compress", "compress test scales",
+            "Compress test scales.");
+    final static OptionDescr DSC_VERBOSE =
+            new OptionDescr("verbose", new String[]{"v"}, "verbose mode", "Enables verbose mode");
+    final static OptionDescr DSC_OUTPUT_TEST_LIST =
+            new OptionDescr("outTestList", "", OptionDescr.VAL_SINGLE,
+            "Generate summary test list. Test names will be extracted from the filename\n"
+            + "operands, which may be specified in one of the following ways:\n"
+            + "    a.jcov#test_a    coverage file for 'test_a'\n"
+            + "    b.jcov%test.lst  coverage file for tests listed in 'test.lst'\n"
+            + "    testC/c.jcov     coverage file for 'testC/c.jcov'");
+    final static OptionDescr DSC_LOOSE =
+            new OptionDescr("loose",
+            "looseness level",
+            new String[][]{
+                {LOOSE_0, "none (strict)"},
+                {LOOSE_1, "moderate"},
+                {LOOSE_2, "high"},
+                {LOOSE_3, "highest"},
+                {LOOSE_BLOCKS, "drop blocks"}
+            },
+            "Sets the \"looseness\" level of merger operation.\n"
+            + "0 - default strict mode. All errors are treated as fatal.\n"
+            + "1 - warning instead of an error when merging two classes "
+            + "with the same name and common timestamp, whose coverage "
+            + "item counts don't match.\n"
+            + "2 - warning instead of an error when merging two classes "
+            + "with the same name and the same arbitrary timestamp, "
+            + "whose coverage item counts don't match.\n"
+            + "Also allows to merge without warning DATA: B and DATA: C classes\n"
+            + "3 - warning instead of any error\n"
+            + "blocks - all blocks information would be dropped. JCov data will be "
+            + "truncated to method coverage without any checks in block structure and then merged by signatures",
+            LOOSE_0);
+    final static OptionDescr DSC_WARNINGS =
+            new OptionDescr("critwarn", "", OptionDescr.VAL_NONE, "Count warnings as errors",
+            "When set JCov will process warnings (e.g. java version missmatch) just as errors");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/ProductInstr.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.constants.MiscConstants;
+import com.sun.tdk.jcov.insert.AbstractUniversalInstrumenter;
+import com.sun.tdk.jcov.instrument.ClassMorph;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.instrument.InstrumentationParams;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.RuntimeUtils;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author Andrey Titov
+ */
+public class ProductInstr extends JCovCMDTool {
+    // instrumentation filters (include/exclude, fields, abstract, ...); removing temporary directory
+
+    static final Logger logger;
+
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(ProductInstr.class.getName());
+    }
+    private File instrProductDir;
+    private File instrOutputDir;
+    private String template = MiscConstants.JcovTemplateFileNameXML;
+    private String tempPath;
+    private File rtLibFile;
+    private String[] rtClassDirTargets;
+
+    public void instrumentProduct() throws IOException {
+        logger.log(Level.INFO, " - Instrumenting product");
+        logger.log(Level.CONFIG, "Product location: ''{0}'', target location: ''{1}''", new Object[]{instrProductDir.getPath(), instrOutputDir.getPath()});
+
+        tempPath = instrProductDir.getPath() + RuntimeUtils.genSuffix();
+        createTempDir();
+
+        AbstractUniversalInstrumenter instrumenter = setupInstrumenter();
+
+        instrOutputDir.mkdir();
+        instrumenter.instrument(instrProductDir, instrOutputDir, rtLibFile.getAbsolutePath(), rtClassDirTargets == null ? null : new ArrayList(Arrays.asList(rtClassDirTargets)), true);
+        instrumenter.finishWork();
+
+        removeTempDir();
+    }
+
+    private AbstractUniversalInstrumenter setupInstrumenter() {
+        InstrumentationParams params = new InstrumentationParams(false, false, true, null, null, InstrumentationOptions.InstrumentationMode.BRANCH)
+                .setInstrumentAnonymous(true)
+                .setInstrumentSynthetic(false);
+
+        final ClassMorph morph = new ClassMorph(params, null);
+        return new AbstractUniversalInstrumenter(true) {
+            @Override
+            protected byte[] instrument(byte[] classData, int classLength) throws IOException {
+                return morph.morph(classBuf, null, tempPath);
+            }
+
+            @Override
+            public void finishWork() {
+                morph.saveData(template, null, InstrumentationOptions.MERGE.OVERWRITE);
+            }
+        };
+    }
+
+    private void createTempDir() {
+        new File(tempPath).mkdir();
+        logger.log(Level.INFO, "Temp directory for storing instrumented classes created: ''{0}''. Automatic removal is not implemented yet so please remove it manually after all is done.", tempPath);
+    }
+
+    private void removeTempDir() {
+        File tempFile = new File(tempPath);
+        if (tempFile.isDirectory()) {
+            Utils.deleteDirectory(tempFile);
+        } else {
+            tempFile.delete();
+        }
+        logger.log(Level.INFO, "Temp directory for storing instrumented classes deleted: ''{0}''.", tempPath);
+    }
+
+    @Override
+    protected int run() throws Exception {
+        instrumentProduct();
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    DSC_INSTRUMENT,
+                    DSC_INSTRUMENT_TO,
+                    Instr.DSC_INCLUDE_RT,
+                    DSC_RT_TO,}, this);
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler envHandler) throws EnvHandlingException {
+        instrProductDir = Utils.checkFileCanBeNull(envHandler.getValue(ProductInstr.DSC_INSTRUMENT), "product directory", Utils.CheckOptions.FILE_ISDIR, Utils.CheckOptions.FILE_CANREAD);
+        instrOutputDir = Utils.checkFileCanBeNull(envHandler.getValue(ProductInstr.DSC_INSTRUMENT_TO), "directory for instrumented product", Utils.CheckOptions.FILE_NOTEXISTS, Utils.CheckOptions.FILE_CANWRITE);
+        if (instrProductDir != null && instrOutputDir == null) {
+            throw new EnvHandlingException("Output directory for instrumented files should be specified in '-instrOutput'");
+        }
+        rtLibFile = Utils.checkFileCanBeNull(envHandler.getValue(Instr.DSC_INCLUDE_RT), "JCov RT Saver path", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_ISFILE, Utils.CheckOptions.FILE_CANREAD);
+        if (rtLibFile == null) {
+            throw new EnvHandlingException("Please specify saver location with '-rt' option");
+        }
+        rtClassDirTargets = envHandler.getValues(ProductInstr.DSC_RT_TO);
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected String getDescr() {
+        return "";
+    }
+
+    @Override
+    protected String usageString() {
+        return "";
+    }
+
+    @Override
+    protected String exampleString() {
+        return "";
+    }
+    public final static OptionDescr DSC_INSTRUMENT =
+            new OptionDescr("product", "", OptionDescr.VAL_SINGLE, "");
+    public final static OptionDescr DSC_INSTRUMENT_TO =
+            new OptionDescr("productOutput", "", OptionDescr.VAL_SINGLE, "");
+    public final static OptionDescr DSC_RT_TO = new OptionDescr("rtTo", "", OptionDescr.VAL_MULTI, "");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/RepGen.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,773 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.instrument.DataMethod;
+import com.sun.tdk.jcov.instrument.DataField;
+import com.sun.tdk.jcov.instrument.DataClass;
+import com.sun.tdk.jcov.processing.DataProcessorSPI;
+import com.sun.tdk.jcov.util.Utils;
+import com.sun.tdk.jcov.data.FileFormatException;
+import com.sun.tdk.jcov.data.Result;
+import com.sun.tdk.jcov.instrument.DataRoot;
+import com.sun.tdk.jcov.processing.ProcessingException;
+
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.io.Reader;
+import com.sun.tdk.jcov.filter.ConveyerFilter;
+import com.sun.tdk.jcov.filter.MemberFilter;
+import com.sun.tdk.jcov.filter.FilterFactory;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.io.ClassSignatureFilter;
+import com.sun.tdk.jcov.processing.DefaultDataProcessorSPI;
+import com.sun.tdk.jcov.processing.StubSpi;
+import com.sun.tdk.jcov.report.DefaultReportGeneratorSPI;
+import com.sun.tdk.jcov.report.ProductCoverage;
+import com.sun.tdk.jcov.report.ReportGenerator;
+import com.sun.tdk.jcov.report.ReportGeneratorSPI;
+import com.sun.tdk.jcov.report.SmartTestService;
+import com.sun.tdk.jcov.report.javap.JavapClass;
+import com.sun.tdk.jcov.report.javap.JavapRepGen;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.SPIDescr;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.sun.tdk.jcov.tools.OptionDescr.*;
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * <p> Report generation. </p>
+ *
+ * @author Andrey Titov
+ * @author Dmitry Fazunenko
+ * @author Alexey Fedorchenko
+ */
+public class RepGen extends JCovCMDTool {
+
+    final static String CUSTOM_REPORT_GENERATOR_SPI = "customreport.spi";
+    final static String DATA_PROCESSOR_SPI = "dataprocessor.spi";
+
+    // logger initialization
+    static {
+        Utils.initLogger();
+        logger = Logger.getLogger(RepGen.class.getName());
+    }
+    private final static Logger logger;
+    /**
+     * Consider or not enums. true - means ignore enum classes.
+     */
+    private boolean noEnums = false;
+    /**
+     * Whether show or not field coverage in the report (false by default).
+     */
+    private boolean showFields = false;
+    private ReportGeneratorSPI reportGeneratorSPIs[];
+    private DataProcessorSPI dataProcessorSPIs[];
+    private String[] include = new String[]{".*"};
+    private String[] exclude = new String[]{""};
+    private String[] fms = null;
+    private String filter = null;
+    private boolean noAbstract = false;
+    private boolean syntheticOn = false;
+    private boolean isPublicAPI = false;
+    private String[] filenames;
+    private String name;
+    private String outputDir;
+    private String testlist;
+    private String srcRootPath;
+    private boolean anonym = false;
+    private boolean withTestsInfo = false;
+    //path to the jar, dir or .class for javap repgen
+    private String classesPath;
+
+    public RepGen() {
+        readPlugins = true;
+    }
+
+    /**
+     * Generate report using default (html) report generator
+     *
+     * @param output
+     * @param jcovResult
+     * @throws ProcessingException
+     * @throws FileFormatException
+     * @throws Exception
+     */
+    public void generateReport(String output, Result jcovResult) throws ProcessingException, FileFormatException, Exception {
+        generateReport(getDefaultReportGenerator(), output, jcovResult, null);
+    }
+
+    /**
+     * Generate report using specified format
+     *
+     * @param format
+     * @param output
+     * @param jcovResult
+     * @throws ProcessingException
+     * @throws FileFormatException
+     * @throws Exception
+     */
+    public void generateReport(String format, String output, Result jcovResult) throws ProcessingException, FileFormatException, Exception {
+        generateReport(format, output, jcovResult, null);
+    }
+
+    /**
+     * Generate report using specified format
+     *
+     * @param format
+     * @param output
+     * @param jcovResult
+     * @param srcRootPath
+     * @throws ProcessingException
+     * @throws FileFormatException
+     * @throws Exception
+     */
+    public void generateReport(String format, String output, Result jcovResult, String srcRootPath) throws ProcessingException, FileFormatException, Exception {
+        ReportGenerator rg = null;
+        if (format != null) {
+            rg = findReportGenerator(format);
+        } else {
+            rg = getDefaultReportGenerator();
+        }
+        if (rg == null) {
+            throw new Exception("Specified ReportGenerator name (" + format + ") was not found");
+        }
+
+        generateReport(rg, output, jcovResult, srcRootPath);
+    }
+
+    /**
+     * Generate report using specified report generator
+     *
+     * @param rg
+     * @param output
+     * @param jcovResult
+     * @param srcRootPath
+     * @throws ProcessingException
+     * @throws FileFormatException
+     * @throws Exception
+     */
+    public void generateReport(ReportGenerator rg, String output, Result jcovResult, String srcRootPath) throws ProcessingException, FileFormatException, Exception {
+        generateReport(rg, output, jcovResult, srcRootPath, null);
+    }
+
+    /**
+     * Generate report using specified report generator
+     *
+     * @param rg
+     * @param output
+     * @param jcovResult
+     * @param srcRootPath
+     * @param classes parsed javap classes
+     * @throws ProcessingException
+     * @throws FileFormatException
+     * @throws Exception
+     */
+    public void generateReport(ReportGenerator rg, String output, Result jcovResult, String srcRootPath, List<JavapClass> classes) throws ProcessingException, FileFormatException, Exception {
+        try {
+            logger.log(Level.INFO, "-- Writing report to {0}", output);
+            rg.init(output);
+            logger.fine("OK");
+        } catch (Throwable ex) {
+            logger.log(Level.SEVERE, "Error while reading output file by ReportGenerator " + rg.getClass().getName(), ex);
+            return;
+        }
+
+        logger.log(Level.INFO, "-- Reading data from {0}", jcovResult.getResultPath());
+        DataRoot file_image = readDataRootFile(jcovResult.getResultPath(), jcovResult.isTestListSet(), include, exclude, fms);
+
+        if (!syntheticOn) {
+            file_image.applyFilter(new ANC_FILTER());
+        }
+
+        MemberFilter customFilter = null;
+        if (filter != null) {
+            logger.fine("-- Initializing custom filter");
+            customFilter = initCustomFilter(filter, null);
+            logger.fine("OK");
+        }
+        if (customFilter != null) {
+            logger.log(Level.INFO, "-- Applying filter {0}", customFilter.getClass().getName());
+            file_image.applyFilter(customFilter);
+            logger.fine("OK");
+        }
+
+        if (dataProcessorSPIs != null) {
+            for (DataProcessorSPI spi : dataProcessorSPIs) {
+                logger.log(Level.INFO, "-- Applying data processor {0}", spi.getClass());
+                file_image = spi.getDataProcessor().process(file_image);
+            }
+        }
+        logger.fine("OK");
+
+        SmartTestService sts = null;
+        if (jcovResult.isTestListSet()) {
+            logger.fine("-- Initializing test list");
+            sts = new SmartTestService(jcovResult.getTestList());
+            if (file_image.getScaleOpts().getScaleSize() != sts.getTestCount()) {
+                logger.log(Level.SEVERE, "The sizes of tests in JCov file and in test list differ.\n"
+                        + "Datafile {0} contains {1} item(s).\nThe test list contains {2} item(s).",
+                        new Object[]{jcovResult.getResultPath(), file_image.getScaleOpts().getScaleSize(), sts.getTestCount()});
+                throw new Exception("The sizes of tests in JCov file and in test list differ");
+            }
+            logger.fine("OK");
+        }
+        ReportGenerator.Options options = new ReportGenerator.Options(srcRootPath, sts, classes, withTestsInfo, false);
+        options.setInstrMode(file_image.getParams().getMode());
+        options.setAnonymOn(anonym);
+
+        try {
+            ProductCoverage coverage = new ProductCoverage(file_image, options.getSrcRootPaths(), options.getJavapClasses(), isPublicAPI, noAbstract, anonym);
+
+            logger.log(Level.INFO, "- Starting ReportGenerator {0}", rg.getClass().getName());
+            rg.generateReport(coverage, options);
+        } catch (Throwable ex) {
+            if (ex.getMessage() != null) {
+                throw new Exception("ReportGenerator produced exception " + ex.getMessage(), ex);
+            } else {
+                throw new Exception("ReportGenerator produced exception " + ex, ex);
+            }
+        }
+
+        logger.log(Level.INFO, "- Report generation done");
+        return;
+    }
+
+    /**
+     * Get default (html) report generator
+     *
+     * @return default (html) report generator
+     */
+    public ReportGenerator getDefaultReportGenerator() {
+        return findReportGenerator("html");
+    }
+
+    private ReportGenerator findReportGenerator(String name) {
+        ReportGenerator rg = null;
+        if (reportGeneratorSPIs != null) {
+            for (ReportGeneratorSPI reportGeneratorSPI : reportGeneratorSPIs) {
+                rg = reportGeneratorSPI.getReportGenerator(name);
+                if (rg != null) {
+                    return rg;
+                }
+            }
+        }
+        return new DefaultReportGeneratorSPI().getReportGenerator(name); // can be null
+    }
+
+    protected DataRoot readDataRootFile(String filename, boolean readScales, String[] include, String[] exclude, String[] modif) throws FileFormatException {
+        DataRoot file_image = null;
+        ClassSignatureFilter acceptor = new ClassSignatureFilter(include, exclude, modif);
+        file_image = Reader.readXML(filename, readScales, acceptor);
+        return file_image;
+    }
+
+    /**
+     * Legacy CMD line entry poiny (use 'java -jar jcov.jar Merger' from cmd
+     * instead of 'java -cp jcov.jar com.sun.tdk.jcov.Merger')
+     *
+     * @param args
+     */
+    public static void main(String args[]) {
+        RepGen tool = new RepGen();
+        try {
+            int res = tool.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+
+    protected String usageString() {
+        return "java com.sun.tdk.jcov.RepGen [options] filename";
+    }
+
+    protected String exampleString() {
+        return "java -cp jcov.jar com.sun.tdk.jcov.RepGen -include java.lang.* -format html -output out result.xml";
+    }
+
+    protected String getDescr() {
+        return "generates text or HTML (or custom) reports";
+    }
+
+    private MemberFilter createCustomFilter(String spiName) {
+        return FilterFactory.
+                getInstance(spiName).getMemberFilter();
+
+    }
+
+    private MemberFilter initCustomFilter(String filter, String sig) {
+        MemberFilter customPlugin = null;
+        MemberFilter sigFilter = null;
+
+        if (filter != null) {
+            customPlugin = createCustomFilter(filter);
+        }
+        if (customPlugin == null && sigFilter == null) {
+            return null;
+        } else if (customPlugin != null && sigFilter != null) {
+            ConveyerFilter f = new ConveyerFilter();
+            f.add(sigFilter);
+            f.add(customPlugin);
+            return f;
+        } else {
+            return sigFilter != null ? sigFilter : customPlugin;
+        }
+
+    }
+
+    public void setReportGeneratorSPIs(ReportGeneratorSPI reportGeneratorSPI[]) {
+        this.reportGeneratorSPIs = reportGeneratorSPI;
+    }
+
+    public ReportGeneratorSPI[] getReportGeneratorSPIs() {
+        return reportGeneratorSPIs;
+    }
+
+    public String getFilter() {
+        return filter;
+    }
+
+    public void setFilter(String filter) {
+        this.filter = filter;
+    }
+
+    public String[] getFms() {
+        return fms;
+    }
+
+    public void setFms(String[] fms) {
+        this.fms = fms;
+    }
+
+    public String[] getInclude() {
+        return include;
+    }
+
+    public void setInclude(String[] include) {
+        this.include = include;
+    }
+
+    public boolean isIsPublicAPI() {
+        return isPublicAPI;
+    }
+
+    public void setIsPublicAPI(boolean isPublicAPI) {
+        this.isPublicAPI = isPublicAPI;
+    }
+
+    public boolean isNoAbstract() {
+        return noAbstract;
+    }
+
+    public void setNoAbstract(boolean noAbstract) {
+        this.noAbstract = noAbstract;
+    }
+
+    public boolean isSyntheticOn() {
+        return syntheticOn;
+    }
+
+    public void setNoANC(boolean syntheticOn) {
+        this.syntheticOn = syntheticOn;
+    }
+
+    public boolean isWithTestsInfo() {
+        return withTestsInfo;
+    }
+
+    public void setWithTestsInfo(boolean withTestsInfo) {
+        this.withTestsInfo = withTestsInfo;
+    }
+
+    public boolean isNoEnums() {
+        return noEnums;
+    }
+
+    public void setNoEnums(boolean noEnums) {
+        this.noEnums = noEnums;
+    }
+
+    public boolean isShowFields() {
+        return showFields;
+    }
+
+    public void setShowFields(boolean showFields) {
+//        this.showFields = showFields;
+    }
+
+    public String[] getExclude() {
+        return exclude;
+    }
+
+    public void setExclude(String[] exclude) {
+        this.exclude = exclude;
+    }
+
+    public String getSrcRootPath() {
+        return srcRootPath;
+    }
+
+    /**
+     * Reset all properties to defaults. reportGeneratorSPI = null; include =
+     * new String[] {".*"}; exclude = new String[] {""}; fms = null; filter =
+     * null; generateShortFormat = false; noAbstract = false; isPublicAPI =
+     * false; showMethods = true; showBlocks = true; showBranches = true;
+     * showLines = true;
+     */
+    public void resetDefaults() {
+        try {
+            handleEnv_(defineHandler());
+            reportGeneratorSPIs = null;
+            //setFilters(new String[]{".*"}, new String[]{""}, null);
+            setNoAbstract(false);
+            setIsPublicAPI(false);
+        } catch (EnvHandlingException ex) {
+            // should not happen
+        }
+    }
+
+    /**
+     * Set all properties (except custorm report service provider)
+     *
+     * @param include patterns for including data. Set null for default value -
+     * {".*"} (include all)
+     * @param exclude patterns for excluding data. Set null for default value -
+     * {""} (exclude nothing)
+     * @param classModifiers modifiers that should have a class to be included
+     * @param filter custom filter classname
+     * @param generateShortFormat should generate short format
+     * @param publicAPI should generate only public API (public and protected)
+     * @param hideAbstract should hide abstract data
+     * @param hideMethods should hide methods
+     * @param hideBlocks should hide blocks
+     * @param hideBranches should hide branches
+     * @param hideLines should hide lines
+     */
+    public void configure(String[] include, String[] exclude, String[] classModifiers,
+            String filter, boolean generateShortFormat, boolean publicAPI, boolean hideAbstract,
+            boolean hideMethods, boolean hideBlocks, boolean hideBranches, boolean hideLines,
+            boolean hideFields) {
+        setFilters(include, exclude, classModifiers);
+        setFilter(filter);
+        this.isPublicAPI = publicAPI;
+//        this.showFields = !hideFields;
+    }
+
+    /**
+     * Set filtering properties for the report
+     *
+     * @param include patterns for including data. Set null for default value -
+     * {".*"} (include all)
+     * @param exclude patterns for excluding data. Set null for default value -
+     * {""} (exclude nothing)
+     * @param classModifiers modifiers that should have a class to be included
+     */
+    public void setFilters(String[] include, String[] exclude, String[] classModifiers) {
+        if (include == null) {
+            include = new String[]{".*"};
+        }
+        this.include = include;
+        if (exclude == null) {
+            exclude = new String[]{""};
+        }
+        this.exclude = exclude;
+        this.fms = classModifiers;
+    }
+
+    @Override
+    protected int run() throws Exception {
+        Result r;
+        boolean srcZipped = false;
+        if (srcRootPath != null) {
+            File srcRootPathFile = new File(srcRootPath);
+
+            if (srcRootPathFile.exists() && srcRootPathFile.isFile() && (srcRootPath.endsWith(".zip") || srcRootPath.endsWith(".jar"))) {
+                srcZipped = true;
+                srcRootPath = outputDir + File.separator + srcRootPathFile.getName().replace(".zip", "").replace(".jar", "");
+                Utils.unzipFolder(srcRootPathFile, srcRootPath);
+            }
+        }
+
+        try {
+
+            logger.log(Level.INFO, "-- Reading test list");
+            if (filenames.length == 1) {
+                r = new Result(filenames[0], testlist);
+            } else {
+                Merger merger = new Merger();
+                Result[] results = Merger.initResults(filenames, true);
+                Merger.Merge merge = new Merger.Merge(results, null);
+                merger.setAddMissing(true);
+                merger.setRead_scales(true);
+                merger.merge(merge, outputDir, true);
+
+                ReportGenerator rg;
+                if (name != null) {
+                    rg = findReportGenerator(name);
+                } else {
+                    rg = getDefaultReportGenerator();
+                }
+                if (rg == null) {
+                    throw new Exception("Specified ReportGenerator name (" + name + ") was not found");
+                }
+                rg.init(outputDir);
+                String[] tl = testlist != null ? Utils.readLines(testlist) : merge.getResultTestList();
+                SmartTestService sts = new SmartTestService(tl);
+                ReportGenerator.Options options = new ReportGenerator.Options(srcRootPath, sts, null, true, true);
+                try {
+                    ProductCoverage coverage = new ProductCoverage(merge.getResult(), options.getSrcRootPaths(), null, isPublicAPI, noAbstract);
+                    rg.generateReport(coverage, options);
+
+                    if (srcZipped) {
+                        Utils.deleteDirectory(new File(srcRootPath));
+                    }
+
+                } catch (Throwable ex) {
+                    if (ex.getMessage() != null) {
+                        throw new Exception("ReportGenerator produced exception " + ex.getMessage(), ex);
+                    } else {
+                        throw new Exception("ReportGenerator produced exception " + ex, ex);
+                    }
+                }
+
+                return 0;
+            }
+        } catch (IOException ex) {
+            logger.log(Level.SEVERE, "Error while reading testlist", ex);
+            return 1;
+        }
+
+        if (classesPath != null) {
+            try {
+                logger.log(Level.INFO, "-- Creating javap report");
+                new JavapRepGen(include, exclude).run(filenames[0], classesPath, outputDir);
+                return 0;
+            } catch (Exception ex) {
+                logger.log(Level.SEVERE, "Error while creating javap report", ex);
+                return 1;
+            }
+        }
+
+        try {
+            generateReport(name, outputDir, r, srcRootPath);
+
+            if (srcZipped) {
+                Utils.deleteDirectory(new File(srcRootPath));
+            }
+
+            return 0;
+        } catch (FileFormatException e) {
+//            logger.log(Level.SEVERE, "malformed jcov file \"{0}", filename);
+            logger.log(Level.SEVERE, e.getMessage(), Arrays.toString(filenames));
+        } catch (ProcessingException ex) {
+        } catch (Exception ex) {
+            logger.log(Level.SEVERE, ex.getMessage(), ex);
+        }
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        EnvHandler envHandler = new EnvHandler(new OptionDescr[]{
+                    DSC_FMT,
+                    DSC_OUTPUT,
+                    //            DSC_STDOUT,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FM,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FM_LIST,
+                    DSC_NO_ABSTRACT,
+                    DSC_SYNTHETIC_ON,
+                    DSC_PUBLIC_API,
+                    DSC_SRC_ROOT,
+                    DSC_VERBOSE,
+                    DSC_FILTER_PLUGIN,
+                    DSC_TEST_LIST,
+                    DSC_ANONYM,
+                    DSC_JAVAP,
+                    DSC_TESTS_INFO,}, this);
+        SPIDescr spiDescr = new SPIDescr(CUSTOM_REPORT_GENERATOR_SPI, ReportGeneratorSPI.class);
+        spiDescr.setDefaultSPI(new DefaultReportGeneratorSPI());
+        envHandler.registerSPI(spiDescr);
+
+        spiDescr = new SPIDescr(DATA_PROCESSOR_SPI, DataProcessorSPI.class);
+        spiDescr.addPreset("none", new StubSpi());
+        spiDescr.setDefaultSPI(new DefaultDataProcessorSPI());
+        envHandler.registerSPI(spiDescr);
+
+        return envHandler;
+    }
+
+    private int handleEnv_(EnvHandler opts) throws EnvHandlingException {
+        if (opts.isSet(DSC_VERBOSE)) {
+//            LoggingFormatter.printStackTrace = true; // by default logger doesn't print stacktrace
+            logger.setLevel(Level.INFO);
+        } else {
+            logger.setLevel(Level.SEVERE);
+        }
+
+        name = opts.getValue(DSC_FMT);
+        outputDir = opts.getValue(DSC_OUTPUT);
+        // no check for output
+
+        filter = opts.getValue(DSC_FILTER_PLUGIN);
+        noAbstract = opts.isSet(DSC_NO_ABSTRACT);
+        isPublicAPI = opts.isSet(DSC_PUBLIC_API);
+        anonym = opts.isSet(DSC_ANONYM);
+        syntheticOn = opts.isSet(DSC_SYNTHETIC_ON);
+
+        include = InstrumentationOptions.handleInclude(opts);
+        exclude = InstrumentationOptions.handleExclude(opts);
+        fms = InstrumentationOptions.handleFM(opts);
+
+        testlist = opts.getValue(DSC_TEST_LIST);
+        Utils.checkFileCanBeNull(testlist, "testlist filename", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_CANREAD, Utils.CheckOptions.FILE_ISFILE);
+
+        withTestsInfo = opts.isSet(DSC_TESTS_INFO);
+
+        srcRootPath = null;
+        if (opts.isSet(DSC_SRC_ROOT)) {
+            srcRootPath = opts.getValue(DSC_SRC_ROOT);
+        }
+
+        ArrayList<ReportGeneratorSPI> reportGenerators = opts.getSPIs(ReportGeneratorSPI.class);
+        if (reportGenerators != null) {
+            reportGeneratorSPIs = reportGenerators.toArray(new ReportGeneratorSPI[reportGenerators.size()]);
+        }
+        ArrayList<DataProcessorSPI> dataProcessors = opts.getSPIs(DataProcessorSPI.class);
+        if (dataProcessors != null) {
+            dataProcessorSPIs = dataProcessors.toArray(new DataProcessorSPI[dataProcessors.size()]);
+        }
+
+        classesPath = opts.getValue(DSC_JAVAP);
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        String[] srcs = opts.getTail();
+        if (srcs == null) {
+            throw new EnvHandlingException("no input files specified");
+        }
+
+        filenames = srcs;
+        Utils.checkFileNotNull(filenames[0], "JCov datafile", Utils.CheckOptions.FILE_EXISTS, Utils.CheckOptions.FILE_CANREAD, Utils.CheckOptions.FILE_ISFILE);
+        /*if (srcs.length > 1) {
+         logger.log(Level.WARNING,"only \"{0}\" will be processed, the rest of the files will be ignored\n" +
+         "\tto generate report for all files, merge them into one using the merger utility", filename);
+         }*/
+
+        return handleEnv_(opts);
+    }
+
+    public static class ANC_FILTER implements MemberFilter {
+
+        @Override
+        public boolean accept(DataClass clz) {
+            return true;
+        }
+
+        @Override
+        public boolean accept(DataClass clz, DataMethod m) {
+
+            boolean ancMethod;
+
+            //Synthetic method (and Bridge method)
+            ancMethod = ((m.getAccess() & Opcodes.ACC_SYNTHETIC) != 0);
+
+            //Enum method
+            ancMethod = ancMethod
+                    || (clz.getSuperName().equals("java/lang/Enum") && (m.getName().equals("valueOf") || m.getName().equals("values")));
+
+            return !ancMethod;
+
+        }
+
+        @Override
+        public boolean accept(DataClass clz, DataField f) {
+            return true;
+        }
+    }
+    final static OptionDescr DSC_FMT =
+            new OptionDescr("format", new String[]{"fmt"},
+            "Report generation output.", VAL_SINGLE,
+            "Specifies the format of the report.\n"
+            + "Use \"text\" for generate text report and \"html\" for generate HTML report\n"
+            + "Text report in one file which contains method/block/branch coverage information.\n"
+            + "HTML report contains coverage information with marked-up sources.\n\n"
+            + "Custom reports can be specified with ReportGeneratorSPI interface.", "html");
+    /**
+     *
+     */
+    public final static OptionDescr DSC_OUTPUT =
+            new OptionDescr("repgen.output", new String[]{"output", "o"}, "", OptionDescr.VAL_SINGLE,
+            "Output directory for generating text and HTML reports.", "report");
+    /**
+     *
+     */
+    public final static OptionDescr DSC_TEST_LIST =
+            new OptionDescr("tests", "Test list", OptionDescr.VAL_SINGLE,
+            "Specify the path to the file containing test list. File should contain a list of tests\n"
+            + "with one name per line.");
+    final static OptionDescr DSC_FILTER_PLUGIN =
+            new OptionDescr("filter", "", OptionDescr.VAL_SINGLE,
+            "Custom filtering plugin class");
+    /**
+     *
+     */
+    public final static OptionDescr DSC_SRC_ROOT =
+            new OptionDescr("sourcepath", new String[]{"source", "src"}, "The source files.", OptionDescr.VAL_SINGLE, "");
+    final static OptionDescr DSC_VERBOSE =
+            new OptionDescr("verbose", "Verbosity.", "Enable verbose mode.");
+    /**
+     *
+     */
+    public final static OptionDescr DSC_NO_ABSTRACT =
+            new OptionDescr("noabstract", "Additional filtering", "Do not count abstract methods");
+    public final static OptionDescr DSC_SYNTHETIC_ON =
+            new OptionDescr("syntheticon", "Additional filtering", "Count coverage for synthetic methods");
+    /**
+     *
+     */
+    public final static OptionDescr DSC_PUBLIC_API =
+            new OptionDescr("publicapi", "", "Count only public and protected members");
+    public final static OptionDescr DSC_ANONYM =
+            new OptionDescr("anonym", "", "include methods from anonymous classes into the report");
+    public final static OptionDescr DSC_JAVAP =
+            new OptionDescr("javap", new String[]{"javap"}, "Path to the class files of the product to use javap", OptionDescr.VAL_SINGLE, "");
+    public final static OptionDescr DSC_TESTS_INFO =
+            new OptionDescr("testsinfo", "Additional information about for specified tests' list", "Show covererage for all tests in test list");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/RepMerge.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.OptionDescr;
+
+/**
+ *
+ * @author Andrey Titov
+ */
+public class RepMerge extends Merger {
+
+    @Override
+    protected int run() throws Exception {
+        System.out.println("Warning, this tool is depricated and would be removed soon");
+        return super.run();
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{DSC_OUTPUT, DSC_NONEW}, this);
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        int res = super.handleEnv(opts);
+
+        if (opts.isSet(DSC_NONEW)) {
+            setAddMissing(false);
+        }
+        setSigmerge(true);
+
+        return res;
+    }
+
+    @Override
+    protected String usageString() {
+        return "java com.sun.tdk.jcov.RepMerge [-output <filename>] [-nonew] <filenames>";
+    }
+
+    @Override
+    protected String exampleString() {
+        return "java -cp jcov.jar com.sun.tdk.jcov.RepMerge -output merged.xml test1.xml test2.xml";
+    }
+
+    @Override
+    protected String getDescr() {
+        return "merges jcov data files at method level not caring of blocks <deprecated>";
+    }
+
+    public static void main(String[] args) {
+        try {
+            System.exit(new RepMerge().run(args));
+        } catch (Exception e) {
+            System.exit(ERROR_EXEC_EXIT_CODE);
+        }
+    }
+    static OptionDescr DSC_NONEW = new OptionDescr("nonew", "", OptionDescr.VAL_NONE, "Don't include any information that doesn't exist in the first merged file.");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/TmplGen.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov;
+
+import com.sun.tdk.jcov.insert.AbstractUniversalInstrumenter;
+import com.sun.tdk.jcov.instrument.ClassMorph;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions.InstrumentationMode;
+import com.sun.tdk.jcov.instrument.InstrumentationParams;
+import com.sun.tdk.jcov.tools.EnvHandler;
+import com.sun.tdk.jcov.tools.JCovCMDTool;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+
+/**
+ * <p> Template generation. </p> <p> JCov Template file should be used to merge
+ * dynamic data and to save static data. Template could be created while
+ * statically instrumenting classes or with TmplGen tool. </p>
+ *
+ * @author Andrey Titov
+ */
+public class TmplGen extends JCovCMDTool {
+
+    private String[] files;
+    private AbstractUniversalInstrumenter instrumenter;
+    private String flushPath;
+    private String template;
+    private String[] include;
+    private String[] exclude;
+    private boolean instrumentAbstract = false;
+    private boolean instrumentNative = true;
+    private boolean instrumentField = false;
+    private boolean instrumentAnonymous = true;
+    private boolean instrumentSynthetic = true;
+    private InstrumentationOptions.InstrumentationMode mode;
+
+    /**
+     * Legacy CMD line entry poiny (use 'java -jar jcov.jar TmplGen' from cmd
+     * instead of 'java -cp jcov.jar com.sun.tdk.jcov.TmplGen')
+     *
+     * @param args
+     */
+    public static void main(String args[]) {
+        TmplGen tool = new TmplGen();
+        try {
+            int res = tool.run(args);
+            System.exit(res);
+        } catch (Exception ex) {
+            System.exit(1);
+        }
+    }
+
+    protected String usageString() {
+        return "java com.sun.tdk.jcov.TmplGen [options] filename";
+    }
+
+    protected String exampleString() {
+        return "java -cp jcov.jar com.sun.tdk.jcov.TmplGen -include java.lang.* -type method -template template.xml classes";
+    }
+
+    protected String getDescr() {
+        return "generates the jcov template.xml";
+    }
+
+    @Override
+    protected int run() throws Exception {
+        try {
+            generateAndSave(files);
+        } catch (IOException ioe) {
+            throw new Exception("Unexpected error during instrumentation", ioe);
+        }
+        return SUCCESS_EXIT_CODE;
+    }
+
+    public void generateAndSave(String[] files) throws IOException {
+        setDefaultInstrumenter();
+        for (String root : files) {
+            instrumenter.instrument(new File(root), null);
+        }
+        instrumenter.finishWork();
+        instrumenter = null;
+    }
+
+    public void generateTemplate(String[] files) throws IOException {
+        if (instrumenter == null) {
+            setDefaultInstrumenter();
+        }
+        for (String root : files) {
+            instrumenter.instrument(new File(root), null);
+        }
+    }
+
+    public void finishWork() {
+        if (instrumenter == null) {
+            throw new IllegalStateException("Instrumenter is not ready");
+        }
+        instrumenter.finishWork();
+        instrumenter = null;
+    }
+
+    public void setDefaultInstrumenter() {
+        if (instrumenter == null) {
+            instrumenter = new AbstractUniversalInstrumenter(true, true) {
+                ClassMorph morph = new ClassMorph(
+                        new InstrumentationParams(instrumentNative, instrumentField, instrumentAbstract, include, exclude, mode)
+                        .setInstrumentAnonymous(instrumentAnonymous)
+                        .setInstrumentSynthetic(instrumentSynthetic), template);
+
+                protected byte[] instrument(byte[] classData, int classLen) throws IOException {
+//                    byte[] res = Arrays.copyOf(classData, classLen);
+                    byte[] res = new byte[classLen];
+                    System.arraycopy(classData, 0, res, 0, classLen);
+                    morph.morph(res, null, flushPath); // jdk1.5 support
+                    return res;
+                }
+
+                public void finishWork() {
+                    morph.saveData(template, InstrumentationOptions.MERGE.MERGE);
+                }
+            };
+        }
+    }
+
+    @Override
+    protected EnvHandler defineHandler() {
+        return new EnvHandler(new OptionDescr[]{
+                    //        DSC_OUTPUT,
+                    DSC_VERBOSE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TEMPLATE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_TYPE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_INCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_EXCLUDE_LIST,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ABSTRACT,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_NATIVE,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_FIELD,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_SYNTHETIC,
+                    com.sun.tdk.jcov.instrument.InstrumentationOptions.DSC_ANONYM,
+                    ClassMorph.DSC_FLUSH_CLASSES,}, this);
+    }
+
+    @Override
+    protected int handleEnv(EnvHandler opts) throws EnvHandlingException {
+        files = opts.getTail();
+        if (files == null) {
+            throw new EnvHandlingException("No input files specified");
+        }
+
+        if (opts.isSet(DSC_VERBOSE)) {
+            Utils.setLoggingLevel(Level.INFO);
+        }
+
+        Utils.addToClasspath(files);
+
+        flushPath = opts.getValue(ClassMorph.DSC_FLUSH_CLASSES);
+        if ("none".equals(flushPath)) {
+            flushPath = null;
+        }
+        String abstractValue = opts.getValue(InstrumentationOptions.DSC_ABSTRACT);
+        if (abstractValue.equals("off")) {
+            instrumentAbstract = false;
+        } else if (abstractValue.equals("on")) {
+            instrumentAbstract = true;
+        } else {
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_ABSTRACT.name + "' parameter value error: expected 'on' or 'off'; found: '" + abstractValue + "'");
+        }
+
+        String nativeValue = opts.getValue(InstrumentationOptions.DSC_NATIVE);
+        if (nativeValue.equals("on")) {
+            instrumentNative = true;
+        } else if (nativeValue.equals("off")) {
+            instrumentNative = false;
+        } else {
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_NATIVE.name + "' parameter value error: expected 'on' or 'off'; found: '" + nativeValue + "'");
+        }
+
+        String fieldValue = opts.getValue(InstrumentationOptions.DSC_FIELD);
+        if (fieldValue.equals("on")) {
+            instrumentField = true;
+        } else if (fieldValue.equals("off")) {
+            instrumentField = false;
+        } else {
+            throw new EnvHandlingException("'" + InstrumentationOptions.DSC_FIELD.name + "' parameter value error: expected 'on' or 'off'; found: '" + fieldValue + "'");
+        }
+
+        String anonym = opts.getValue(InstrumentationOptions.DSC_ANONYM);
+        if (anonym.equals("on")) {
+            instrumentAnonymous = true;
+        } else { // off
+            instrumentAnonymous = false;
+        }
+
+        String synth = opts.getValue(InstrumentationOptions.DSC_SYNTHETIC);
+        if (synth.equals("on")) {
+            instrumentSynthetic = true;
+        } else { // off
+            instrumentSynthetic = false;
+        }
+
+        mode = InstrumentationOptions.InstrumentationMode.fromString(opts.getValue(InstrumentationOptions.DSC_TYPE));
+        template = opts.getValue(InstrumentationOptions.DSC_TEMPLATE);
+        File tmpl = new File(template);
+        if (tmpl.isDirectory()) {
+            throw new EnvHandlingException("'" + template + "' is a directory while expected template filename");
+        }
+        if (tmpl.getParentFile() != null && !tmpl.getParentFile().exists()) {
+            throw new EnvHandlingException("Template parent directory '" + tmpl.getParentFile() + "' doesn't exits");
+        }
+
+        include = InstrumentationOptions.handleInclude(opts);
+        exclude = InstrumentationOptions.handleExclude(opts);
+
+        com.sun.tdk.jcov.runtime.CollectDetect.enableInvokeCounts();
+
+        return SUCCESS_EXIT_CODE;
+    }
+
+    public String[] getExclude() {
+        return exclude;
+    }
+
+    public void setExclude(String[] exclude) {
+        this.exclude = exclude;
+    }
+
+    public String getFlushPath() {
+        return flushPath;
+    }
+
+    public void setFlushPath(String flushPath) {
+        this.flushPath = flushPath;
+    }
+
+    public String[] getInclude() {
+        return include;
+    }
+
+    public void setInclude(String[] include) {
+        this.include = include;
+    }
+
+    public boolean isInstrumentAbstract() {
+        return instrumentAbstract;
+    }
+
+    public void setInstrumentAbstract(boolean instrumentAbstract) {
+        this.instrumentAbstract = instrumentAbstract;
+    }
+
+    public boolean isInstrumentField() {
+        return instrumentField;
+    }
+
+    public void setInstrumentField(boolean instrumentField) {
+        this.instrumentField = instrumentField;
+    }
+
+    public boolean isInstrumentNative() {
+        return instrumentNative;
+    }
+
+    public void setInstrumentNative(boolean instrumentNative) {
+        this.instrumentNative = instrumentNative;
+    }
+
+    public InstrumentationMode getMode() {
+        return mode;
+    }
+
+    public void setMode(InstrumentationMode mode) {
+        this.mode = mode;
+    }
+
+    public String getTemplate() {
+        return template;
+    }
+
+    public void setTemplate(String template) {
+        this.template = template;
+    }
+
+    final static OptionDescr DSC_VERBOSE =
+            new OptionDescr("verbose", "Verbosity", "Enables verbose mode.");
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/ant/AllTasks.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov.ant;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ *
+ * @author Andrey Titov
+ */
+public class AllTasks extends Task {
+
+    @Override
+    public void execute() throws BuildException {
+        getProject().addTaskDefinition("grabber", Grabber.class);
+        getProject().addTaskDefinition("grabber-manager", GrabberManager.class);
+        getProject().addTaskDefinition("instrument", Instrument.class);
+        getProject().addTaskDefinition("merge", Merge.class);
+        getProject().addTaskDefinition("report", Report.class);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/ant/AntableSPI.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov.ant;
+
+/**
+ *
+ * @author Andrey Titov
+ */
+public interface AntableSPI {
+
+    /**
+     * Allows to use this SPI through ANT wrappers
+     *
+     * @param attrName ANT attribute name.
+     * @param attrValue Value of attribute to set.
+     * @return False if this attribute name is not supported. If SPI returns
+     * FALSE here - JCov will try to resolve setter method (setXXX where XXX is
+     * attrName). If neither setter method exists neither handleAttribute()
+     * accepts the attrName - BuildException would thrown.
+     */
+    public boolean handleAttribute(String attrName, String attrValue);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/ant/Grabber.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov.ant;
+
+import com.sun.tdk.jcov.constants.MiscConstants;
+import java.io.File;
+import java.io.IOException;
+import java.net.BindException;
+import java.util.logging.FileHandler;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ *
+ * @author Andrey Titov
+ * @author Alexey Fedorchenko
+ */
+public class Grabber extends Task {
+
+    private int port = MiscConstants.JcovPortNumber;
+    private File template;
+    private File output;
+    private int count = 0;
+    private boolean saveonrecieve = false;
+    private boolean startCommandListener = true;
+    private int commandport = MiscConstants.JcovGrabberCommandPort;
+    private File logFile;
+    private File propFile;
+    private String outTestList;
+    private boolean mergeByTestNames = false;
+
+    @Override
+    public void execute() throws BuildException {
+        if (template == null || !template.exists() || !template.isFile()) {
+            throw new BuildException("Incorrect template: " + template);
+        }
+
+        if (output == null) {
+            output = new File(getProject().getBaseDir(), "result.xml");
+        }
+
+        com.sun.tdk.jcov.Grabber grabber = new com.sun.tdk.jcov.Grabber();
+
+        if (logFile != null) {
+            try {
+                Logger logger = LogManager.getLogManager().getLogger("");
+                Handler[] handlers = logger.getHandlers();
+                for (int i = 0; i < handlers.length; i++) {
+                    logger.removeHandler(handlers[i]);
+                }
+                FileHandler fh = new FileHandler(logFile.getPath());
+                logger.addHandler(fh);
+                logger.setLevel(Level.FINE);
+                LogManager.getLogManager().addLogger(logger);
+
+            } catch (Exception ex) {
+                throw new BuildException(ex);
+            }
+
+        } else {
+            LogManager.getLogManager().getLogger("").setLevel(Level.OFF);
+        }
+
+        try {
+
+            grabber.setPort(port);
+            grabber.setOutputFilename(output.getPath());
+            grabber.setTemplate(template.getPath());
+            grabber.setMaxCount(count);
+            grabber.setSaveOnReceive(saveonrecieve);
+            grabber.setCommandPort(commandport);
+            grabber.setOutTestList(outTestList);
+            grabber.setMergeByTestNames(mergeByTestNames);
+            if (startCommandListener) {
+                grabber.start(true);
+            } else {
+                grabber.start(false);
+            }
+
+            // setting run command after starting the server to use real ports
+            grabber.setRunCommand(String.format("ANT: grabber port: %d, command port: %d, template: %s, output: %s, save on: %s, max connections: %s",
+                    grabber.getServerPort(), grabber.getCommandListenerPort(), template, output, (saveonrecieve ? "recieve" : "exit"),
+                    (count > 0 ? Integer.toString(count) : "unlimited")));
+
+        } catch (BindException ex) {
+            throw new BuildException("Can't bind to specified port", ex);
+        } catch (IOException ex) {
+            throw new BuildException("Can't create server", ex);
+        }
+
+        if (propFile != null) {
+            try {
+                grabber.writePropfile(propFile.getPath());
+            } catch (IOException ex) {
+                log(ex, 2);
+            }
+        }
+
+        log("Server started at port " + port + " listening commands on " + commandport);
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public void setCommandport(int commandport) {
+        this.commandport = commandport;
+    }
+
+    public void setSaveonrecieve(boolean saveonrecieve) {
+        this.saveonrecieve = saveonrecieve;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
+    public void setTemplate(File template) {
+        this.template = template;
+    }
+
+    public void setLogFile(File logFile) {
+        this.logFile = logFile;
+    }
+
+    public void setPropertiesFile(File propFile) {
+        this.propFile = propFile;
+    }
+
+    public void setOutTestList(String outTestList) {
+        this.outTestList = outTestList;
+    }
+
+    public void setMergeByTestNames(boolean mergeByTestNames) {
+        this.mergeByTestNames = mergeByTestNames;
+    }
+
+    public void setStartCommandListener(boolean startCommandListener) {
+        this.startCommandListener = startCommandListener;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/ant/GrabberManager.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov.ant;
+
+import com.sun.tdk.jcov.constants.MiscConstants;
+import com.sun.tdk.jcov.util.Utils;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+
+/**
+ *
+ * @author Andrey Titov
+ * @author Alexey Fedorchenko
+ */
+public class GrabberManager extends Task implements Condition {
+
+    private int port = MiscConstants.JcovGrabberCommandPort;
+    private String host = "localhost";
+    private String command;
+    private File output;
+    private File propertiesFile;
+
+    @Override
+    public void execute() throws BuildException {
+        com.sun.tdk.jcov.GrabberManager ctrl = new com.sun.tdk.jcov.GrabberManager();
+        if (propertiesFile != null) {
+            try {
+                ctrl.initPortFromFile(propertiesFile.getPath());
+            } catch (FileNotFoundException ex) {
+                throw new BuildException("Properties file not found", ex);
+            } catch (IOException ex) {
+                throw new BuildException(ex);
+            }
+        } else {
+            ctrl.setPort(port);
+        }
+        ctrl.setHost(host);
+        if (command != null) {
+            try {
+                if ("save".equalsIgnoreCase(command)) {
+                    ctrl.sendSaveCommand();
+                } else if ("kill".equalsIgnoreCase(command)) {
+                    ctrl.sendKillCommand();
+                } else if ("forcekill".equalsIgnoreCase(command)) {
+                    ctrl.sendForceKillCommand();
+                } else if ("status".equalsIgnoreCase(command)) {
+                    String status = ctrl.sendStatusCommand();
+                    if (output != null) {
+                        String[] split = status.split(";");
+                        Utils.writeLines(output.getPath(), split);
+                    }
+                } else {
+                    throw new BuildException("Command " + command + " is not supported. Only 'save', 'kill' and 'forcekill' are supported.");
+                }
+            } catch (IOException ex) {
+                if ("Connection refused".equals(ex.getMessage())) {
+                    throw new BuildException("Connection refused on " + host + ":" + port);
+                } else {
+                    throw new BuildException(ex);
+                }
+            }
+        }
+    }
+
+    public void setCommand(String command) {
+        this.command = command;
+    }
+
+    public boolean eval() throws BuildException {
+        com.sun.tdk.jcov.GrabberManager ctrl = new com.sun.tdk.jcov.GrabberManager(port, host);
+        boolean working = false;
+        try {
+            String gotstatus = ctrl.sendStatusCommand();
+            String[] split = gotstatus.split(";");
+            working = Boolean.parseBoolean(split[0]);
+        } catch (IOException ex) {
+            if ("Connection refused".equals(ex.getMessage())) {
+                log("Connection refused on " + host + ":" + port);
+                return false;
+            } else {
+                throw new BuildException(ex);
+            }
+        }
+        return working;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
+    public void setPropertiesFile(File propertiesFile) {
+        this.propertiesFile = propertiesFile;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/classes/com/sun/tdk/jcov/ant/Instrument.java	Tue Mar 25 18:12:53 2014 +0400
@@ -0,0 +1,429 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.tdk.jcov.ant;
+
+import com.sun.tdk.jcov.Instr;
+import com.sun.tdk.jcov.instrument.InstrumentationOptions;
+import com.sun.tdk.jcov.runtime.PropertyFinder;
+import com.sun.tdk.jcov.tools.OptionDescr;
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Copy;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.resources.FileResource;
+
+/**
+ *
+ * @author Andrey Titov
+ */
+public class Instrument extends Task {
+
+    private File destdir;
+    private File productDir;
+    private List<BinPath> implantTo = new LinkedList<BinPath>();
+    private List<FileSet> files = new LinkedList<FileSet>();
+    private PatternSet filter;
+    private LinkedList<SavePoint> saveBegin = new LinkedList<SavePoint>();
+    private LinkedList<SavePoint> saveEnd = new LinkedList<SavePoint>();
+    private boolean verbose;
+    private File template;
+    private File outtemplate;
+    private boolean showabstract = false;
+    private boolean shownative = false;
+    private boolean showfields = false;
+    public File propfile = null;
+
+//    private String type; // not used (MERGE now)
+//    private String flushClasses; // not wrapped