/* 
 * 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.ngrinder.region.service;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import net.grinder.common.processidentity.AgentIdentity;
import net.grinder.util.NetworkUtils;
import net.sf.ehcache.Ehcache;
import org.apache.commons.lang.StringUtils;
import org.ngrinder.common.constant.ClusterConstants;
import org.ngrinder.common.util.TypeConvertUtils;
import org.ngrinder.infra.config.Config;
import org.ngrinder.infra.schedule.ScheduledTaskService;
import org.ngrinder.perftest.service.AgentManager;
import org.ngrinder.region.model.RegionInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;

import static org.ngrinder.common.util.ExceptionUtils.processException;

/**
 * Region service class. This class responsible to keep the status of available regions.
 *
 * @author Mavlarn
 * @author JunHo Yoon
 * @since 3.1
 */
@Service
public class RegionService {

	@SuppressWarnings("UnusedDeclaration")
	private static final Logger LOGGER = LoggerFactory.getLogger(RegionService.class);

	@Autowired
	private Config config;

	@Autowired
	private ScheduledTaskService scheduledTaskService;

	@Autowired
	private CacheManager cacheManager;
	private Cache cache;


	/**
	 * Set current region into cache, using the IP as key and region name as value.
	 */
	@PostConstruct
	public void initRegion() {
		if (config.isClustered()) {
			cache = cacheManager.getCache("regions");
			verifyDuplicatedRegion();
			scheduledTaskService.addFixedDelayedScheduledTask(new Runnable() {
				@Override
				public void run() {
					checkRegionUpdate();
				}
			}, 3000);
		}
	}

	/**
	 * Verify duplicate region when starting with cluster mode.
	 *
	 * @since 3.2
	 */
	private void verifyDuplicatedRegion() {
		Map<String, RegionInfo> regions = getAll();
		String localRegion = getCurrent();
		RegionInfo regionInfo = regions.get(localRegion);
		if (regionInfo != null && !StringUtils.equals(regionInfo.getIp(), config.getClusterProperties().getProperty
				(ClusterConstants.PROP_CLUSTER_IP, NetworkUtils.DEFAULT_LOCAL_HOST_ADDRESS))) {
			throw processException("The region name, " + localRegion
					+ ", is already used by other controller " + regionInfo.getIp()
					+ ". Please set the different region name in this controller.");
		}
	}

	@Autowired
	private AgentManager agentManager;

	/**
	 * check Region and Update its value.
	 */
	public void checkRegionUpdate() {
		if (!config.isInvisibleRegion()) {
			HashSet<AgentIdentity> newHashSet = Sets.newHashSet(agentManager.getAllAttachedAgents());
			final String regionIP = StringUtils.defaultIfBlank(config.getCurrentIP(), NetworkUtils.DEFAULT_LOCAL_HOST_ADDRESS);
			cache.put(getCurrent(), new RegionInfo(regionIP, config.getControllerPort(), newHashSet));
		}
	}


	/**
	 * Get current region. This method returns where this service is running.
	 *
	 * @return current region.
	 */
	public String getCurrent() {
		return config.getRegion();
	}

	/**
	 * Get region by region name
	 *
	 * @param regionName
	 * @return region info
	 */
	public RegionInfo getOne(String regionName) {
		return (RegionInfo) cache.get(regionName).get();
	}

	/**
	 * Get region list of all clustered controller.
	 *
	 * @return region list
	 */
	public Map<String, RegionInfo> getAll() {
		Map<String, RegionInfo> regions = Maps.newHashMap();
		if (config.isClustered()) {
			for (Object eachKey : ((Ehcache) (cache.getNativeCache())).getKeysWithExpiryCheck()) {
				ValueWrapper valueWrapper = cache.get(eachKey);
				if (valueWrapper != null && valueWrapper.get() != null) {
					regions.put((String) eachKey, (RegionInfo) valueWrapper.get());
				}
			}
		}
		return regions;
	}

	public ArrayList<String> getAllVisibleRegionNames() {
		final ArrayList<String> regions = new ArrayList<String>();
		if (config.isClustered()) {
			for (Object eachKey : ((Ehcache) (cache.getNativeCache())).getKeysWithExpiryCheck()) {
				ValueWrapper valueWrapper = cache.get(eachKey);
				if (valueWrapper != null && valueWrapper.get() != null) {
					final RegionInfo region = TypeConvertUtils.cast(valueWrapper.get());
					if (region.isVisible()) {
						regions.add((String) eachKey);
					}
				}
			}
		}
		Collections.sort(regions);
		return regions;
	}

	Config getConfig() {
		return config;
	}

	/**
	 * For unit test
	 */
	public void setConfig(Config config) {
		this.config = config;
	}

	/**
	 * For unit test
	 */
	public void setCache(Cache cache) {
		this.cache = cache;
	}
}

@@ -3401,16 +3401,26 @@
 on()) %7B%0A
+%09%09%09try %7B%0A%09
 %09%09%09HashS
@@ -3504,16 +3504,17 @@
 nts());%0A
+%09
 %09%09%09final
@@ -3624,16 +3624,17 @@
 DRESS);%0A
+%09
 %09%09%09cache
@@ -3719,16 +3719,118 @@
 hSet));%0A
+%09%09%09%7D catch (Exception e) %7B%0A%09%09%09%09LOGGER.error(%22Error while updating regions. %7B%7D%22, e.getMessage());%0A%09%09%09%7D%0A
 %09%09%7D%0A%09%7D%0A%0A

Reduce unnecessary long log