/* * Licensed to the University Corporation for Advanced Internet Development, * Inc. (UCAID) under one or more contributor license agreements. See the * NOTICE file distributed with this work for additional information regarding * copyright ownership. The UCAID 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 net.shibboleth.idp.test; import java.io.Closeable; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.concurrent.locks.LockSupport; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.testng.Assert; import org.testng.annotations.Test; /** * Status test. */ public class StatusTest extends BaseIntegrationTest { /** Path to status page. */ public final static String statusPath = "/idp/status"; /** Initial text of status page . */ public final static String STARTS_WITH = "### Operating Environment Information"; @Test(dataProvider = "sauceOnDemandBrowserDataProvider") public void testStatus(@Nullable final BrowserData browserData, @Nonnull final ServerData serverData) throws Exception { startSeleniumClient(browserData); try (JettyServer server = new JettyServer(serverData)) { driver.get(server.getBaseURL() + statusPath); Assert.assertTrue(getPageSource().startsWith(STARTS_WITH)); } } public static class JettyServer implements Closeable { private static final Object lock = new Object(); private static final Object blocker = new Object(); private static JettyInstance instance; // protected by 'lock' private static Set waiters = new HashSet<>(); // protected by 'lock' private final JettyInstance myInstance; // Should always be the same as instance /** * Constructor. * * @param serverData */ public JettyServer(@Nonnull ServerData serverData) { Thread me = Thread.currentThread(); while (true) { synchronized (lock) { waiters.remove(me); // If I added myself below, remove myself now if (instance == null) { // No Jetty Server running Start it up and we are done instance = new JettyInstance(serverData); break; } else if (instance.getServerData().equals(serverData)) { // There's a Jetty doing just what we want. Tell it about us and we are done instance.increment(); break; } else if (instance.isQuiescent()) { // There is a jetty, but its not doing what we want. OTOH noone is using it. // kill it instance.stop(); // Its dead, so note that instance = null; // and start at the top. continue; } // // The current jetty isn't right for us and is busy doing stuff for someone else // tell it to wake us when its done // waiters.add(Thread.currentThread()); } LockSupport.park(blocker); } myInstance = instance; } /** {@inheritDoc} */ @Override public void close() throws IOException { synchronized (lock) { Assert.assertTrue(myInstance == instance); if (myInstance.decrement()) { // // last man using this config // for (Thread t: waiters) { // // Wake everyone who might be waiting. They will look in turn to see if // there is work to do and if so do it. // LockSupport.unpark(t); } } } } } public class ServerData { } public static class JettyInstance { private int count = 0; private Object serverData; /** * Constructor. * * @param serverData */ public JettyInstance(ServerData config) { count ++; //Start a Jetty Server with this parameterization serverData = config; } /** * */ public void stop() { Assert.assertTrue(count > 0); // Stop the server dead and wait for it to die. } /** * @return */ public boolean isQuiescent() { return (count == 0); } /** * @return */ public boolean decrement() { synchronized (this) { // doesnt need the lock since the lock in JettyServer will syncrhonize Assert.assertTrue(count > 0); count--; return count == 0; } } /** * */ public void increment() { synchronized (this) { // doesnt need the lock since the lock in JettyServer will syncrhonize count++; } } /** * @return */ public Object getServerData() { return serverData; } } }