package org.rapidoid.deploy;
import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since;
import org.rapidoid.commons.Str;
import org.rapidoid.gui.Btn;
import org.rapidoid.gui.GUI;
import org.rapidoid.html.Tag;
import org.rapidoid.http.Current;
import org.rapidoid.http.HttpVerb;
import org.rapidoid.http.Req;
import org.rapidoid.http.ReqHandler;
import org.rapidoid.io.IO;
import org.rapidoid.process.ProcessHandle;
import org.rapidoid.u.U;
import org.rapidoid.util.Msc;
import org.rapidoid.util.Tokens;
import java.io.File;
import java.util.Date;
import java.util.List;
import java.util.Map;
/*
* #%L
* rapidoid-platform
* %%
* Copyright (C) 2014 - 2017 Nikolche Mihajlovski and contributors
* %%
* 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.
* #L%
*/
@Authors("Nikolche Mihajlovski")
@Since("5.1.0")
public class DeploymentHandler extends GUI implements ReqHandler {
public static final String SCOPES = "POST:/rapidoid/stage, POST:/rapidoid/deploy, GET:POST:/rapidoid/deployment";
private static final String DEPLOYMENT_HELP = "Application deployment works by uploading a JAR and executing it in a child Java process.";
@Override
public Object execute(Req req) throws Exception {
List<Object> info = U.list();
Map<String, String> tokenData = U.map(Tokens._USER, Current.username(), Tokens._SCOPE, SCOPES);
String token = Tokens.serialize(tokenData);
String appJar = Msc.mainAppJar();
String stagedAppJar = appJar + ".staged";
info.add(h3("Deployment status:"));
info.add(grid(jarsInfo(appJar, stagedAppJar, req)));
info.add(h3("Your deployment token is:"));
info.add(copy(textarea(token).rows("2").attr("readonly", "readonly").style("width:100%; font-size: 10px;")));
info.add(h3("Upload an application JAR to stage it and then deploy it:"));
info.add(render("upload-jar.html"));
info.add(h3("Packaging and deploying with Maven:"));
String cmd = "mvn clean org.rapidoid:app:deploy";
info.add(copy(textarea(cmd).rows("2").attr("readonly", "readonly").style("width:100%; font-size: 10px;")));
info.add(h3("HTTP API for Deployment:"));
info.add(grid(apisInfo()));
return multi(info);
}
@SuppressWarnings("unchecked")
private List<Map<String, ?>> jarsInfo(String appJar, String stagedAppJar, Req req) {
return U.list(
jarInfo(appJar, "Currently deployed application JAR", false, req),
jarInfo(stagedAppJar, "Application JAR to be deployed", true, req)
);
}
private Map<String, ?> jarInfo(final String filename, String desc, boolean staged, Req req) {
File file = new File(filename);
boolean exists = file.exists();
String size = exists ? (file.length() / 1024) + " KB" : "";
Object since = exists ? GUI.display(new Date(file.lastModified())) : "";
Btn deploy = staged && exists ? btn("Deploy")
.class_("btn btn-primary btn-xs")
.confirm("Do you want to DEPLOY the application?")
.onSuccess(new Runnable() {
@Override
public void run() {
AppDeployer.deploy(filename, Str.trimr(filename, ".staged"));
}
}) : null;
String cmd = staged ? "staged" : "deployed";
Btn delete = exists
? btn("Delete").command("delete_" + cmd)
.class_("btn btn-danger btn-xs")
.confirm("Do you want to delete the file '" + filename + "'?")
: null;
if (delete != null) {
delete.onSuccess(new Runnable() {
@Override
public void run() {
AppDeployer.stopApp("app");
IO.delete(filename);
}
});
}
Btn details = null;
Btn restart = null;
if (!staged && exists) {
List<ProcessHandle> processes = AppDeployer.processes().items();
if (U.notEmpty(processes)) {
String procHandleId = processes.get(0).id();
String processUrl = Msc.specialUri("processes/" + procHandleId);
details = btn("Details")
.class_("btn btn-default btn-xs")
.go(processUrl);
restart = btn("Restart")
.class_("btn btn-warning btn-xs")
.onSuccess(new Runnable() {
@Override
public void run() {
AppDeployer.startOrRestartApp("app");
}
});
}
}
return U.map(
"file", filename,
"description", desc,
"exists", exists,
"size", size,
"since", since,
"actions", multi(deploy, details, restart, delete)
);
}
@SuppressWarnings("unchecked")
private List<Map<String, ?>> apisInfo() {
return U.list(
apiInfo("Staging of the application JAR",
verb(HttpVerb.POST), "/rapidoid/stage",
U.map("_token", "Deployment token", "file", "<your-jar>"),
"curl -F 'file=@app.jar' 'http://example.com/rapidoid/stage?_token=..."),
apiInfo("Deployment of the staged JAR",
verb(HttpVerb.POST), "/rapidoid/deploy",
U.map("_token", "Deployment token"),
"curl -X POST 'http://example.com/rapidoid/deploy?_token=...")
);
}
private Map<String, ?> apiInfo(String desc, Tag verb, String uri, Map<String, String> params, String example) {
return U.map("description", desc, "verb", verb, "uri", span(uri).class_("text-box"), "parameters", grid(params).headless(true), "example", example);
}
}