blob: eedb4cca9c3f65cc7c247e3837068ae08aa82d05 [file] [log] [blame]
/*
* 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.geode.management.internal;
import java.net.URI;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.RegionAttributes;
import org.apache.geode.cache.Scope;
import org.apache.geode.cache.internal.HttpService;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.internal.GemFireVersion;
import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.InternalRegionArguments;
import org.apache.geode.internal.net.SocketCreator;
import org.apache.geode.internal.security.SecurityService;
import org.apache.geode.logging.internal.log4j.api.LogService;
/**
* Agent implementation that controls the HTTP server end points used for REST clients to connect
* gemfire data node.
* <p>
* The RestAgent is used to start http service in embedded mode on any non manager data node with
* developer REST APIs service enabled.
*
* @since GemFire 8.0
*/
public class RestAgent {
private static final Logger logger = LogService.getLogger();
private boolean running = false;
private final DistributionConfig config;
private final SecurityService securityService;
public RestAgent(DistributionConfig config, SecurityService securityService) {
this.config = config;
this.securityService = securityService;
}
public synchronized boolean isRunning() {
return this.running;
}
public synchronized void start(InternalCache cache) {
if (!this.running && this.config.getHttpServicePort() != 0) {
try {
startHttpService(cache);
this.running = true;
cache.setRESTServiceRunning(true);
// create region to hold query information (queryId, queryString). Added
// for the developer REST APIs
RestAgent.createParameterizedQueryRegion();
} catch (Throwable e) {
logger.warn("Unable to start dev REST API: {}", e.toString());
}
}
}
private final String GEMFIRE_VERSION = GemFireVersion.getGemFireVersion();
private AgentUtil agentUtil = new AgentUtil(GEMFIRE_VERSION);
private boolean isRunningInTomcat() {
return (System.getProperty("catalina.base") != null
|| System.getProperty("catalina.home") != null);
}
// Start HTTP service in embedded mode
public void startHttpService(InternalCache cache) throws Exception {
// Find the developer REST WAR file
final URI gemfireAPIWar = agentUtil.findWarLocation("geode-web-api");
if (gemfireAPIWar == null) {
logger.info(
"Unable to find GemFire Developer REST API WAR file; the Developer REST Interface for GemFire will not be accessible.");
}
// Check if we're already running inside Tomcat
if (isRunningInTomcat()) {
logger.warn(
"Detected presence of catalina system properties. HTTP service will not be started. To enable the GemFire Developer REST API, please deploy the /geode-web-api WAR file in your application server.");
} else if (agentUtil.isAnyWarFileAvailable(gemfireAPIWar)) {
Map<String, Object> securityServiceAttr = new HashMap<>();
securityServiceAttr.put(HttpService.SECURITY_SERVICE_SERVLET_CONTEXT_PARAM,
securityService);
if (cache.getOptionalService(HttpService.class).isPresent()) {
HttpService httpService = cache.getOptionalService(HttpService.class).get();
Path gemfireAPIWarPath = Paths.get(gemfireAPIWar);
httpService.addWebApplication("/gemfire-api", gemfireAPIWarPath, securityServiceAttr);
httpService.addWebApplication("/geode", gemfireAPIWarPath, securityServiceAttr);
} else {
logger.warn("HttpService is not available - could not start Dev REST API");
}
}
}
public static String getBindAddressForHttpService(DistributionConfig config) {
String bindAddress = config.getHttpServiceBindAddress();
if (StringUtils.isNotBlank(bindAddress))
return bindAddress;
bindAddress = config.getServerBindAddress();
if (StringUtils.isNotBlank(bindAddress))
return bindAddress;
bindAddress = config.getBindAddress();
if (StringUtils.isNotBlank(bindAddress))
return bindAddress;
try {
bindAddress = SocketCreator.getLocalHost().getHostAddress();
logger.info("RestAgent.getBindAddressForHttpService.localhost: "
+ SocketCreator.getLocalHost().getHostAddress());
} catch (UnknownHostException e) {
logger.error("LocalHost could not be found.", e);
}
return bindAddress;
}
/**
* This method will create a REPLICATED region named _ParameterizedQueries__. In developer REST
* APIs, this region will be used to store the queryId and queryString as a key and value
* respectively.
*/
public static void createParameterizedQueryRegion() {
try {
if (logger.isDebugEnabled()) {
logger.debug("Starting creation of __ParameterizedQueries__ region");
}
InternalCache cache = (InternalCache) CacheFactory.getAnyInstance();
if (cache != null) {
final InternalRegionArguments regionArguments = new InternalRegionArguments();
regionArguments.setIsUsedForMetaRegion(true);
final AttributesFactory<String, String> attributesFactory =
new AttributesFactory<String, String>();
attributesFactory.setConcurrencyChecksEnabled(false);
attributesFactory.setDataPolicy(DataPolicy.REPLICATE);
attributesFactory.setKeyConstraint(String.class);
attributesFactory.setScope(Scope.DISTRIBUTED_ACK);
attributesFactory.setStatisticsEnabled(false);
attributesFactory.setValueConstraint(String.class);
final RegionAttributes<String, String> regionAttributes = attributesFactory.create();
cache.createVMRegion("__ParameterizedQueries__", regionAttributes, regionArguments);
if (logger.isDebugEnabled()) {
logger.debug("Successfully created __ParameterizedQueries__ region");
}
} else {
logger.error("Cannot create ParameterizedQueries Region as no cache found!");
}
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Error creating __ParameterizedQueries__ Region with cause {}", e.getMessage(),
e);
}
}
}
}