/*
 * Copyright 2002-2012 the original author or authors.
 *
 * 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
 *
 *      https://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.springframework.integration.samples.ftp;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.messaging.MessageChannel;

/**
 * Demonstrates how a dynamic Spring Integration flow snippet can be used
 * to send files to dynamic destinations.
 *
 * @author Gary Russell
 * @author Amol Nayak
 * @since 2.1
 *
 */
public class DynamicFtpChannelResolver {

	//In production environment this value will be significantly higher
	//This is just to demonstrate the concept of limiting the max number of
	//Dynamically created application contexts we'll hold in memory when we execute
	//the code from a junit
	public static final int MAX_CACHE_SIZE = 2;

	private final LinkedHashMap<String, MessageChannel> channels =
				new LinkedHashMap<String, MessageChannel>() {
					
					private static final long serialVersionUID = 1L;

					@Override
					protected boolean removeEldestEntry(
							Entry<String, MessageChannel> eldest) {
						//This returning true means the least recently used
						//channel and its application context will be closed and removed
						boolean remove = size() > MAX_CACHE_SIZE;
						if(remove) {
							MessageChannel channel = eldest.getValue();
							ConfigurableApplicationContext ctx = contexts.get(channel);
							if(ctx != null) { //shouldn't be null ideally
								ctx.close();
								contexts.remove(channel);
							}
						}
						return remove;
					}
					
				};

	private final Map<MessageChannel, ConfigurableApplicationContext> contexts =
				new HashMap<MessageChannel, ConfigurableApplicationContext>();



	/**
	 * Resolve a customer to a channel, where each customer gets a private
	 * application context and the channel is the inbound channel to that
	 * application context.
	 *
	 * @param customer
	 * @return a channel
	 */
	public MessageChannel resolve(String customer) {
		MessageChannel channel = this.channels.get(customer);
		if (channel == null) {
			channel = createNewCustomerChannel(customer);
		}
		return channel;
	}

	private synchronized MessageChannel createNewCustomerChannel(String customer) {
		MessageChannel channel = this.channels.get(customer);
		if (channel == null) {
			ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
					new String[] { "/META-INF/spring/integration/dynamic-ftp-outbound-adapter-context.xml" },
					false);
			this.setEnvironmentForCustomer(ctx, customer);
			ctx.refresh();
			channel = ctx.getBean("toFtpChannel", MessageChannel.class);
			this.channels.put(customer, channel);
			//Will works as the same reference is presented always
			this.contexts.put(channel, ctx);
		}
		return channel;
	}

	/**
	 * Use Spring 3.1. environment support to set properties for the
	 * customer-specific application context.
	 *
	 * @param ctx
	 * @param customer
	 */
	private void setEnvironmentForCustomer(ConfigurableApplicationContext ctx,
			String customer) {
		StandardEnvironment env = new StandardEnvironment();
		Properties props = new Properties();
		// populate properties for customer
		props.setProperty("host", "host.for." + customer);
		props.setProperty("user", "user");
		props.setProperty("password", "password");
		props.setProperty("remote.directory", "/tmp");
		PropertiesPropertySource pps = new PropertiesPropertySource("ftpprops", props);
		env.getPropertySources().addLast(pps);
		ctx.setEnvironment(env);
	}
}
