/*
* 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_HOST, 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()) {
try {
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));
} catch (Exception e) {
LOGGER.error("Error while updating regions. {}", e.getMessage());
}
}
}
/**
* 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;
}
}