001/*
002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 */
019package org.nuxeo.runtime.test.runner;
020
021import java.io.File;
022import java.io.IOException;
023import java.io.InputStream;
024import java.lang.reflect.Field;
025import java.net.URL;
026
027import org.apache.commons.io.FileUtils;
028import org.nuxeo.runtime.test.WorkingDirectoryConfigurator;
029
030import sun.net.www.http.HttpClient;
031
032/**
033 * Runs an embedded Jetty server with the nuxeo webapp deployed.
034 * <p>
035 * Note that at initialization the feature disables the {@code retryPostProp} property of
036 * {@link sun.net.www.http.HttpClient}, the underlying HTTP client used by {@link com.sun.jersey.api.client.Client}.
037 * <p>
038 * This is to prevent the JDK's default behavior kept for backward compatibility: an unsuccessful HTTP POST request is
039 * automatically resent to the server, unsuccessful in this case meaning the server did not send a valid HTTP response
040 * or an {@code IOException} occurred. Yet in the tests using the Jersey client to make calls to Nuxeo we don't want
041 * this as it can hide errors occurring in the HTTP communication that should prevent an appropriate response from being
042 * sent by the server.
043 */
044@Deploy("org.nuxeo.runtime.jetty")
045@Features(RuntimeFeature.class)
046public class JettyFeature extends SimpleFeature implements WorkingDirectoryConfigurator {
047
048    @Override
049    public void initialize(FeaturesRunner runner) throws Exception {
050        disableSunHttpClientRetryPostProp();
051
052        Jetty jetty = runner.getConfig(Jetty.class);
053        if (jetty == null) {
054            jetty = Defaults.of(Jetty.class);
055        }
056        configureJetty(jetty);
057
058        runner.getFeature(RuntimeFeature.class).getHarness().addWorkingDirectoryConfigurator(this);
059    }
060
061    protected void configureJetty(Jetty jetty) {
062        int p = jetty.port();
063        try {
064            String s = System.getenv("JETTY_PORT");
065            if (s != null) {
066                p = Integer.parseInt(s);
067            }
068        } catch (Exception e) {
069            // do nothing ; the jetty.port
070        }
071        if (p > 0) {
072            System.setProperty("jetty.port", Integer.toString(p));
073        }
074
075        String host = System.getenv("JETTY_HOST");
076        if (host == null) {
077            host = jetty.host();
078        }
079        if (host.length() > 0) {
080            System.setProperty("jetty.host", host);
081        }
082
083        String config = System.getenv("JETTY_CONFIG");
084        if (config == null) {
085            config = jetty.config();
086        }
087        if (config.length() > 0) {
088            System.setProperty("org.nuxeo.jetty.config", config);
089        }
090
091        System.setProperty("org.nuxeo.jetty.propagateNaming", Boolean.toString(jetty.propagateNaming()));
092    }
093
094    @Override
095    public void configure(RuntimeHarness harness, File workingDir) throws IOException {
096        File dest = new File(workingDir, "config");
097        dest.mkdirs();
098
099        dest = new File(workingDir + "/config", "default-web.xml");
100        try (InputStream in = getResource("jetty/default-web.xml").openStream()) {
101            FileUtils.copyInputStreamToFile(in, dest);
102        }
103
104        dest = new File(workingDir + "/config", "jetty.xml");
105        try (InputStream in = getResource("jetty/jetty.xml").openStream()) {
106            FileUtils.copyInputStreamToFile(in, dest);
107        }
108    }
109
110    private static URL getResource(String resource) {
111        // return
112        // Thread.currentThread().getContextClassLoader().getResource(resource);
113        return Jetty.class.getClassLoader().getResource(resource);
114    }
115
116    /**
117     * Prevents the JDK's default behavior of resending an unsuccessful HTTP POST request automatically to the server by
118     * disabling the the {@code retryPostProp} property of {@link sun.net.www.http.HttpClient}.
119     * <p>
120     * This can also be achieved by setting the {@code sun.net.http.retryPost} system property to {@code false}.
121     *
122     * @since 9.3
123     */
124    public static void disableSunHttpClientRetryPostProp()
125            throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
126        Field field = HttpClient.class.getDeclaredField("retryPostProp");
127        field.setAccessible(true);
128        field.setBoolean(null, false);
129    }
130
131}