/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2015, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.test.integration.deployment.overlaywithsymboliclinks;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.as.arquillian.api.ContainerResource;
import org.jboss.as.arquillian.container.ManagementClient;
import org.jboss.as.cli.CommandContext;
import org.jboss.as.test.integration.management.util.CLITestUtil;
import org.jboss.as.test.integration.management.util.SimpleServlet;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.shrinkwrap.impl.base.exporter.ExplodedExporterImpl;
import org.jboss.shrinkwrap.impl.base.exporter.zip.ZipExporterImpl;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * @author wangc
 *
 */
@RunWith(Arquillian.class)
@RunAsClient
public class OverlaySymboliclinkTestCase {

    private static File explodedwars_basedir;
    private static File war1;
    private static File war1_exploded;
    private static String DEPLOYMENT = "deployment";

    private static String fooTargetDir;
    private static String fooLinkDir;
    private static String BAR = "bar";
    private static String BAR_CONTENT = "this is bar content";
    private static String bazTargetDir;
    private static String bazLinkDir;
    private static String QUX = "qux";
    private static String QUX_CONTENT = "this is qux content";
    @ContainerResource
    private ManagementClient managementClient;

    @ArquillianResource
    URL url;

    private CommandContext ctx;

    @Deployment
    public static Archive<?> getDeployment() {
        JavaArchive ja = ShrinkWrap.create(JavaArchive.class, "dummy.jar");
        ja.addClass(OverlaySymboliclinkTestCase.class);
        return ja;
    }

    @BeforeClass
    public static void before() throws Exception {
        WebArchive war;
        String tempDir = TestSuiteEnvironment.getTmpDir();

        explodedwars_basedir = new File(tempDir + File.separator + "exploded_deployments");
        explodedwars_basedir.mkdirs();

        fooTargetDir = explodedwars_basedir + File.separator + "fooTargetDir";
        fooLinkDir = explodedwars_basedir + File.separator + "fooLinkDir";

        bazTargetDir = fooTargetDir + File.separator + "bazTargetDir";
        bazLinkDir = fooLinkDir + File.separator + "bazLinkDir";

        createTarget(fooTargetDir, BAR, BAR_CONTENT);
        createTarget(bazTargetDir, QUX, QUX_CONTENT);

        createSymlink(fooTargetDir, fooLinkDir);
        createSymlink(bazTargetDir, bazLinkDir);

        // deployment
        war = ShrinkWrap.create(WebArchive.class, DEPLOYMENT + ".war");
        war.addClass(SimpleServlet.class);
        war.addAsWebResource(OverlaySymboliclinkTestCase.class.getPackage(), "index.jsp", "index.jsp");
        war.addAsWebInfResource(
                new StringAsset(
                        "<jboss-web version=\"7.2\"  xmlns:jboss=\"http://www.jboss.com/xml/ns/javaee\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.jboss.com/xml/ns/javaee jboss-web_7_2.xsd\">"
                        + "<overlay>" + fooLinkDir + "</overlay>"
                        + "<symbolic-linking-enabled>true</symbolic-linking-enabled>"
                        + "</jboss-web>"),
                "jboss-web.xml");

        war1 = new File(tempDir + File.separator + war.getName());

        new ZipExporterImpl(war).exportTo(war1, true);
        war1_exploded = new ExplodedExporterImpl(war).exportExploded(explodedwars_basedir);
    }

    @AfterClass
    public static void after() throws Exception {
        war1.delete();
        FileUtils.deleteDirectory(explodedwars_basedir);
    }

    @Before
    public void setUp() throws Exception {
        // skip test if on Windows
        Assume.assumeFalse(SystemUtils.IS_OS_WINDOWS);
        ctx = CLITestUtil.getCommandContext();
        ctx.connectController();
    }

    @After
    public void tearDown() throws Exception {
        if (ctx != null) {
            ctx.handleSafe("undeploy " + war1.getName());
            ctx.terminateSession();
        }
    }

    @Test
    public void testSymboliclinksInOverlayDirectory() throws Exception {
        ctx.handle("/deployment=" + war1_exploded.getName() + ":add(content=[{\"path\"=>\""
                + war1_exploded.getAbsolutePath().replace("\\", "\\\\") + "\",\"archive\"=>false}], enabled=true)");

        DefaultHttpClient httpclient = new DefaultHttpClient();
        HttpGet httpget = new HttpGet(url.toExternalForm() + "/" + DEPLOYMENT);

        // test basic resource in deployment
        HttpResponse response = httpclient.execute(httpget);
        StatusLine statusLine = response.getStatusLine();
        assertEquals(200, statusLine.getStatusCode());
        HttpEntity entity = response.getEntity();
        String responseMessage = EntityUtils.toString(entity);
        assertTrue(responseMessage.contains("Hello World"));

        // test bar.txt in symbolic link resource /deployment/bar.txt
        httpget = new HttpGet(url.toExternalForm() + "/" + DEPLOYMENT + "/" + BAR + ".txt");
        response = httpclient.execute(httpget);
        statusLine = response.getStatusLine();
        assertEquals(200, statusLine.getStatusCode());
        entity = response.getEntity();
        responseMessage = EntityUtils.toString(entity);
        assertEquals(BAR_CONTENT, responseMessage);

        // test qux.txt in symbolic link resource /deployment/bazLinkDir/qux.txt
        httpget = new HttpGet(url.toExternalForm() + "/" + DEPLOYMENT + "/" + "bazLinkDir" + "/" + QUX + ".txt");
        response = httpclient.execute(httpget);
        statusLine = response.getStatusLine();
        assertEquals(200, statusLine.getStatusCode());
        entity = response.getEntity();
        responseMessage = EntityUtils.toString(entity);
        assertEquals(QUX_CONTENT, responseMessage);
    }

    private static void createTarget(String targetDir, String name, String content) {
        File fooTargetDirFile = new File(targetDir);
        try {
            if (!fooTargetDirFile.exists())
                fooTargetDirFile.mkdir();

            File barFile = new File(fooTargetDirFile, name + ".txt");
            if (barFile.exists()) {
                barFile.delete();
            }
            if (barFile.createNewFile()) {
                BufferedWriter writer = new BufferedWriter(new FileWriter(barFile));
                try {
                    writer.append(content);
                    writer.flush();
                } finally {
                    if (writer != null)
                        try {
                            writer.close();
                        } catch (IOException e) {
                        }
                }
            }
        } catch (IOException e) {
            // ignore
        }
    }

    private static void createSymlink(String target, String link) throws IOException, InterruptedException {
        String[] toExecute = new String[] { "ln", "-s", target, link };
        Runtime.getRuntime().exec(toExecute).waitFor();
    }
}