/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch licenses this file to you under
 * the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.elasticsearch.plugins;

import com.google.common.io.Files;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.FileSystemUtils;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;

// NB: the tests uses System Properties to pass the information from different plugins (loaded in separate CLs) to the test.
// hence the use of try/finally blocks to clean these up after the test has been executed as otherwise the test framework will trigger a failure
@ClusterScope(scope = Scope.TEST, numNodes = 0)
public class IsolatedPluginTests extends ElasticsearchIntegrationTest {

    private static final Settings SETTINGS;
    private static final File PLUGIN_DIR;
    private Properties props;

    static {
        PLUGIN_DIR = Files.createTempDir();

        SETTINGS = ImmutableSettings.settingsBuilder()
                .put("discovery.zen.ping.multicast.enabled", false)
                .put("force.http.enabled", true)
                .put("plugin.isolation", true)
                .put("path.plugins", PLUGIN_DIR.getAbsolutePath())
                .build();
    }

    @Before
    public void beforeTest() throws Exception {
        props = new Properties();
        props.putAll(System.getProperties());

        logger.info("Installing plugins into folder {}", PLUGIN_DIR);
        FileSystemUtils.mkdirs(PLUGIN_DIR);

        // copy plugin
        copyPlugin(new File(PLUGIN_DIR, "plugin-v1"));
        copyPlugin(new File(PLUGIN_DIR, "plugin-v2"));
    }

    private void copyPlugin(File p1) throws IOException {
        FileSystemUtils.mkdirs(p1);
        // copy plugin
        File dir = new File(p1, "org/elasticsearch/plugins/isolation/");
        FileSystemUtils.mkdirs(dir);
        copyFile(getClass().getResourceAsStream("isolation/DummyClass.class"), new File(dir, "DummyClass.class"));
        copyFile(getClass().getResourceAsStream("isolation/IsolatedPlugin.class"), new File(dir, "IsolatedPlugin.class"));
        copyFile(getClass().getResourceAsStream("isolation/es-plugin.properties"), new File(p1, "es-plugin.properties"));
    }

    private void copyFile(InputStream source, File out) throws IOException {
        out.createNewFile();
        Streams.copy(source, new FileOutputStream(out));
    }

    @After
    public void afterTest() throws Exception {
        FileSystemUtils.deleteRecursively(PLUGIN_DIR);
    }

    protected Settings nodeSettings(int nodeOrdinal) {
        return SETTINGS;
    }

    @Test
    public void testPluginNumberOfIsolatedInstances() throws Exception {
        try {
            NodesInfoResponse nodesInfoResponse = client().admin().cluster().prepareNodesInfo().clear().setPlugins(true).get();
            assertThat(nodesInfoResponse.getNodes().length, equalTo(1));
            assertThat(nodesInfoResponse.getNodes()[0].getPlugins().getInfos(), notNullValue());
            assertThat(nodesInfoResponse.getNodes()[0].getPlugins().getInfos().size(), equalTo(2));
        } finally {
            System.setProperties(props);
        }
    }

    @Test
    public void testIsolatedPluginProperties() throws Exception {
        try {
            client();
            Properties p = System.getProperties();
            assertThat(p.getProperty("es.test.isolated.plugin.count"), equalTo("2"));
            String prop = p.getProperty("es.test.isolated.plugin.instantiated.hashes");
            String[] hashes = Strings.delimitedListToStringArray(prop, " ");
            // 2 plugins plus trailing space
            assertThat(hashes.length, equalTo(3));
            assertThat(p.getProperty("es.test.isolated.plugin.instantiated"), equalTo(hashes[1]));
        } finally {
            System.setProperties(props);
        }
    }
}
@@ -1550,32 +1550,57 @@
 io.InputStream;%0A
+import java.util.Arrays;%0A
 import java.util
@@ -1715,16 +1715,74 @@
 llValue;
+%0Aimport static org.hamcrest.Matchers.greaterThanOrEqualTo;
 %0A%0A// NB:
@@ -2541,16 +2541,17 @@
 (%22plugin
+s
 .isolati
@@ -4707,173 +4707,381 @@
     
-client();%0A            Properties p = System.getProperties();%0A            assertThat(p.getProperty(%22es.test.isolated.plugin.count%22), equalTo(%222%22));%0A            String
+String prop = %22es.test.isolated.plugin.count%22;%0A            int count = Integer.getInteger(prop, 0);%0A            client();%0A            // do a %3E= comparison in case there are multiple tests running in the same JVM (build server)%0A            assertThat(Integer.getInteger(prop), greaterThanOrEqualTo(count + 2));%0A            Properties p = System.getProperties();%0A           
  pro
@@ -5310,44 +5310,93 @@
 th, 
-equalTo(3));%0A            assertThat(
+greaterThanOrEqualTo(count + 2));%0A            assertThat(Arrays.binarySearch(hashes, 
 p.ge
@@ -5448,27 +5448,33 @@
 ed%22)
+)
 , 
-equalTo(hashes%5B1%5D
+greaterThanOrEqualTo(0
 ));%0A

make test assertions relative in IsolatedPluginTests