All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.admin.rest.resources.custom.SystemPropertiesCliResource Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2015 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.
 */
// Portions Copyright [2016-2021] [Payara Foundation and/or its affiliates]

package org.glassfish.admin.rest.resources.custom;

import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.PathSegment;
import jakarta.ws.rs.core.Response;

import com.sun.enterprise.config.serverbeans.Cluster;
import com.sun.enterprise.config.serverbeans.Domain;
import com.sun.enterprise.config.serverbeans.Server;

import org.glassfish.admin.rest.resources.TemplateExecCommand;
import org.glassfish.admin.rest.results.ActionReportResult;
import org.glassfish.admin.rest.results.OptionsResult;
import org.glassfish.admin.rest.utils.ResourceUtil;
import org.glassfish.admin.rest.utils.xml.RestActionReporter;
import org.glassfish.api.ActionReport;
import org.glassfish.api.ActionReport.ExitCode;
import org.glassfish.api.admin.CommandLock;
import org.glassfish.api.admin.ExecuteOn;
import org.glassfish.api.admin.ParameterMap;
import org.glassfish.api.admin.RuntimeType;
import org.glassfish.config.support.CommandTarget;
import org.glassfish.config.support.TargetType;
import org.glassfish.hk2.api.ServiceLocator;
import org.jvnet.hk2.config.Dom;

/**
 *
 * @author jasonlee
 */
@Produces({MediaType.TEXT_HTML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.APPLICATION_FORM_URLENCODED})
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.APPLICATION_FORM_URLENCODED})
@CommandLock(CommandLock.LockType.NONE)
@ExecuteOn({RuntimeType.DAS})
@TargetType(value = {CommandTarget.CLUSTER, CommandTarget.CLUSTERED_INSTANCE, CommandTarget.CONFIG, CommandTarget.DAS,
    CommandTarget.DOMAIN, CommandTarget.STANDALONE_INSTANCE})
public class SystemPropertiesCliResource extends TemplateExecCommand {
    protected static final String TAG_SYSTEM_PROPERTY = "system-property";
    private static final String VALUE = "value";
    private static final String DEFAULT_VALUE = "defaultValue";
    private static final String DESCRIPTION = "description";


    @Context
    protected ServiceLocator injector;

    protected Dom entity;

    protected Domain domain;

    public SystemPropertiesCliResource() {
        super(SystemPropertiesCliResource.class.getSimpleName(), "", "", "", "", true);
    }

    public void setEntity(Dom p) {
        entity = p;
    }

    public Dom getEntity() {
        return entity;
    }

    @GET
    public ActionReportResult get() {
        domain = locatorBridge.getRemoteLocator().getService(Domain.class);

        ParameterMap data = new ParameterMap();
        processCommandParams(data);
        addQueryString(uriInfo.getQueryParameters(), data);
        adjustParameters(data);
        Map> properties = new TreeMap>();

        RestActionReporter actionReport = new RestActionReporter();
        getSystemProperties(properties, getEntity(), false);

        actionReport.getExtraProperties().put("systemProperties", new ArrayList<>(properties.values()));
        if (properties.isEmpty()) {
            actionReport.getTopMessagePart().setMessage("Nothing to list."); // i18n
        }
        return new ActionReportResult(commandName, actionReport, new OptionsResult());
    }

    @POST
    public Response create(Map data) {
        return saveProperties(data);
    }

    @PUT
    public Response update(Map data) {
        data.entrySet().removeIf(entry -> entry.getValue() == null || entry.getValue().isEmpty());
        Response resp = deleteRemovedProperties(data.keySet());
        return (resp == null) ? saveProperties(data) : resp;
    }

    @Path("{Name}/")
    @POST
    public Response getSystemPropertyResource(@PathParam("Name") String id, Map data) {
        data.put(id, data.get(VALUE));
        data.remove(VALUE);
        List segments = uriInfo.getPathSegments(true);
        String grandParent = segments.get(segments.size() - 3).getPath();

        return saveProperties(grandParent, data);
    }

    @Path("{Name}/")
    @DELETE
    public Response deleteSystemProperty(@PathParam("Name") String id, HashMap data) {
        List segments = uriInfo.getPathSegments(true);
        String grandParent = segments.get(segments.size() - 3).getPath();

        return deleteProperty(grandParent, id);
    }

