/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.http;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Optional;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import org.apache.commons.io.IOUtils;
import org.sonar.api.config.Settings;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.process.DefaultProcessCommands;
import org.sonar.process.systeminfo.protobuf.ProtobufSystemInfo;
import static java.util.Objects.requireNonNull;
import static org.sonar.process.ProcessEntryPoint.PROPERTY_SHARED_PATH;
import static org.sonar.process.ProcessId.COMPUTE_ENGINE;
/**
* Client for the HTTP server of the Compute Engine.
*/
public class CeHttpClient {
private static final String PATH_CHANGE_LOG_LEVEL = "changeLogLevel";
private static final String PATH_SYSTEM_INFO = "systemInfo";
private final File ipcSharedDir;
public CeHttpClient(Settings props) {
this.ipcSharedDir = new File(props.getString(PROPERTY_SHARED_PATH));
}
/**
* Connects to the specified JVM process and requests system information.
* @return the system info, or absent if the process is not up or if its HTTP URL
* is not registered into IPC.
*/
public Optional<ProtobufSystemInfo.SystemInfo> retrieveSystemInfo() {
return call(SystemInfoActionClient.INSTANCE);
}
private enum SystemInfoActionClient implements ActionClient<Optional<ProtobufSystemInfo.SystemInfo>> {
INSTANCE;
@Override
public String getPath() {
return PATH_SYSTEM_INFO;
}
@Override
public Optional<ProtobufSystemInfo.SystemInfo> getDefault() {
return Optional.empty();
}
@Override
public Optional<ProtobufSystemInfo.SystemInfo> call(String url) throws Exception {
byte[] protobuf = IOUtils.toByteArray(new URI(url));
return Optional.of(ProtobufSystemInfo.SystemInfo.parseFrom(protobuf));
}
}
public void changeLogLevel(LoggerLevel level) {
requireNonNull(level, "level can't be null");
call(new ChangeLogLevelActionClient(level));
}
private static final class ChangeLogLevelActionClient implements ActionClient<Void> {
private final LoggerLevel newLogLevel;
private ChangeLogLevelActionClient(LoggerLevel newLogLevel) {
this.newLogLevel = newLogLevel;
}
@Override
public String getPath() {
return PATH_CHANGE_LOG_LEVEL;
}
@Override
public Void getDefault() {
return null;
}
@Override
public Void call(String url) throws Exception {
okhttp3.Request request = new okhttp3.Request.Builder()
.post(RequestBody.create(null, new byte[0]))
.url(url + "?level=" + newLogLevel.name())
.build();
okhttp3.Response response = new OkHttpClient().newCall(request).execute();
if (response.code() != 200) {
throw new IOException(
String.format(
"Failed to change log level in Compute Engine. Code was '%s' and response was '%s' for url '%s'",
response.code(),
response.body().string(),
url));
}
return null;
}
}
private <T> T call(ActionClient<T> actionClient) {
try (DefaultProcessCommands commands = DefaultProcessCommands.secondary(ipcSharedDir, COMPUTE_ENGINE.getIpcIndex())) {
if (commands.isUp()) {
return actionClient.call(commands.getHttpUrl() + "/" + actionClient.getPath());
}
return actionClient.getDefault();
} catch (Exception e) {
throw new IllegalStateException("Failed to call HTTP server of process " + COMPUTE_ENGINE, e);
}
}
private interface ActionClient<T> {
/**
* Path of the action.
*/
String getPath();
/**
* Value to return when the Compute Engine is not ready.
*/
T getDefault();
/**
* Delegates to perform the call to the Compute Engine's specified absolute URL.
*/
T call(String url) throws Exception;
}
}