@Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    if (skip) {
      getLog().info("skipping execution");
    } else {
      String[] sourceFiles = getSourceFiles();
      if (sourceFiles.length == 0) {
        getLog().info("no source files found");
      } else {
        final InputStream jsHintSrc = openClasspathResource(jsHintJS);

        JsEngine engine;
        try {
          engine = new JsEngine().browserEnv().eval(jsHintSrc);
        } catch (IOException e) {
          throw new MojoExecutionException("failed to bootstrap JSHint", e);
        }

        NativeObject combinedOpts = readOptionsFromFile(engine);
        NativeObject combinedGlobals = extractGlobals(combinedOpts);

        addOptions(combinedOpts);
        addGlobals(combinedGlobals);

        Function jsHint = (Function) engine.get("JSHINT");

        Multimap<String, JsHintError> errors = HashMultimap.create();

        for (String srcFile : sourceFiles) {
          List<String> source;
          try {
            source = FileUtils.readLines(new File(srcDirectory, srcFile));
          } catch (IOException e) {
            throw new MojoExecutionException("failed to read " + srcFile, e);
          }
          NativeArray array = new NativeArray(source.toArray());
          engine.call(jsHint, array, combinedOpts, combinedGlobals);
          NativeArray nativeErrors = (NativeArray) engine.get(jsHint, "errors");
          for (int i = 0; i < nativeErrors.size(); i++) {
            NativeObject nativeError = (NativeObject) nativeErrors.get(i);
            JsHintError error = new JsHintError(srcFile, nativeError);
            // handling the built-in JsHint error limit
            if (error.getReason().startsWith("Too many errors")) {
              break;
            }
            getLog().error(error.toString());
            errors.put(error.getSource(), error);
          }
        }

        try {
          writeReport(errors);
        } catch (IOException e) {
          throw new MojoExecutionException("failed to write the report", e);
        }

        if (errors.size() > maxErrorsAllowed) {
          throw new MojoFailureException("JSHint violations: " + errors.size() + ". Allowed violations: " + maxErrorsAllowed);
        }

      }
    }
  }