    protected void getSystemProperties(Map> properties, Dom dom, boolean getDefaults) {
        List sysprops;
        synchronized (dom) {
            sysprops = dom.nodeElements(TAG_SYSTEM_PROPERTY);
        }
        if ((sysprops != null) && (!sysprops.isEmpty())) {
            for (Dom sysprop : sysprops) {
                String name = sysprop.attribute("name");
                Map currValue = properties.get(name);
                if (currValue == null) {
                    currValue = new HashMap();
                    currValue.put("name", name);
                    currValue.put(getDefaults ? DEFAULT_VALUE : VALUE, sysprop.attribute(VALUE));

                    if (sysprop.attribute(DESCRIPTION) != null) {
                        currValue.put(DESCRIPTION, sysprop.attribute(DESCRIPTION));
                    }
                    properties.put(name, currValue);
                } else {
                    // Only add a default value if there isn't one already
                    if (currValue.get(DEFAULT_VALUE) == null) {
                        currValue.put(DEFAULT_VALUE, sysprop.attribute(VALUE));
                    }
                }
            }
        }

        // Figure out how to recurse
        if (dom.getProxyType().equals(Server.class)) {
            // Clustered instance
            if (((Server)dom.createProxy()).getCluster() != null) {
                getSystemProperties(properties, getCluster(dom.parent().parent(), ((Server)dom.createProxy()).getCluster().getName()), true);
            } else {
                // Standalone instance or DAS
                getSystemProperties(properties, getConfig(dom.parent().parent(), dom.attribute("config-ref")), true);
            }
        } else if (dom.getProxyType().equals(Cluster.class)) {
            getSystemProperties(properties, getConfig(dom.parent().parent(), dom.attribute("config-ref")), true);
        }
    }

    protected Dom getCluster(Dom domain, String clusterName) {
        List configs;
        Dom clusterElements;
        synchronized (domain) {
            clusterElements = domain.nodeElements("clusters").get(0);
        }
        synchronized (clusterElements) {
            configs = clusterElements.nodeElements("cluster");
        }
        for(Dom config : configs) {
            if (config.attribute("name").equals(clusterName)) {
                return config;
            }
        }
        return null;
    }

    protected Dom getConfig(Dom domain, String configName) {
        Dom rootConfig;
        List configs;
        synchronized (domain) {
            rootConfig = domain.nodeElements("configs").get(0);
        }
        synchronized (rootConfig) {
            configs = rootConfig.nodeElements("config");
        }
        for(Dom config : configs) {
            if (config.attribute("name").equals(configName)) {
                return config;
            }
        }
        return null;
    }

    /**
     * Saves the passed map of system properties. Any entry with a null or empty
     * value will be deleted with the delete-system-property command, and the rest
     * will then be created with create-system-properties.
     *
     * @param data a map of properties to create or delete
     * @return the result of the command
     */
    protected Response saveProperties(Map data) {
        return saveProperties(null, data);
    }

    /**
     * Saves the passed map of system properties. Any entry with a null or empty
     * value will be deleted with the delete-system-property command, and the rest
     * will then be created with create-system-properties.
     *
     * @param parent the name of the parent object of the target
     * @param data   a map of properties to create or delete
     * @return the result of the command
     */
    protected Response saveProperties(String parent, Map data) {

        // Prepare all empty properties for explicit deletion
        Collection emptyProps = new HashSet<>();
        Iterator> dataIterator = data.entrySet().iterator();
        while (dataIterator.hasNext()) {
            Entry dataEntry = dataIterator.next();
            String value = dataEntry.getValue();
            if (value == null || value.isEmpty()) {
                dataIterator.remove();
                emptyProps.add(dataEntry.getKey());
            }
        }

        // Delete the prepared properties for deletion
        if (!emptyProps.isEmpty()) {
            Response response = deleteProperties(parent, emptyProps);
            if (data.isEmpty() || response.getStatus() != HttpURLConnection.HTTP_OK) {
                return response;
            }
        }

        return createProperties(null, data);
    }

