org.glassfish.admin.rest.resources.admin.CommandResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project for IBM JDK
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.admin.rest.resources.admin;
import com.sun.enterprise.admin.remote.ParamsWithPayload;
import com.sun.enterprise.admin.remote.RemoteRestAdminCommand;
import com.sun.enterprise.admin.remote.RestPayloadImpl;
import com.sun.enterprise.admin.util.CachedCommandModel;
import com.sun.enterprise.config.serverbeans.Server;
import com.sun.enterprise.universal.collections.ManifestUtils;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.StringUtils;
import com.sun.enterprise.util.uuid.UuidGenerator;
import com.sun.enterprise.util.uuid.UuidGeneratorImpl;
import com.sun.enterprise.v3.common.ActionReporter;
import com.sun.enterprise.v3.common.PlainTextActionReporter;
import com.sun.enterprise.v3.common.PropsFileActionReporter;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.logging.Level;
import javax.inject.Inject;
import javax.security.auth.Subject;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.ResponseBuilder;
import org.glassfish.admin.rest.RestLogging;
import org.glassfish.admin.rest.utils.SseCommandHelper;
import org.glassfish.api.ActionReport;
import org.glassfish.api.admin.*;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.internal.api.Globals;
import org.glassfish.jersey.internal.util.collection.Ref;
import org.glassfish.jersey.media.sse.SseFeature;
/**
*
* @author mmares
*/
@Path("/")
public class CommandResource {
private final static LocalStringManagerImpl strings = new LocalStringManagerImpl(CommandResource.class);
public static final String SESSION_COOKIE_NAME = "JSESSIONID";
public static final int MAX_AGE = 86400 ;
private static UuidGenerator uuidGenerator = new UuidGeneratorImpl();
private static volatile String serverName;
private CommandRunner commandRunner;
@Inject
protected Ref subjectRef;
// -------- GET+OPTION: Get CommandModel
@GET
@Path("/{command:.*}/")
@Produces({MediaType.APPLICATION_JSON, "application/x-javascript"})
public Response getCommandModel(@PathParam("command") String command) throws WebApplicationException {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "getCommandModel({0})", commandName);
}
CommandModel model = getCommandModel(commandName);
String eTag = CachedCommandModel.computeETag(model);
return Response.ok(model).tag(new EntityTag(eTag, true)).build();
}
@OPTIONS
@Path("/{command:.*}/")
@Produces({MediaType.APPLICATION_JSON, "application/x-javascript"})
public Response optionsCommandModel(@PathParam("command") String commandName) throws WebApplicationException {
return getCommandModel(commandName);
}
// -------- GET: Manpage
@GET
@Path("/{command:.*}/manpage")
@Produces({MediaType.TEXT_HTML})
public String getManPageHtml(@PathParam("command") String command)
throws IOException, WebApplicationException {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "getManPageHtml({0})", commandName);
}
BufferedReader help = getManPageReader(commandName);
if (help == null) {
return null;
}
StringBuilder result = new StringBuilder();
result.append("");
String line;
while ((line = help.readLine()) != null) {
result.append(leadingSpacesToNbsp(StringUtils.escapeForHtml(line))).append("
\n");
}
result.append("");
return result.toString();
}
@GET
@Path("/{command:.*}/manpage")
@Produces({MediaType.TEXT_PLAIN})
public String getManPageTxt(@PathParam("command") String command, @QueryParam("eol") String eol)
throws IOException, WebApplicationException {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "getManPageTxt({0}, {1})", new Object[]{commandName, eol});
}
BufferedReader help = getManPageReader(commandName);
if (help == null) {
return null;
}
if (!StringUtils.ok(eol)) {
eol = ManifestUtils.EOL;
}
StringBuilder result = new StringBuilder();
String line;
while ((line = help.readLine()) != null) {
result.append(line).append(eol);
}
return result.toString();
}
// -------- POST: Execute command [just ACTION-REPORT]
@POST
@Path("/{command:.*}/")
@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
@Produces({MediaType.APPLICATION_JSON, "application/x-javascript"})
public Response execCommandSimpInSimpOut(@PathParam("command") String command,
@HeaderParam("X-Indent") String indent,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId,
ParameterMap data) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandSimpInSimpOut({0})", commandName);
}
return executeCommand(commandName, null, data, false, indent, modelETag, jSessionId);
}
@POST
@Path("/{command:.*}/")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces({MediaType.APPLICATION_JSON, "application/x-javascript"})
public Response execCommandMultInSimpOut(@PathParam("command") String command,
@HeaderParam("X-Indent") String indent,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId,
ParamsWithPayload pwp) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandMultInSimpOut({0})", commandName);
}
ParameterMap data = null;
Payload.Inbound inbound = null;
if (pwp != null) {
data = pwp.getParameters();
inbound = pwp.getPayloadInbound();
}
return executeCommand(commandName, inbound, data, false, indent, modelETag, jSessionId);
}
@POST
@Path("/{command:.*}/")
@Produces({MediaType.APPLICATION_JSON, "application/x-javascript"})
public Response execCommandEmptyInSimpOut(@PathParam("command") String command,
@HeaderParam("X-Indent") String indent,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandEmptyInSimpOut({0})", commandName);
}
ParameterMap data = new ParameterMap();
return executeCommand(commandName, null, data, false, indent, modelETag, jSessionId);
}
// -------- POST: Execute command [MULTIPART result]
@POST
@Path("/{command:.*}/")
@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
@Produces("multipart/mixed")
public Response execCommandSimpInMultOut(@PathParam("command") String command,
@HeaderParam("X-Indent") String indent,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId,
ParameterMap data) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandSimpInMultOut({0})", commandName);
}
return executeCommand(commandName, null, data, true, indent, modelETag, jSessionId);
}
@POST
@Path("/{command:.*}/")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces("multipart/mixed")
public Response execCommandMultInMultOut(@PathParam("command") String command,
@HeaderParam("X-Indent") String indent,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId,
ParamsWithPayload pwp) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandMultInMultOut({0})", commandName);
}
ParameterMap data = null;
Payload.Inbound inbound = null;
if (pwp != null) {
data = pwp.getParameters();
inbound = pwp.getPayloadInbound();
}
return executeCommand(commandName, inbound, data, true, indent, modelETag, jSessionId);
}
@POST
@Path("/{command:.*}/")
@Produces("multipart/mixed")
public Response execCommandEmptyInMultOut(@PathParam("command") String command,
@HeaderParam("X-Indent") String indent,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandEmptyInMultOut({0})", commandName);
}
ParameterMap data = new ParameterMap();
return executeCommand(commandName, null, data, true, indent, modelETag, jSessionId);
}
// -------- POST: Execute command [SSE]
@POST
@Path("/{command:.*}/")
@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
@Produces(SseFeature.SERVER_SENT_EVENTS)
public Response execCommandSimpInSseOut(@PathParam("command") String command,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId,
ParameterMap data) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandSimpInSseOut({0})", commandName);
}
return executeSseCommand(commandName, null, data, modelETag, jSessionId);
}
@POST
@Path("/{command:.*}/")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(SseFeature.SERVER_SENT_EVENTS)
public Response execCommandMultInSseOut(@PathParam("command") String command,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId,
ParamsWithPayload pwp) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandMultInMultOut({0})", commandName);
}
ParameterMap data = null;
if (pwp != null) {
data = pwp.getParameters();
}
return executeSseCommand(commandName, null, data, modelETag, jSessionId);
}
@POST
@Path("/{command:.*}/")
@Produces(SseFeature.SERVER_SENT_EVENTS)
public Response execCommandEmptyInSseOut(@PathParam("command") String command,
@HeaderParam(RemoteRestAdminCommand.COMMAND_MODEL_MATCH_HEADER) String modelETag,
@CookieParam(SESSION_COOKIE_NAME) Cookie jSessionId) {
CommandName commandName = new CommandName(normalizeCommandName(command));
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "execCommandEmptyInMultOut({0})", commandName);
}
ParameterMap data = new ParameterMap();
return executeSseCommand(commandName, null, data, modelETag, jSessionId);
}
// -------- private implementation
private String normalizeCommandName(String str) {
if (str == null) {
return null;
}
if (str.endsWith("/")) {
return str.substring(0, str.length() - 1);
} else {
return str;
}
}
private void checkCommandModelETag(CommandModel model, String modelETag) throws WebApplicationException {
CommandRunner cr = getCommandRunner();
if (StringUtils.ok(modelETag) && !cr.validateCommandModelETag(model, modelETag)) {
String message =
strings.getLocalString("commandmodel.etag.invalid",
"Cached command model for command {0} is invalid.", model.getCommandName());
throw new WebApplicationException(Response.status(Response.Status.PRECONDITION_FAILED)
.type(MediaType.TEXT_PLAIN)
.entity(message)
.build());
}
}
private Response executeSseCommand(CommandName commandName, Payload.Inbound inbound,
ParameterMap params, String modelETag, Cookie jSessionId) throws WebApplicationException {
//Scope support
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "executeSseCommand(): ", commandName);
}
//Check command model
CommandModel model = getCommandModel(commandName);
checkCommandModelETag(model, modelETag);
//Execute it
boolean notifyOption = false;
if (params != null) {
notifyOption = params.containsKey("notify");
}
final CommandRunner.CommandInvocation commandInvocation =
getCommandRunner().getCommandInvocation(commandName.getScope(),
commandName.getName(), new PropsFileActionReporter(), getSubject(),notifyOption);
if (inbound != null) {
commandInvocation.inbound(inbound);
}
commandInvocation
.outbound(new RestPayloadImpl.Outbound(false))
.managedJob()
.parameters(params);
ResponseBuilder rb = Response.status(HttpURLConnection.HTTP_OK);
if ( isSingleInstanceCommand(model)) {
rb.cookie(getJSessionCookie(jSessionId));
}
rb.entity(SseCommandHelper.invokeAsync(commandInvocation, null));
return rb.build();
}
private Response executeCommand(CommandName commandName, Payload.Inbound inbound,
ParameterMap params, boolean supportsMultiparResult, String xIndentHeader,
String modelETag, Cookie jSessionId) throws WebApplicationException {
//Scope support
if (RestLogging.restLogger.isLoggable(Level.FINEST)) {
RestLogging.restLogger.log(Level.FINEST, "executeCommand(): ", commandName);
}
//Check command model
CommandModel model = getCommandModel(commandName);
checkCommandModelETag(model, modelETag);
//Execute it
boolean notifyOption = false;
if (params != null) {
notifyOption = params.containsKey("notify");
}
ActionReporter ar = new PropsFileActionReporter(); //new RestActionReporter(); //Must use PropsFileActionReporter because some commands react diferently on it :-(
final RestPayloadImpl.Outbound outbound = new RestPayloadImpl.Outbound(false);
final CommandRunner.CommandInvocation commandInvocation =
getCommandRunner().getCommandInvocation(commandName.getScope(), commandName.getName(), ar, getSubject(),notifyOption);
if (inbound != null) {
commandInvocation.inbound(inbound);
}
commandInvocation
.outbound(outbound)
.parameters(params)
.execute();
ar = (ActionReporter) commandInvocation.report();
fixActionReporterSpecialCases(ar);
ActionReport.ExitCode exitCode = ar.getActionExitCode();
int status = HttpURLConnection.HTTP_OK; /*200 - ok*/
if (exitCode == ActionReport.ExitCode.FAILURE) {
status = HttpURLConnection.HTTP_INTERNAL_ERROR;
}
ResponseBuilder rb = Response.status(status);
if (xIndentHeader != null) {
rb.header("X-Indent", xIndentHeader);
}
if (supportsMultiparResult && outbound.size() > 0) {
ParamsWithPayload pwp = new ParamsWithPayload(outbound, ar);
rb.entity(pwp);
} else {
rb.type(MediaType.APPLICATION_JSON_TYPE);
rb.entity(ar);
}
if ( isSingleInstanceCommand(model)) {
rb.cookie(getJSessionCookie(jSessionId));
}
return rb.build();
}
/** Some ActionReporters has special logic which must be reflected here
*/
private void fixActionReporterSpecialCases(ActionReporter ar) {
if (ar == null) {
return;
}
if (ar instanceof PlainTextActionReporter) {
PlainTextActionReporter par = (PlainTextActionReporter) ar;
StringBuilder finalOutput = new StringBuilder();
par.getCombinedMessages(par, finalOutput);
String outs = finalOutput.toString();
if (!StringUtils.ok(outs)) {
par.getTopMessagePart().setMessage(strings.getLocalString("get.mon.no.data", "No monitoring data to report.") + "\n");
}
}
}
/**
* This will create a unique SessionId, Max-Age,Version,Path to be added to the Set-Cookie header
*/
public NewCookie getJSessionCookie(Cookie jSessionId) {
String value;
// If the request has a Cookie header and
// there is no failover then send back the same
// JSESSIONID
if (jSessionId != null && isJSessionCookieOk(jSessionId.getValue())) {
value = jSessionId.getValue();
} else {
value = uuidGenerator.generateUuid() + '.' + getServerName();
}
NewCookie result = new NewCookie(SESSION_COOKIE_NAME, value, "/command", null, null, MAX_AGE, false);
return result;
}
private boolean isJSessionCookieOk(String value) {
if (!StringUtils.ok(value)) {
return false;
}
return value.endsWith("." + getServerName());
}
private static boolean isSingleInstanceCommand(CommandModel model) {
if (model != null ) {
ExecuteOn executeOn = model.getClusteringAttributes();
if ((executeOn != null) && (executeOn.value().length ==1) &&
executeOn.value()[0].equals(org.glassfish.api.admin.RuntimeType.SINGLE_INSTANCE)) {
return true;
}
}
return false;
}
private static String leadingSpacesToNbsp(String str) {
if (str == null) {
return null;
}
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) != ' ') {
StringBuilder sb = new StringBuilder((i * 6) + (str.length() - i));
for (int j = 0; j < i; j++) {
sb.append(" ");
}
sb.append(str.substring(i));
return sb.toString();
}
}
return str;
}
private CommandModel getCommandModel(CommandName commandName) throws WebApplicationException {
CommandRunner cr = getCommandRunner();
CommandModel model = cr.getModel(commandName.getScope(), commandName.getName(), RestLogging.restLogger);
if (model == null) {
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND)
.type(MediaType.TEXT_PLAIN)
.entity(strings.getLocalString("adapter.command.notfound",
"Command {0} not found. \nCheck the entry of command name. This command may be provided by a package that is not installed.", commandName.getName()))
.build());
}
return model;
}
private BufferedReader getManPageReader(CommandName commandName) throws WebApplicationException {
CommandModel model = getCommandModel(commandName);
return getCommandRunner().getHelp(model);
}
private CommandRunner getCommandRunner() {
if (this.commandRunner == null) {
commandRunner = getHabitat().getService(CommandRunner.class);
}
return this.commandRunner;
}
private ServiceLocator getHabitat() {
return Globals.getDefaultHabitat();
}
private String getServerName() {
if (serverName == null) {
Server server = getHabitat().getService(Server.class, ServerEnvironment.DEFAULT_INSTANCE_NAME);
if (server != null) {
serverName = server.getName();
}
}
return serverName;
}
private Subject getSubject() {
return subjectRef.get();
}
private static class CommandName {
private String scope;
private String name;
public CommandName(String scope, String name) {
this.scope = scope;
this.name = name;
}
public CommandName(String fullName) {
if (fullName == null) {
return;
}
int ind = fullName.indexOf('/');
if (ind > 0) {
this.scope = fullName.substring(0, ind + 1);
this.name = fullName.substring(ind + 1);
} else {
this.name = fullName;
}
}
public String getName() {
return name;
}
public String getScope() {
return scope;
}
@Override
public String toString() {
if (this.scope == null) {
return "CommandName[" + name + "]";
} else {
return "CommandName[" + scope + name + "]";
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy