| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF 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.apache.felix.http.itest; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.fail; |
| import static org.ops4j.pax.exam.Constants.START_LEVEL_SYSTEM_BUNDLES; |
| import static org.ops4j.pax.exam.Constants.START_LEVEL_TEST_BUNDLE; |
| import static org.ops4j.pax.exam.CoreOptions.frameworkStartLevel; |
| import static org.ops4j.pax.exam.CoreOptions.junitBundles; |
| import static org.ops4j.pax.exam.CoreOptions.mavenBundle; |
| import static org.ops4j.pax.exam.CoreOptions.options; |
| import static org.ops4j.pax.exam.CoreOptions.systemProperty; |
| import static org.ops4j.pax.exam.CoreOptions.when; |
| |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.HttpURLConnection; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.Collection; |
| import java.util.Dictionary; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Map; |
| import java.util.Scanner; |
| import java.util.concurrent.CountDownLatch; |
| |
| import javax.inject.Inject; |
| import javax.servlet.Filter; |
| import javax.servlet.FilterChain; |
| import javax.servlet.FilterConfig; |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.ServletResponse; |
| import javax.servlet.http.HttpServlet; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.ops4j.pax.exam.Configuration; |
| import org.ops4j.pax.exam.Option; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.cm.ConfigurationException; |
| import org.osgi.service.cm.ManagedService; |
| import org.osgi.service.http.HttpContext; |
| import org.osgi.service.http.HttpService; |
| import org.osgi.service.http.NamespaceException; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| /** |
| * Base class for integration tests. |
| * |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| public abstract class BaseIntegrationTest |
| { |
| protected static class TestFilter implements Filter |
| { |
| private final CountDownLatch m_initLatch; |
| private final CountDownLatch m_destroyLatch; |
| |
| public TestFilter() |
| { |
| this(null, null); |
| } |
| |
| public TestFilter(CountDownLatch initLatch, CountDownLatch destroyLatch) |
| { |
| m_initLatch = initLatch; |
| m_destroyLatch = destroyLatch; |
| } |
| |
| @Override |
| public void destroy() |
| { |
| if (m_destroyLatch != null) |
| { |
| m_destroyLatch.countDown(); |
| } |
| } |
| |
| @Override |
| public final void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException |
| { |
| filter((HttpServletRequest) req, (HttpServletResponse) resp, chain); |
| } |
| |
| @Override |
| public void init(FilterConfig config) throws ServletException |
| { |
| if (m_initLatch != null) |
| { |
| m_initLatch.countDown(); |
| } |
| } |
| |
| protected void filter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws IOException, ServletException |
| { |
| resp.setStatus(HttpServletResponse.SC_OK); |
| } |
| } |
| |
| protected static class TestServlet extends HttpServlet |
| { |
| private static final long serialVersionUID = 1L; |
| |
| private final CountDownLatch m_initLatch; |
| private final CountDownLatch m_destroyLatch; |
| |
| public TestServlet() |
| { |
| this(null, null); |
| } |
| |
| public TestServlet(CountDownLatch initLatch, CountDownLatch destroyLatch) |
| { |
| m_initLatch = initLatch; |
| m_destroyLatch = destroyLatch; |
| } |
| |
| @Override |
| public void destroy() |
| { |
| super.destroy(); |
| if (m_destroyLatch != null) |
| { |
| m_destroyLatch.countDown(); |
| } |
| } |
| |
| @Override |
| public void init() throws ServletException |
| { |
| super.init(); |
| if (m_initLatch != null) |
| { |
| m_initLatch.countDown(); |
| } |
| } |
| |
| @Override |
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException |
| { |
| resp.setStatus(HttpServletResponse.SC_OK); |
| } |
| } |
| |
| protected static final int DEFAULT_TIMEOUT = 10000; |
| |
| private static final String ORG_APACHE_FELIX_HTTP_JETTY = "org.apache.felix.http.jetty"; |
| |
| protected static void assertContent(int expectedRC, String expected, URL url) throws IOException |
| { |
| HttpURLConnection conn = (HttpURLConnection) url.openConnection(); |
| |
| int rc = conn.getResponseCode(); |
| assertEquals("Unexpected response code,", expectedRC, rc); |
| |
| if (rc >= 200 && rc < 500) |
| { |
| InputStream is = null; |
| try |
| { |
| is = conn.getInputStream(); |
| assertEquals(expected, slurpAsString(is)); |
| } |
| finally |
| { |
| close(is); |
| conn.disconnect(); |
| } |
| } |
| else |
| { |
| InputStream is = null; |
| try |
| { |
| is = conn.getErrorStream(); |
| assertEquals(expected, slurpAsString(is)); |
| } |
| finally |
| { |
| close(is); |
| conn.disconnect(); |
| } |
| } |
| } |
| |
| protected static void assertContent(String expected, URL url) throws IOException |
| { |
| assertContent(200, expected, url); |
| } |
| |
| protected static void assertResponseCode(int expected, URL url) throws IOException |
| { |
| HttpURLConnection conn = (HttpURLConnection) url.openConnection(); |
| try |
| { |
| assertEquals(expected, conn.getResponseCode()); |
| } |
| finally |
| { |
| conn.disconnect(); |
| } |
| } |
| |
| protected static void close(Closeable resource) |
| { |
| if (resource != null) |
| { |
| try |
| { |
| resource.close(); |
| } |
| catch (IOException e) |
| { |
| // Ignore... |
| } |
| } |
| } |
| |
| protected static Dictionary<String, ?> createDictionary(Object... entries) |
| { |
| Dictionary<String, Object> props = new Hashtable<>(); |
| for (int i = 0; i < entries.length; i += 2) |
| { |
| String key = (String) entries[i]; |
| Object value = entries[i + 1]; |
| props.put(key, value); |
| } |
| return props; |
| } |
| |
| protected static URL createURL(String path) |
| { |
| if (path == null) |
| { |
| path = ""; |
| } |
| while (path.startsWith("/")) |
| { |
| path = path.substring(1); |
| } |
| int port = Integer.getInteger("org.osgi.service.http.port", 8080); |
| try |
| { |
| return new URL(String.format("http://localhost:%d/%s", port, path)); |
| } |
| catch (MalformedURLException e) |
| { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| protected static String slurpAsString(InputStream is) throws IOException |
| { |
| // See <weblogs.java.net/blog/pat/archive/2004/10/stupid_scanner_1.html> |
| Scanner scanner = new Scanner(is, "UTF-8"); |
| try |
| { |
| scanner.useDelimiter("\\A"); |
| |
| return scanner.hasNext() ? scanner.next() : null; |
| } |
| finally |
| { |
| try |
| { |
| scanner.close(); |
| } |
| catch (Exception e) |
| { |
| // Ignore... |
| } |
| } |
| } |
| |
| @Inject |
| protected volatile BundleContext m_context; |
| |
| @Configuration |
| public Option[] config() |
| { |
| final String localRepo = System.getProperty("maven.repo.local", ""); |
| |
| return options( |
| when( localRepo.length() > 0 ).useOptions( |
| systemProperty("org.ops4j.pax.url.mvn.localRepository").value(localRepo) |
| ), |
| // CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787"), |
| |
| // scavenge sessions every 10 seconds (10 minutes is default in 9.4.x) |
| systemProperty("org.eclipse.jetty.servlet.SessionScavengingInterval").value("10"), |
| mavenBundle("org.slf4j", "slf4j-api", "1.7.5"), |
| mavenBundle("org.slf4j", "jcl-over-slf4j", "1.7.5"), |
| mavenBundle("org.slf4j", "log4j-over-slf4j", "1.7.5"), |
| |
| mavenBundle("org.apache.sling", "org.apache.sling.commons.log", "4.0.0"), |
| mavenBundle("org.apache.sling", "org.apache.sling.commons.logservice", "1.0.2"), |
| |
| mavenBundle("org.apache.geronimo.specs", "geronimo-json_1.0_spec", "1.0-alpha-1").startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| mavenBundle("org.apache.johnzon", "johnzon-core", "1.0.0").startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| |
| mavenBundle("org.apache.felix", "org.apache.felix.configadmin").version("1.8.14").startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| mavenBundle("org.apache.felix", "org.apache.felix.http.servlet-api", System.getProperty("http.servlet.api.version")).startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| mavenBundle("org.apache.felix", ORG_APACHE_FELIX_HTTP_JETTY, System.getProperty("http.jetty.version")).startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| mavenBundle("org.apache.felix", "org.apache.felix.http.whiteboard", "4.0.0").startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| |
| mavenBundle("org.apache.httpcomponents", "httpcore-osgi", "4.4.6").startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| mavenBundle("org.apache.httpcomponents", "httpclient-osgi", "4.5.3").startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| mavenBundle("org.mockito", "mockito-all", "1.10.19").startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| mavenBundle("org.objenesis", "objenesis", "2.6").startLevel(START_LEVEL_SYSTEM_BUNDLES), |
| |
| junitBundles(), |
| frameworkStartLevel(START_LEVEL_TEST_BUNDLE)); |
| } |
| |
| private final Map<String, ServiceTracker<?, ?>> trackers = new HashMap<>(); |
| |
| @Before |
| public void setUp() throws Exception |
| { |
| assertNotNull("No bundle context?!", m_context); |
| } |
| |
| @After |
| public void tearDown() throws Exception |
| { |
| synchronized ( trackers ) |
| { |
| for(final Map.Entry<String, ServiceTracker<?, ?>> entry : trackers.entrySet()) |
| { |
| entry.getValue().close(); |
| } |
| trackers.clear(); |
| } |
| Bundle bundle = getHttpJettyBundle(); |
| // Restart the HTTP-service to clean all registrations... |
| if (bundle.getState() == Bundle.ACTIVE) |
| { |
| bundle.stop(); |
| bundle.start(); |
| } |
| } |
| |
| /** |
| * Waits for a service to become available in certain time interval. |
| * @param serviceName |
| * @return |
| * @throws Exception |
| */ |
| protected <T> T awaitService(String serviceName) throws Exception |
| { |
| ServiceTracker<T, T> tracker = null; |
| tracker = getTracker(serviceName); |
| return tracker.waitForService(DEFAULT_TIMEOUT); |
| } |
| |
| /** |
| * Return an array of {@code ServiceReference}s for all services for the |
| * given serviceName |
| * @param serviceName |
| * @return Array of {@code ServiceReference}s or {@code null} if no services |
| * are being tracked. |
| */ |
| protected <T> ServiceReference<T>[] getServiceReferences(String serviceName) |
| { |
| ServiceTracker<T, T> tracker = getTracker(serviceName); |
| return tracker.getServiceReferences(); |
| } |
| |
| private <T> ServiceTracker<T, T> getTracker(String serviceName) |
| { |
| synchronized ( this.trackers ) |
| { |
| ServiceTracker<?, ?> tracker = trackers.get(serviceName); |
| if ( tracker == null ) |
| { |
| tracker = new ServiceTracker<T, T>(m_context, serviceName, null); |
| trackers.put(serviceName, tracker); |
| tracker.open(); |
| } |
| return (ServiceTracker<T, T>) tracker; |
| } |
| } |
| |
| protected void configureHttpService(Dictionary<String, ?> props) throws Exception |
| { |
| final String pid = "org.apache.felix.http"; |
| |
| final Collection<ServiceReference<ManagedService>> serviceRefs = m_context.getServiceReferences(ManagedService.class, String.format("(%s=%s)", Constants.SERVICE_PID, pid)); |
| assertNotNull("Unable to obtain managed configuration for " + pid, serviceRefs); |
| assertFalse("Unable to obtain managed configuration for " + pid, serviceRefs.isEmpty()); |
| |
| for (final ServiceReference<ManagedService> serviceRef : serviceRefs) |
| { |
| ManagedService service = m_context.getService(serviceRef); |
| try |
| { |
| service.updated(props); |
| } |
| catch (ConfigurationException ex) |
| { |
| fail("Invalid configuration provisioned: " + ex.getMessage()); |
| } |
| finally |
| { |
| m_context.ungetService(serviceRef); |
| } |
| } |
| } |
| |
| /** |
| * @param bsn |
| * @return |
| */ |
| protected Bundle findBundle(String bsn) |
| { |
| for (Bundle bundle : m_context.getBundles()) |
| { |
| if (bsn.equals(bundle.getSymbolicName())) |
| { |
| return bundle; |
| } |
| } |
| return null; |
| } |
| |
| protected Bundle getHttpJettyBundle() |
| { |
| Bundle b = findBundle(ORG_APACHE_FELIX_HTTP_JETTY); |
| assertNotNull("Filestore bundle not found?!", b); |
| return b; |
| } |
| |
| protected HttpService getHttpService() |
| { |
| return getService(HttpService.class.getName()); |
| } |
| |
| /** |
| * Obtains a service without waiting for it to become available. |
| * @param serviceName |
| * @return |
| */ |
| protected <T> T getService(final String serviceName) |
| { |
| ServiceTracker<?, ?> tracker = null; |
| synchronized ( this.trackers ) |
| { |
| tracker = trackers.get(serviceName); |
| if ( tracker == null ) |
| { |
| tracker = new ServiceTracker(m_context, serviceName, null); |
| trackers.put(serviceName, tracker); |
| tracker.open(); |
| } |
| } |
| return (T) tracker.getService(); |
| } |
| |
| protected void register(String alias, Servlet servlet) throws ServletException, NamespaceException |
| { |
| register(alias, servlet, null); |
| } |
| |
| protected void register(String alias, Servlet servlet, HttpContext context) throws ServletException, NamespaceException |
| { |
| getHttpService().registerServlet(alias, servlet, null, context); |
| } |
| |
| protected void register(String alias, String name) throws ServletException, NamespaceException |
| { |
| register(alias, name, null); |
| } |
| |
| protected void register(String alias, String name, HttpContext context) throws ServletException, NamespaceException |
| { |
| getHttpService().registerResources(alias, name, context); |
| } |
| |
| |
| protected void unregister(String alias) throws ServletException, NamespaceException |
| { |
| getHttpService().unregister(alias); |
| } |
| } |