    /**
     * Create some system properties using the create-system-properties asadmin
     * command.
     *
     * @param parent the name of the parent object of the target
     * @param data   a map of properties to create
     * @return the result of the command
     */
    protected Response createProperties(String parent, Map data) {
        String propertiesString = convertPropertyMapToString(data);

        data = new HashMap();
        data.put("DEFAULT", propertiesString);
        data.put("target", (parent == null) ? getParent(uriInfo) : parent);

        RestActionReporter actionReport = ResourceUtil.runCommand("create-system-properties", data, getSubject());
        ActionReport.ExitCode exitCode = actionReport.getActionExitCode();
        ActionReportResult results = new ActionReportResult(commandName, actionReport, new OptionsResult());

        int status = HttpURLConnection.HTTP_OK; /*200 - ok*/
        if (exitCode == ActionReport.ExitCode.FAILURE) {
            status = HttpURLConnection.HTTP_INTERNAL_ERROR;
        }

        return Response.status(status).entity(results).build();
    }

    /**
     * Delete some system properties using the delete-system-property asadmin
     * command and aggregating the results.
     *
     * @param parent    the name of the parent object of the target
     * @param propNames the names of the properties to delete
     * @return the result of the commands
     */
    protected Response deleteProperties(String parent, Collection propNames) {
        int status = HttpURLConnection.HTTP_OK;

        // Prepare a report for aggregating results
        RestActionReporter report = new RestActionReporter();
        report.setActionExitCode(ExitCode.SUCCESS);
        report.setMessage("");

        for (String propName : propNames) {
            // Delete the property
            Response response = deleteProperty(null, propName);

            ActionReportResult result = (ActionReportResult) response.getEntity();
            ActionReport resultReport = result.getActionReport();
            ExitCode responseExitCode = resultReport.getActionExitCode();

            // Put the results into the aggregator report
            if (!responseExitCode.equals(ExitCode.SUCCESS)) {
                report.setActionExitCode(responseExitCode);
                if (responseExitCode.equals(ExitCode.FAILURE)) {
                    status = HttpURLConnection.HTTP_INTERNAL_ERROR;
                }
            }
            report.getExtraProperties().putAll(resultReport.getExtraProperties());
            report.setMessage((report.getMessage() + "\n" + resultReport.getMessage()).trim());
            if (response.getStatus() != HttpURLConnection.HTTP_OK){
                return response;
            }
        }

        return Response.status(status).entity(report).build();
    }

    /**
     * Delete a system property using the delete-system-property asadmin command.
     *
     * @param parent   the name of the parent object of the target
     * @param propName the name of the property to delete
     * @return the result of the command
     */
    protected Response deleteProperty(String parent, String propName) {
        ParameterMap pm = new ParameterMap();
        pm.add("DEFAULT", propName);
        pm.add("target", (parent == null) ? getParent(uriInfo) : parent);

        RestActionReporter actionReport = ResourceUtil.runCommand("delete-system-property", pm, getSubject());
        ActionReport.ExitCode exitCode = actionReport.getActionExitCode();
        ActionReportResult results = new ActionReportResult(commandName, actionReport, new OptionsResult());

        int status = HttpURLConnection.HTTP_OK; /*200 - ok*/
        if (exitCode == ActionReport.ExitCode.FAILURE) {
            status = HttpURLConnection.HTTP_INTERNAL_ERROR;
        }
        return Response.status(status).entity(results).build();
    }

    /**
     * Delete any properties not contained in the passed collection.
     *
     * @param newProps the properties to not delete
     * @return null if successful or a Response which contains the error message
     */
    private Response deleteRemovedProperties(Collection newProps) {
        List existingList = new ArrayList<>();
        Dom parent = getEntity();
        if (parent == null) {
            return null;
        }
        List existingProps;
        synchronized (parent) {
            existingProps = parent.nodeElements(TAG_SYSTEM_PROPERTY);
        }
        for (Dom existingProp : existingProps) {
            existingList.add(existingProp.attribute("name"));
        }
        //no existing properites,return null
        if (existingList.isEmpty()){
            return null;
        }

        //delete the props thats no longer in the new list.
        for(String onePropName : existingList){
            if (!newProps.contains(onePropName)){
                Response resp = deleteProperty(null, onePropName);
                if (resp.getStatus() != HttpURLConnection.HTTP_OK){
                    return resp;
                }
            }
        }
        return null;
    }

    protected String convertPropertyMapToString(Map data) {
        StringBuilder options = new StringBuilder();
        String sep = "";
        for (Map.Entry entry : data.entrySet()) {
            final String value = entry.getValue();
            if ((value != null) && !value.isEmpty()) {
                options.append(sep)
                        .append(entry.getKey())
                        .append("=")
                        .append(value.replaceAll(":", "\\\\:").replaceAll("=", "\\\\="));
                sep = ":";
            }
        }

        return options.toString();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy