{"action":"created","comment":{"url":"https://api.github.com/repos/apache/nifi/pulls/comments/51149080","id":51149080,"diff_hunk":"@@ -0,0 +1,585 @@\n+/*\n+ * Licensed to the Apache Software Foundation (ASF) under one or more\n+ * contributor license agreements.  See the NOTICE file distributed with\n+ * this work for additional information regarding copyright ownership.\n+ * The ASF licenses this file to You under the Apache License, Version 2.0\n+ * (the \"License\"); you may not use this file except in compliance with\n+ * the License.  You may obtain a copy of the License at\n+ *\n+ *     http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+package org.apache.nifi.processors.script;\n+\n+import org.apache.commons.lang3.StringUtils;\n+import org.apache.nifi.annotation.behavior.DynamicProperty;\n+import org.apache.nifi.annotation.behavior.TriggerWhenEmpty;\n+import org.apache.nifi.annotation.lifecycle.OnShutdown;\n+import org.apache.nifi.components.AllowableValue;\n+import org.apache.nifi.components.PropertyDescriptor;\n+import org.apache.nifi.components.ValidationContext;\n+import org.apache.nifi.components.ValidationResult;\n+import org.apache.nifi.components.Validator;\n+import org.apache.nifi.expression.AttributeExpression;\n+import org.apache.nifi.logging.ProcessorLog;\n+import org.apache.nifi.annotation.lifecycle.OnScheduled;\n+import org.apache.nifi.annotation.documentation.CapabilityDescription;\n+import org.apache.nifi.annotation.documentation.Tags;\n+import org.apache.nifi.processor.AbstractProcessor;\n+import org.apache.nifi.processor.ProcessContext;\n+import org.apache.nifi.processor.ProcessSession;\n+import org.apache.nifi.processor.Relationship;\n+import org.apache.nifi.processor.exception.ProcessException;\n+import org.apache.nifi.processor.util.StandardValidators;\n+import org.apache.nifi.stream.io.ByteArrayInputStream;\n+import org.apache.nifi.util.file.monitor.LastModifiedMonitor;\n+import org.apache.nifi.util.file.monitor.SynchronousFileWatcher;\n+\n+import javax.script.Bindings;\n+import javax.script.Compilable;\n+import javax.script.CompiledScript;\n+import javax.script.ScriptContext;\n+import javax.script.ScriptEngine;\n+import javax.script.ScriptEngineFactory;\n+import javax.script.ScriptEngineManager;\n+import javax.script.ScriptException;\n+import java.io.BufferedReader;\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.IOException;\n+import java.io.InputStream;\n+import java.io.InputStreamReader;\n+import java.io.Reader;\n+import java.net.MalformedURLException;\n+import java.net.URL;\n+import java.net.URLClassLoader;\n+import java.nio.file.Files;\n+import java.nio.file.Paths;\n+import java.util.ArrayList;\n+import java.util.Collection;\n+import java.util.Collections;\n+import java.util.Comparator;\n+import java.util.HashMap;\n+import java.util.HashSet;\n+import java.util.LinkedList;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.ServiceLoader;\n+import java.util.Set;\n+import java.util.concurrent.ConcurrentHashMap;\n+import java.util.concurrent.Executors;\n+import java.util.concurrent.ScheduledExecutorService;\n+import java.util.concurrent.TimeUnit;\n+import java.util.concurrent.atomic.AtomicBoolean;\n+import java.util.concurrent.locks.Lock;\n+import java.util.concurrent.locks.ReentrantLock;\n+\n+@TriggerWhenEmpty\n+@Tags({\"script\", \"execute\", \"groovy\", \"python\", \"jython\", \"jruby\", \"ruby\", \"javascript\", \"js\", \"lua\", \"luaj\", \"scala\"})\n+@CapabilityDescription(\"Executes a script given the flow file and a process session.  The script is responsible for \"\n+        + \"handling the incoming flow file (transfer to SUCCESS or remove, e.g.) as well as any flow files created by \"\n+        + \"the script. If the handling is incomplete or incorrect, the session will be rolled back.\")\n+@DynamicProperty(\n+        name = \"A script engine property to update\",\n+        value = \"The value to set it to\",\n+        supportsExpressionLanguage = true,\n+        description = \"Updates a script engine property specified by the Dynamic Property's key with the value \"\n+                + \"specified by the Dynamic Property's value\")\n+public class ExecuteScript extends AbstractProcessor {\n+\n+    public static final Relationship REL_SUCCESS = new Relationship.Builder()\n+            .name(\"success\")\n+            .description(\"FlowFiles that were successfully processed\")\n+            .build();\n+\n+    public static final Relationship REL_FAILURE = new Relationship.Builder()\n+            .name(\"failure\")\n+            .description(\"FlowFiles that failed to be processed\")\n+            .build();\n+\n+    public static PropertyDescriptor SCRIPT_ENGINE;\n+\n+    public static final PropertyDescriptor SCRIPT_FILE = new PropertyDescriptor.Builder()\n+            .name(\"Script File\")\n+            .required(false)\n+            .description(\"Path to script file to execute. Only one of Script File or Script Body may be used\")\n+            .addValidator(new StandardValidators.FileExistsValidator(true))\n+            .expressionLanguageSupported(true)\n+            .build();\n+\n+    public static final PropertyDescriptor SCRIPT_BODY = new PropertyDescriptor.Builder()\n+            .name(\"Script Body\")\n+            .required(false)\n+            .description(\"Body of script to execute. Only one of Script File or Script Body may be used\")\n+            .addValidator(Validator.VALID)\n+            .expressionLanguageSupported(true)\n+            .build();\n+\n+    public static final PropertyDescriptor MODULES = new PropertyDescriptor.Builder()\n+            .name(\"Module Directory\")\n+            .description(\"Path to a directory which contains modules required by the script.\")\n+            .required(false)\n+            .expressionLanguageSupported(true)\n+            .addValidator(new StandardValidators.DirectoryExistsValidator(true, false))\n+            .build();\n+\n+    // A map from engine name to a custom configurator for that engine\n+    private final Map<String, ScriptEngineConfigurator> scriptEngineConfiguratorMap = new ConcurrentHashMap<>();\n+\n+    private final AtomicBoolean isInitialized = new AtomicBoolean(false);\n+    private final Lock lock = new ReentrantLock();\n+    private SynchronousFileWatcher scriptWatcher;\n+\n+    private Map<String, ScriptEngineFactory> scriptEngineFactoryMap;\n+    private ScriptEngine scriptEngine;\n+    private String scriptEngineName;\n+    private String scriptPath;\n+    private String scriptBody;\n+    private String modulePath;\n+    private CompiledScript compiledScript;\n+    private final AtomicBoolean scriptNeedsReload = new AtomicBoolean(true);\n+    private ScheduledExecutorService reloadService;\n+    private List<PropertyDescriptor> descriptors;\n+\n+\n+    protected void createResources() {\n+\n+        // Set up script file reloader service. This checks to see if the script file has changed, and if so, marks\n+        // the script file as needing a reload before evaluation\n+        if (reloadService == null) {\n+\n+            reloadService = Executors.newScheduledThreadPool(1);\n+            Thread reloadThread = Executors.defaultThreadFactory().newThread(\n+\n+                    new Runnable() {\n+                        @Override\n+                        public void run() {\n+                            try {\n+                                final boolean hasLock = lock.tryLock();\n+\n+                                // if a property is changing we don't need to reload this iteration\n+                                if (hasLock) {\n+                                    try {\n+                                        if (scriptWatcher != null && scriptWatcher.checkAndReset()) {\n+                                            if (isFile(scriptPath)) {\n+                                                scriptNeedsReload.set(true);\n+                                            }\n+                                        }\n+                                    } finally {\n+                                        lock.unlock();\n+                                    }\n+                                }\n+                            } catch (final Throwable t) {\n+                                final ProcessorLog logger = getLogger();\n+                                final String message = \"Unable to reload configured script Processor: \" + t;\n+\n+                                logger.error(message);\n+                                if (logger.isDebugEnabled()) {\n+                                    logger.error(message, t);\n+                                }\n+                            }\n+                        }\n+                    });\n+            reloadThread.setName(\"[ExecuteScript] Script reloading thread\");\n+            // monitor the script if configured for changes\n+            reloadService.scheduleWithFixedDelay(reloadThread, 30, 10, TimeUnit.SECONDS);\n+        }\n+\n+        descriptors = new ArrayList<>();\n+\n+        // The following is required for JRuby, should be transparent to everything else.\n+        // Note this is not done in a ScriptEngineConfigurator, as it is too early in the lifecycle. The\n+        // setting must be there before the factories/engines are loaded.\n+        System.setProperty(\"org.jruby.embed.localvariable.behavior\", \"persistent\");\n+\n+        // Create list of available engines\n+        ScriptEngineManager scriptEngineManager = new ScriptEngineManager();\n+        List<ScriptEngineFactory> scriptEngineFactories = scriptEngineManager.getEngineFactories();\n+        if (scriptEngineFactories != null) {\n+            scriptEngineFactoryMap = new HashMap<>(scriptEngineFactories.size());\n+            List<AllowableValue> engineList = new LinkedList<>();\n+            for (ScriptEngineFactory factory : scriptEngineFactories) {\n+                engineList.add(new AllowableValue(factory.getLanguageName()));\n+                scriptEngineFactoryMap.put(factory.getLanguageName(), factory);\n+            }\n+\n+            // Sort the list by name so the list always looks the same.\n+            Collections.sort(engineList, new Comparator<AllowableValue>() {\n+                @Override\n+                public int compare(AllowableValue o1, AllowableValue o2) {\n+                    if (o1 == null) {\n+                        return o2 == null ? 0 : 1;\n+                    }\n+                    if (o2 == null) {\n+                        return -1;\n+                    }\n+                    return o1.getValue().compareTo(o2.getValue());\n+                }\n+            });\n+\n+            AllowableValue[] engines = engineList.toArray(new AllowableValue[engineList.size()]);\n+\n+            SCRIPT_ENGINE = new PropertyDescriptor.Builder()\n+                    .name(\"Script Engine\")\n+                    .required(true)\n+                    .description(\"The engine to execute scripts\")\n+                    .allowableValues(engines)\n+                    .defaultValue(engines[0].getValue())\n+                    .required(true)\n+                    .expressionLanguageSupported(false)\n+                    .build();\n+            descriptors.add(SCRIPT_ENGINE);\n+        }\n+\n+        descriptors.add(SCRIPT_FILE);\n+        descriptors.add(SCRIPT_BODY);\n+        descriptors.add(MODULES);\n+\n+        isInitialized.set(true);\n+    }\n+\n+    /**\n+     * Returns the valid relationships for this processor.\n+     *\n+     * @return a Set of Relationships supported by this processor\n+     */\n+    @Override\n+    public Set<Relationship> getRelationships() {\n+        final Set<Relationship> relationships = new HashSet<>();\n+        relationships.add(REL_SUCCESS);\n+        relationships.add(REL_FAILURE);\n+        return Collections.unmodifiableSet(relationships);\n+    }\n+\n+    /**\n+     * Returns a list of property descriptors supported by this processor. The list always includes properties such as\n+     * script engine name, script file name, script body name, script arguments, and an external module path. If the\n+     * scripted processor also defines supported properties, those are added to the list as well.\n+     *\n+     * @return a List of PropertyDescriptor objects supported by this processor\n+     */\n+    @Override\n+    protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {\n+        synchronized (isInitialized) {\n+            if (!isInitialized.get()) {\n+                createResources();\n+            }\n+        }\n+\n+        return Collections.unmodifiableList(descriptors);\n+    }\n+\n+    /**\n+     * Returns a PropertyDescriptor for the given name. This is for the user to be able to define their own properties\n+     * which will be available as variables in the script\n+     *\n+     * @param propertyDescriptorName used to lookup if any property descriptors exist for that name\n+     * @return a PropertyDescriptor object corresponding to the specified dynamic property name\n+     */\n+    @Override\n+    protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {\n+        return new PropertyDescriptor.Builder()\n+                .name(propertyDescriptorName)\n+                .required(false)\n+                .addValidator(StandardValidators.createAttributeExpressionLanguageValidator(AttributeExpression.ResultType.STRING, true))\n+                .addValidator(StandardValidators.ATTRIBUTE_KEY_PROPERTY_NAME_VALIDATOR)\n+                .expressionLanguageSupported(true)\n+                .dynamic(true)\n+                .build();\n+    }\n+\n+    /**\n+     * Determines whether the given path refers to a valid file\n+     *\n+     * @param path a path to a file\n+     * @return true if the path refers to a valid file, false otherwise\n+     */\n+    private boolean isFile(final String path) {\n+        return path != null && Files.isRegularFile(Paths.get(path));\n+    }\n+\n+\n+    /**\n+     * Custom validation for ensuring exactly one of Script File or Script Body is populated\n+     *\n+     * @param validationContext provides a mechanism for obtaining externally\n+     *                          managed values, such as property values and supplies convenience methods\n+     *                          for operating on those values\n+     * @return A collection of validation results\n+     */\n+    @Override\n+    protected Collection<ValidationResult> customValidate(ValidationContext validationContext) {\n+        return super.customValidate(validationContext);","path":"nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/processors/script/ExecuteScript.java","position":317,"original_position":317,"commit_id":"6cdc49254f7f44f000c163ac2691b8f0e9fecb67","original_commit_id":"6cdc49254f7f44f000c163ac2691b8f0e9fecb67","user":{"login":"markap14","id":184268,"avatar_url":"https://avatars.githubusercontent.com/u/184268?v=3","gravatar_id":"","url":"https://api.github.com/users/markap14","html_url":"https://github.com/markap14","followers_url":"https://api.github.com/users/markap14/followers","following_url":"https://api.github.com/users/markap14/following{/other_user}","gists_url":"https://api.github.com/users/markap14/gists{/gist_id}","starred_url":"https://api.github.com/users/markap14/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markap14/subscriptions","organizations_url":"https://api.github.com/users/markap14/orgs","repos_url":"https://api.github.com/users/markap14/repos","events_url":"https://api.github.com/users/markap14/events{/privacy}","received_events_url":"https://api.github.com/users/markap14/received_events","type":"User","site_admin":false},"body":"You added a comment indicating that custom validation will ensure that exactly 1 of the Script File or Script Body is populated but the implementation just delegates to the super class, which returns an empty collection, I believe..","created_at":"2016-01-28T16:47:41Z","updated_at":"2016-01-28T16:47:41Z","html_url":"https://github.com/apache/nifi/pull/185#discussion_r51149080","pull_request_url":"https://api.github.com/repos/apache/nifi/pulls/185","_links":{"self":{"href":"https://api.github.com/repos/apache/nifi/pulls/comments/51149080"},"html":{"href":"https://github.com/apache/nifi/pull/185#discussion_r51149080"},"pull_request":{"href":"https://api.github.com/repos/apache/nifi/pulls/185"}}},"pull_request":{"url":"https://api.github.com/repos/apache/nifi/pulls/185","id":56822526,"html_url":"https://github.com/apache/nifi/pull/185","diff_url":"https://github.com/apache/nifi/pull/185.diff","patch_url":"https://github.com/apache/nifi/pull/185.patch","issue_url":"https://api.github.com/repos/apache/nifi/issues/185","number":185,"state":"open","locked":false,"title":"NIFI-210: Add ExecuteScript and InvokeScriptProcessor","user":{"login":"mattyb149","id":2124747,"avatar_url":"https://avatars.githubusercontent.com/u/2124747?v=3","gravatar_id":"","url":"https://api.github.com/users/mattyb149","html_url":"https://github.com/mattyb149","followers_url":"https://api.github.com/users/mattyb149/followers","following_url":"https://api.github.com/users/mattyb149/following{/other_user}","gists_url":"https://api.github.com/users/mattyb149/gists{/gist_id}","starred_url":"https://api.github.com/users/mattyb149/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattyb149/subscriptions","organizations_url":"https://api.github.com/users/mattyb149/orgs","repos_url":"https://api.github.com/users/mattyb149/repos","events_url":"https://api.github.com/users/mattyb149/events{/privacy}","received_events_url":"https://api.github.com/users/mattyb149/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-01-21T22:56:03Z","updated_at":"2016-01-28T16:47:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c9436af1e5aa62124e2871898d76b36cae89208d","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/apache/nifi/pulls/185/commits","review_comments_url":"https://api.github.com/repos/apache/nifi/pulls/185/comments","review_comment_url":"https://api.github.com/repos/apache/nifi/pulls/comments{/number}","comments_url":"https://api.github.com/repos/apache/nifi/issues/185/comments","statuses_url":"https://api.github.com/repos/apache/nifi/statuses/6cdc49254f7f44f000c163ac2691b8f0e9fecb67","head":{"label":"mattyb149:script-processors","ref":"script-processors","sha":"6cdc49254f7f44f000c163ac2691b8f0e9fecb67","user":{"login":"mattyb149","id":2124747,"avatar_url":"https://avatars.githubusercontent.com/u/2124747?v=3","gravatar_id":"","url":"https://api.github.com/users/mattyb149","html_url":"https://github.com/mattyb149","followers_url":"https://api.github.com/users/mattyb149/followers","following_url":"https://api.github.com/users/mattyb149/following{/other_user}","gists_url":"https://api.github.com/users/mattyb149/gists{/gist_id}","starred_url":"https://api.github.com/users/mattyb149/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattyb149/subscriptions","organizations_url":"https://api.github.com/users/mattyb149/orgs","repos_url":"https://api.github.com/users/mattyb149/repos","events_url":"https://api.github.com/users/mattyb149/events{/privacy}","received_events_url":"https://api.github.com/users/mattyb149/received_events","type":"User","site_admin":false},"repo":{"id":28576559,"name":"nifi","full_name":"mattyb149/nifi","owner":{"login":"mattyb149","id":2124747,"avatar_url":"https://avatars.githubusercontent.com/u/2124747?v=3","gravatar_id":"","url":"https://api.github.com/users/mattyb149","html_url":"https://github.com/mattyb149","followers_url":"https://api.github.com/users/mattyb149/followers","following_url":"https://api.github.com/users/mattyb149/following{/other_user}","gists_url":"https://api.github.com/users/mattyb149/gists{/gist_id}","starred_url":"https://api.github.com/users/mattyb149/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mattyb149/subscriptions","organizations_url":"https://api.github.com/users/mattyb149/orgs","repos_url":"https://api.github.com/users/mattyb149/repos","events_url":"https://api.github.com/users/mattyb149/events{/privacy}","received_events_url":"https://api.github.com/users/mattyb149/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mattyb149/nifi","description":"Mirror of Apache NiFi (Incubating)","fork":true,"url":"https://api.github.com/repos/mattyb149/nifi","forks_url":"https://api.github.com/repos/mattyb149/nifi/forks","keys_url":"https://api.github.com/repos/mattyb149/nifi/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mattyb149/nifi/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mattyb149/nifi/teams","hooks_url":"https://api.github.com/repos/mattyb149/nifi/hooks","issue_events_url":"https://api.github.com/repos/mattyb149/nifi/issues/events{/number}","events_url":"https://api.github.com/repos/mattyb149/nifi/events","assignees_url":"https://api.github.com/repos/mattyb149/nifi/assignees{/user}","branches_url":"https://api.github.com/repos/mattyb149/nifi/branches{/branch}","tags_url":"https://api.github.com/repos/mattyb149/nifi/tags","blobs_url":"https://api.github.com/repos/mattyb149/nifi/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mattyb149/nifi/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mattyb149/nifi/git/refs{/sha}","trees_url":"https://api.github.com/repos/mattyb149/nifi/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mattyb149/nifi/statuses/{sha}","languages_url":"https://api.github.com/repos/mattyb149/nifi/languages","stargazers_url":"https://api.github.com/repos/mattyb149/nifi/stargazers","contributors_url":"https://api.github.com/repos/mattyb149/nifi/contributors","subscribers_url":"https://api.github.com/repos/mattyb149/nifi/subscribers","subscription_url":"https://api.github.com/repos/mattyb149/nifi/subscription","commits_url":"https://api.github.com/repos/mattyb149/nifi/commits{/sha}","git_commits_url":"https://api.github.com/repos/mattyb149/nifi/git/commits{/sha}","comments_url":"https://api.github.com/repos/mattyb149/nifi/comments{/number}","issue_comment_url":"https://api.github.com/repos/mattyb149/nifi/issues/comments{/number}","contents_url":"https://api.github.com/repos/mattyb149/nifi/contents/{+path}","compare_url":"https://api.github.com/repos/mattyb149/nifi/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mattyb149/nifi/merges","archive_url":"https://api.github.com/repos/mattyb149/nifi/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mattyb149/nifi/downloads","issues_url":"https://api.github.com/repos/mattyb149/nifi/issues{/number}","pulls_url":"https://api.github.com/repos/mattyb149/nifi/pulls{/number}","milestones_url":"https://api.github.com/repos/mattyb149/nifi/milestones{/number}","notifications_url":"https://api.github.com/repos/mattyb149/nifi/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mattyb149/nifi/labels{/name}","releases_url":"https://api.github.com/repos/mattyb149/nifi/releases{/id}","deployments_url":"https://api.github.com/repos/mattyb149/nifi/deployments","created_at":"2014-12-29T01:48:08Z","updated_at":"2016-01-12T16:43:50Z","pushed_at":"2016-01-27T20:34:33Z","git_url":"git://github.com/mattyb149/nifi.git","ssh_url":"git@github.com:mattyb149/nifi.git","clone_url":"https://github.com/mattyb149/nifi.git","svn_url":"https://github.com/mattyb149/nifi","homepage":null,"size":36185,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"apache:master","ref":"master","sha":"c5a174ae6de9e18b53d9077300781def17c129f0","user":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"repo":{"id":27911088,"name":"nifi","full_name":"apache/nifi","owner":{"login":"apache","id":47359,"avatar_url":"https://avatars.githubusercontent.com/u/47359?v=3","gravatar_id":"","url":"https://api.github.com/users/apache","html_url":"https://github.com/apache","followers_url":"https://api.github.com/users/apache/followers","following_url":"https://api.github.com/users/apache/following{/other_user}","gists_url":"https://api.github.com/users/apache/gists{/gist_id}","starred_url":"https://api.github.com/users/apache/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/apache/subscriptions","organizations_url":"https://api.github.com/users/apache/orgs","repos_url":"https://api.github.com/users/apache/repos","events_url":"https://api.github.com/users/apache/events{/privacy}","received_events_url":"https://api.github.com/users/apache/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/apache/nifi","description":"Mirror of Apache NiFi","fork":false,"url":"https://api.github.com/repos/apache/nifi","forks_url":"https://api.github.com/repos/apache/nifi/forks","keys_url":"https://api.github.com/repos/apache/nifi/keys{/key_id}","collaborators_url":"https://api.github.com/repos/apache/nifi/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/apache/nifi/teams","hooks_url":"https://api.github.com/repos/apache/nifi/hooks","issue_events_url":"https://api.github.com/repos/apache/nifi/issues/events{/number}","events_url":"https://api.github.com/repos/apache/nifi/events","assignees_url":"https://api.github.com/repos/apache/nifi/assignees{/user}","branches_url":"https://api.github.com/repos/apache/nifi/branches{/branch}","tags_url":"https://api.github.com/repos/apache/nifi/tags","blobs_url":"https://api.github.com/repos/apache/nifi/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/apache/nifi/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/apache/nifi/git/refs{/sha}","trees_url":"https://api.github.com/repos/apache/nifi/git/trees{/sha}","statuses_url":"https://api.github.com/repos/apache/nifi/statuses/{sha}","languages_url":"https://api.github.com/repos/apache/nifi/languages","stargazers_url":"https://api.github.com/repos/apache/nifi/stargazers","contributors_url":"https://api.github.com/repos/apache/nifi/contributors","subscribers_url":"https://api.github.com/repos/apache/nifi/subscribers","subscription_url":"https://api.github.com/repos/apache/nifi/subscription","commits_url":"https://api.github.com/repos/apache/nifi/commits{/sha}","git_commits_url":"https://api.github.com/repos/apache/nifi/git/commits{/sha}","comments_url":"https://api.github.com/repos/apache/nifi/comments{/number}","issue_comment_url":"https://api.github.com/repos/apache/nifi/issues/comments{/number}","contents_url":"https://api.github.com/repos/apache/nifi/contents/{+path}","compare_url":"https://api.github.com/repos/apache/nifi/compare/{base}...{head}","merges_url":"https://api.github.com/repos/apache/nifi/merges","archive_url":"https://api.github.com/repos/apache/nifi/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/apache/nifi/downloads","issues_url":"https://api.github.com/repos/apache/nifi/issues{/number}","pulls_url":"https://api.github.com/repos/apache/nifi/pulls{/number}","milestones_url":"https://api.github.com/repos/apache/nifi/milestones{/number}","notifications_url":"https://api.github.com/repos/apache/nifi/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/apache/nifi/labels{/name}","releases_url":"https://api.github.com/repos/apache/nifi/releases{/id}","deployments_url":"https://api.github.com/repos/apache/nifi/deployments","created_at":"2014-12-12T08:00:05Z","updated_at":"2016-01-27T17:03:27Z","pushed_at":"2016-01-28T14:35:28Z","git_url":"git://github.com/apache/nifi.git","ssh_url":"git@github.com:apache/nifi.git","clone_url":"https://github.com/apache/nifi.git","svn_url":"https://github.com/apache/nifi","homepage":"","size":41700,"stargazers_count":201,"watchers_count":201,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":170,"mirror_url":"git://git.apache.org/nifi.git","open_issues_count":12,"forks":170,"open_issues":12,"watchers":201,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/apache/nifi/pulls/185"},"html":{"href":"https://github.com/apache/nifi/pull/185"},"issue":{"href":"https://api.github.com/repos/apache/nifi/issues/185"},"comments":{"href":"https://api.github.com/repos/apache/nifi/issues/185/comments"},"review_comments":{"href":"https://api.github.com/repos/apache/nifi/pulls/185/comments"},"review_comment":{"href":"https://api.github.com/repos/apache/nifi/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/apache/nifi/pulls/185/commits"},"statuses":{"href":"https://api.github.com/repos/apache/nifi/statuses/6cdc49254f7f44f000c163ac2691b8f0e9fecb67"}}}}