/** 
 * 
 * Copyright 2004 Hiram Chirino
 * 
 * Licensed 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.codehaus.activemq.security.jassjacc;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.codehaus.activemq.message.ActiveMQQueue;
import org.codehaus.activemq.message.ActiveMQTopic;

/**
 * Parses a Properties object into a set of {@see org.codehaus.activemq.security.jassjacc.BrokerSecurityConfig} and 
 * {@see org.codehaus.activemq.security.jassjacc.DestinationSecurityConfig} objects that can be used to 
 * secure the ActiveMQ broker.
 * 
 * Sample properties configuration:
 * <pre>
 * 
 * # Secure a connection the the 'localhost' broker
 * connect.roles=admins,traders,brokers,guests
 * 
 * # Secure the TEST_TOPIC topic.
 * topic.T1.names=TEST_TOPIC
 * topic.T1.consume.roles=traders
 * topic.T1.produce.roles=traders,brokers 
 * topic.T1.send.roles=traders,brokers 
 * 
 * # You can also secure more than one destination in one go.
 * queue.Q1.names=TEST_QUEUE,A_QUEUE,B_QUEUE
 * queue.Q1.consume.roles=traders
 * queue.Q1.produce.roles=traders,brokers 
 * queue.Q1.send.roles=traders,brokers
 *  
 * </pre>
 *  
 * 
 * @version $Revision: 1.1 $
 */
public class PropertiesConfigLoader {
	
	HashMap destinationMap = new HashMap();
	BrokerSecurityConfig brokerSecurityConfig = new BrokerSecurityConfig();
	
	public PropertiesConfigLoader(String brokerName, Properties props) throws IOException {
	
		brokerSecurityConfig.setBrokerName(brokerName);
		Pattern brokerConnectRoles = Pattern.compile("^connect\\.roles$");		
		Pattern destNames = Pattern.compile("^(queue|topic)\\.([^\\.]+)\\.names$");		
		Pattern destConsumeRoles = Pattern.compile("^(queue|topic)\\.([^\\.]+)\\.consume\\.roles$");		
		Pattern destProduceRoles = Pattern.compile("^(queue|topic)\\.([^\\.]+)\\.produce\\.roles$");		
		Pattern destSendRoles = Pattern.compile("^(queue|topic)\\.([^\\.]+)\\.send\\.roles$");		
		
		Matcher matcher;
		Enumeration enumeration;
		
		enumeration = props.propertyNames();
		while (enumeration.hasMoreElements()) {
			String prop = (String) enumeration.nextElement();
			if ((matcher=brokerConnectRoles.matcher(prop)).matches()) {				
				String[] roles = trim(props.getProperty(prop).split("\\,"));
				brokerSecurityConfig.setConnectRoles(new HashSet(Arrays.asList(roles)));
			} else if ((matcher=destNames.matcher(prop)).matches()) {				
				String type = matcher.group(1);				
				String dest = matcher.group(2);				
				setDestNames( type, dest, trim(props.getProperty(prop).split("\\,")) );
			}
		}
		
		enumeration = props.propertyNames();
		while (enumeration.hasMoreElements()) {
			String prop = (String) enumeration.nextElement();
			if ((matcher=destConsumeRoles.matcher(prop)).matches()) {				
				String type = matcher.group(1);				
				String dest = matcher.group(2);				
				setDestConsumeRoles( type, dest, trim(props.getProperty(prop).split("\\,")) );
			} else if ((matcher=destProduceRoles.matcher(prop)).matches()) {				
				String type = matcher.group(1);				
				String dest = matcher.group(2);				
				setDestProduceRoles( type, dest, trim(props.getProperty(prop).split("\\,")) );
			} else if ((matcher=destSendRoles.matcher(prop)).matches()) {				
				String type = matcher.group(1);				
				String dest = matcher.group(2);				
				setDestSendRoles( type, dest, trim(props.getProperty(prop).split("\\,")) );
			}				
		}
		
	}

	private void setDestSendRoles(String type, String dest, String[] roles) throws IOException {
		List configs = getDestConfig(type, dest);
		for (Iterator iter = configs.iterator(); iter.hasNext();) {
			DestinationSecurityConfig config = (DestinationSecurityConfig) iter.next();
			config.setProduceRoles(new HashSet(Arrays.asList(roles)));
		}
	}

	private void setDestProduceRoles(String type, String dest, String[] roles) throws IOException {
		List configs = getDestConfig(type, dest);
		for (Iterator iter = configs.iterator(); iter.hasNext();) {
			DestinationSecurityConfig config = (DestinationSecurityConfig) iter.next();
			config.setProduceRoles(new HashSet(Arrays.asList(roles)));
		}
	}

	private void setDestConsumeRoles(String type, String dest, String[] roles) throws IOException {
		List configs = getDestConfig(type, dest);
		for (Iterator iter = configs.iterator(); iter.hasNext();) {
			DestinationSecurityConfig config = (DestinationSecurityConfig) iter.next();
			config.setConsumeRoles(new HashSet(Arrays.asList(roles)));			
		}
	}

	private List getDestConfig(String type, String dest) throws IOException {
		List rc = (List) destinationMap.get(type+":"+dest);
		if( rc==null ) {
			throw new IOException("Expected property not found: "+type+"."+dest+".names");
		}
		return rc;
	}

	private void setDestNames(String type, String dest, String[] names) throws IOException {
		ArrayList list = new ArrayList();
		for (int i = 0; i < names.length; i++) {
			DestinationSecurityConfig config = new DestinationSecurityConfig();
			config.setBrokerName( brokerSecurityConfig.getBrokerName() );
			if( "queue".equals(type) ) {
				config.setDestination(new ActiveMQQueue(dest));
			} else {
				config.setDestination(new ActiveMQTopic(dest));
			}
			list.add(config);			
		}		
		destinationMap.put(type+":"+dest, list);		
	}

	private static String[] trim(String[] brokers) {
		for (int i = 0; i < brokers.length; i++) {
			brokers[i] = brokers[i].trim();
		}
		return brokers;
	}
	
	public DestinationSecurityConfig[] getDestinationSecurityConfigs() {
		ArrayList answer = new ArrayList();		
		for (Iterator iter = destinationMap.values().iterator(); iter.hasNext();) {
			List l = (List) iter.next();
			answer.addAll(l);
		}
		
		DestinationSecurityConfig rc[] = new DestinationSecurityConfig[answer.size()];
		answer.toArray(rc);
		return rc;		
	}
	
	public BrokerSecurityConfig getBrokerSecurityConfig() {
		return brokerSecurityConfig;		
	}

	public void installSecurity() {
		JassJaccSecurityAdapter.secure(brokerSecurityConfig);

		DestinationSecurityConfig[] destinationSecurityConfigs = getDestinationSecurityConfigs();
		for (int i = 0; i < destinationSecurityConfigs.length; i++) {
			DestinationSecurityConfig config = destinationSecurityConfigs[i];
			JassJaccSecurityAdapter.secure(config);
		}

	}
	
}
