Java tutorial
/* * 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.qpid.server.security.acl; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.lang.StringUtils; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQConnectionURL; import org.apache.qpid.jms.ConnectionListener; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.model.Broker; import org.apache.qpid.test.utils.QpidBrokerTestCase; import org.apache.qpid.url.URLSyntaxException; import javax.jms.Connection; import javax.jms.ExceptionListener; import javax.jms.JMSException; import javax.naming.NamingException; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Abstract test case for ACLs. * * This base class contains convenience methods to manage ACL files and implements a mechanism that allows each * test method to run its own setup code before the broker starts. * * TODO move the pre broker-startup setup method invocation code to {@link QpidBrokerTestCase} * * @see ExternalACLTest * @see ExternalACLJMXTest * @see ExhaustiveACLTest */ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements ConnectionListener { /** Used to synchronise {@link #tearDown()} when exceptions are thrown */ protected CountDownLatch _exceptionReceived; @Override public void setUp() throws Exception { getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, System.getProperty(QPID_HOME) + "/etc/groups-systests"); // run test specific setup String testSetup = StringUtils.replace(getName(), "test", "setUp"); try { Method setup = getClass().getDeclaredMethod(testSetup); setup.invoke(this); } catch (NoSuchMethodException e) { // Ignore } catch (InvocationTargetException e) { throw (Exception) e.getTargetException(); } super.setUp(); } @Override public void tearDown() throws Exception { try { super.tearDown(); } catch (JMSException e) { //we're throwing this away as it can happen in this test as the state manager remembers exceptions //that we provoked with authentication failures, where the test passes - we can ignore on con close } } public void writeACLFile(final String vhost, final String... rules) throws ConfigurationException, IOException { writeACLFileUtil(this, vhost, rules); } public static void writeACLFileUtil(QpidBrokerTestCase testcase, String vhost, String... rules) throws ConfigurationException, IOException { File aclFile = File.createTempFile(testcase.getClass().getSimpleName(), testcase.getName()); aclFile.deleteOnExit(); if (vhost == null) { testcase.getBrokerConfiguration().setBrokerAttribute(Broker.ACL_FILE, aclFile.getAbsolutePath()); } else { testcase.setVirtualHostConfigurationProperty("virtualhosts.virtualhost." + vhost + ".security.acl", aclFile.getAbsolutePath()); } PrintWriter out = new PrintWriter(new FileWriter(aclFile)); out.println(String.format("# %s", testcase.getName())); for (String line : rules) { out.println(line); } out.close(); } /** * Creates a connection to the broker, and sets a connection listener to prevent failover and an exception listener * with a {@link CountDownLatch} to synchronise in the {@link #check403Exception(Throwable)} method and allow the * {@link #tearDown()} method to complete properly. */ public Connection getConnection(String vhost, String username, String password) throws NamingException, JMSException, URLSyntaxException { AMQConnection connection = (AMQConnection) getConnection(createConnectionURL(vhost, username, password)); //Prevent Failover connection.setConnectionListener(this); //QPID-2081: use a latch to sync on exception causing connection close, to work //around the connection close race during tearDown() causing sporadic failures _exceptionReceived = new CountDownLatch(1); connection.setExceptionListener(new ExceptionListener() { public void onException(JMSException e) { _exceptionReceived.countDown(); } }); return (Connection) connection; } // Connection Listener Interface - Used here to block failover public void bytesSent(long count) { } public void bytesReceived(long count) { } public boolean preFailover(boolean redirect) { //Prevent failover. return false; } public boolean preResubscribe() { return false; } public void failoverComplete() { } /** * Convenience method to build an {@link AMQConnectionURL} with the right parameters. */ public AMQConnectionURL createConnectionURL(String vhost, String username, String password) throws URLSyntaxException { String url = "amqp://" + username + ":" + password + "@clientid/" + vhost + "?brokerlist='" + getBroker() + "?retries='0''"; return new AMQConnectionURL(url); } /** * Convenience method to validate a JMS exception with a linked {@link AMQConstant#ACCESS_REFUSED} 403 error code exception. */ public void check403Exception(Throwable t) throws Exception { assertNotNull("There was no linked exception", t); assertTrue("Wrong linked exception type : " + t.getClass(), t instanceof AMQException); assertEquals("Incorrect error code received", 403, ((AMQException) t).getErrorCode().getCode()); //use the latch to ensure the control thread waits long enough for the exception thread //to have done enough to mark the connection closed before teardown commences assertTrue("Timed out waiting for conneciton to report close", _exceptionReceived.await(2, TimeUnit.SECONDS)); } }