Package com.sun.enterprise.admin.servermgmt.services

Source Code of com.sun.enterprise.admin.servermgmt.services.SMFService

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 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 com.sun.enterprise.admin.servermgmt.services;

import com.sun.enterprise.universal.process.ProcessManager;
import com.sun.enterprise.universal.process.ProcessManagerException;
import com.sun.enterprise.util.OS;
import static com.sun.enterprise.util.StringUtils.ok;
import com.sun.enterprise.util.SystemPropertyConstants;
import com.sun.enterprise.util.ProcessExecutor;
import com.sun.enterprise.util.io.FileUtils;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.universal.io.SmartFile;
import com.sun.enterprise.util.io.ServerDirs;

import java.io.File;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Arrays;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Properties;
import static com.sun.enterprise.admin.servermgmt.services.Constants.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/** Represents the SMF Service.
* Holds the tokens and their values that are consumed by the SMF templates. The recommended
* way to use this class (or its instances) is to initialize it with default constructor
* and then apply various mutators to configure the service. Finally, callers should
* make sure that the configuration is valid, before attempting to create the service in
* the Solaris platform.
* @since SJSAS 9.0
* @see #isConfigValid
* @see SMFServiceHandler
* @author Kedar Mhaswade
*/
public final class SMFService extends ServiceAdapter {
    public static final String TIMEOUT_SECONDS_DV = "0";
    public static final String AS_ADMIN_USER_DEF_VAL = "admin";
    public static final String SP_DELIMITER = ":";
    public static final String PRIVILEGES_DEFAULT_VAL = "basic";
    public static final String NETADDR_PRIV_VAL = "net_privaddr";
    public static final String BASIC_NETADDR_PRIV_VAL = PRIVILEGES_DEFAULT_VAL + "," + NETADDR_PRIV_VAL;
    public static final String START_INSTANCES_TN = "START_INSTANCES";
    public static final String START_INSTANCES_DEFAULT_VAL = Boolean.TRUE.toString();
    public static final String NO_START_INSTANCES_PROPERTY = "startinstances=false";
    public static final String SVCCFG = "/usr/sbin/svccfg";
    public static final String SVCADM = "/usr/sbin/svcadm";
    public static final String MANIFEST_HOME = "/var/svc/manifest/application/GlassFish/";
    private static final String NULL_VALUE = "null";
    private static final StringManager sm = StringManager.getManager(SMFService.class);
    private static final String nullArgMsg = sm.getString("null_arg");
    private static final String MANIFEST_FILE_SUFFIX = "Domain-service-smf.xml";
    private static final String MANIFEST_FILE_TEMPL_SUFFIX = MANIFEST_FILE_SUFFIX + ".template";
    private static final String REL_PATH_TEMPLATES = "lib/install/templates";

    /**
     * Creates SMFService instance. All the tokens are initialized to default values.
     * Callers must verify that the tokens are properly token-replaced before
     * using this instance.
     */
    SMFService(ServerDirs dirs, AppserverServiceType type) {
        super(dirs, type);
        if (!apropos()) {
            throw new IllegalArgumentException("Internal Error: SMFService constructor called but SMF is not available.");
        }
        init_old_delete_me();
    }

    static boolean apropos() {
        // suggested by smf-discuss forum on OpenSolaris
        return OS.isSun() && new File(SVCADM).isFile();
    }

    /** Creates the service on the given platform.
     */
    @Override
    public void createServiceInternal() {
        boolean success = false;
        boolean previousManifestExists = new File(getManifestFilePath()).exists();
        try {
            isConfigValid(); //safe, throws exception if not valid
            if (info.trace)
                printOut(toString());
            validateManifest(getManifestFilePath());
            previousManifestExists = false;
            ServicesUtils.tokenReplaceTemplateAtDestination(
                    tokensAndValues(),
                    getManifestFileTemplatePath(),
                    getManifestFilePath());
            validateService();
            success = importService();
        }
        catch (final Exception e) {
            if (!success && !previousManifestExists) {
                cleanupManifest();
            }
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteServiceInternal() {
        try {
            String serviceName = info.serviceName;
            if (!ok(serviceName))
                throw new RuntimeException(Strings.get("internal.error", "no service name is set"));
            String me = System.getProperty("user.name");
            StringBuilder sb = new StringBuilder();
            if (!isUserSmfAuthorized(me, sb))
                throw new RuntimeException(Strings.get("noSmfAuth", me, sb.toString()));
            ProcessManager pm = new ProcessManager(SVCADM, "disable", info.serviceName);
            pm.setEcho(false);
            pm.execute();
            pm = new ProcessManager(SVCCFG, "delete", info.serviceName);
            pm.setEcho(false);
            pm.execute();
        }
        catch (RuntimeException re) {
            throw re;
        }
        catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    private void checkServiceName() {
        if (serviceNameExists(info.smfFullServiceName)) {
            final String msg = sm.getString("serviceNameExists", info.smfFullServiceName);
            throw new IllegalArgumentException(msg);
        }
    }

    /** Returns timeout in seconds before the master boot restarter should
     * give up starting this service.
     */
    public int getTimeoutSeconds() {
        final int to = Integer.parseInt(getTokenMap().get(TIMEOUT_SECONDS_TN));
        return (to);
    }

    /** Sets timeout in seconds before the master boot restarter should
     * give up starting this service.
     * @param number a non-negative integer representing timeout. A value of zero implies infinite timeout.
     */
    public void setTimeoutSeconds(final int number) {
        Integer to = Integer.valueOf(number);
        if (to < 0) {
            final String msg = sm.getString("invalidTO", number);
            throw new IllegalArgumentException(msg);
        }
        getTokenMap().put(TIMEOUT_SECONDS_TN, to.toString());
    }

    /** Sets the OS-level user-id who should start and own the processes started
     * by this service. This user is the same as the value returned by
     * System.getProperty("user.name"). The idea is that the method is
     * called by the user who actually wants to own the service.
     * @throws IllegalArgumentException if the user can not modify MANIFEST_HOME
     * @throws IllegalArgumentException if solaris.smf.modify Authorization is not implied by the authorizations available for the user.
     */
    private void checkOSUser() {
        String msg;
        if (!canCreateManifest()) {
            msg = sm.getString("noPermissionToCreateManifest", info.osUser, MANIFEST_HOME);
            throw new IllegalArgumentException(msg);
        }
        final StringBuilder auths = new StringBuilder();
        if (!isUserSmfAuthorized(info.osUser, auths)) {
            msg = sm.getString("noSmfAuth", info.osUser, auths);
            throw new IllegalArgumentException(msg);
        }
    }

    /** Returns the additional properties of the Service.
     * @return String representing addtional properties of the service. May return default properties as well.
     */
    public String getServiceProperties() {
        return (getTokenMap().get(PRIVILEGES_TN));
    }

    /** Sets the additional service properties that are specific to it.
     * @param must be a colon separated String, if not null. No effect, if null is passed.
     */
    public void setServiceProperties(final String cds) {
        /* For now, we have to take care of only net_privaddr privilege property.
         * Additional properties will result in additional tokens being replaced.
         * A null value for parameter results in setting the basic privilege property.
         */
        if (cds != null) {
            final Set<String> props = ps2Pairs(cds);
            if (props.contains(NETADDR_PRIV_VAL)) {
                getTokenMap().put(PRIVILEGES_TN, BASIC_NETADDR_PRIV_VAL); // you get both basic, netaddr_priv
            }
            if (props.contains(NO_START_INSTANCES_PROPERTY)) {
                getTokenMap().put(START_INSTANCES_TN, Boolean.FALSE.toString());
            }
        }
    }

    /** Determines if the configuration of the method is valid. When this class
     * is constructed, appropriate defaults are used. But before attempting to create
     * the service in the Solaris platform, it is important that the necessary
     * configuration is done by the users via various mutator methods of this class.
     * This method must be called to guard against some abnormal failures before
     * creating the service. It makes sure that the caller has set all the necessary
     * parameters reasonably. Note that it does not validate the actual values.
     * @throws RuntimeException if the configuration is not valid
     * @return true if the configuration is valid, an exception is thrown otherwise
     */
    public boolean isConfigValid() {
        final Set<String> keys = getTokenMap().keySet();
        for (final String k : keys) {
            final boolean aNullValue = NULL_VALUE.equals(getTokenMap().get(k));
            if (aNullValue) {
                final String msg = sm.getString("smfTokenNeeded", k, getTokenMap().get(k));
                throw new RuntimeException(msg);
            }
        }
        final File mf = new File(getManifestFileTemplatePath());
        if (!mf.exists()) {
            final String msg = sm.getString("serviceTemplateNotFound", getManifestFileTemplatePath());
            throw new RuntimeException(msg);
        }

        // bnevins May 27, 2009
        // passwordfile is now optional for start-domain
        // BEFORE:  --user %%%AS_ADMIN_USER%%% --passwordfile %%%PASSWORD_FILE_PATH%%%
        // AFTER:   %%%CREDENTIALS%%%


        return (true);
    }

    /** Returns the tokens and values of the service as a map.
     * Note that a copy is returned.
     * @return a copy of tokens and values
     */
    public Map<String, String> tokensAndValues() {
        return (new HashMap<String, String>(getTokenMap())); //send only copy
    }

    /** Returns the absolute location of the manifest file as SMF understands it.
     * It takes into account the name, type and configuration location of the
     * service. It is expected that these are set before calling this method.
     * If the <b> Fully Qualified Service Name </b> is invalid, a RuntimeException results.
     */
    public String getManifestFilePath() {
        final String fn = new StringBuilder().append(MANIFEST_HOME).append(info.fqsn).append("/").append(MANIFEST_FILE_SUFFIX).toString();
        return (fn);
    }

    /** Returns the absolute location of the template for the given service.
     * If the file can not be found at its required location then the file will be
     * copied from inside this jar file to the file system.
     * The type of the service must be set before calling this method, otherwise
     * a runtime exception results.
     */
    public String getManifestFileTemplatePath() {
        String ir = System.getProperty(SystemPropertyConstants.INSTALL_ROOT_PROPERTY);
        if (!ok(ir))
            throw new RuntimeException("Internal Error - System Property not set: "
                    + SystemPropertyConstants.INSTALL_ROOT_PROPERTY);

        File rootDir = SmartFile.sanitize(new File(ir));
        if (!rootDir.isDirectory())
            throw new RuntimeException("Internal Error - Not a directory: " + rootDir);

        File templatesDir = new File(rootDir, REL_PATH_TEMPLATES);
        String filename = MANIFEST_FILE_TEMPL_SUFFIX;

        File f = new File(templatesDir, filename);
        return f.getPath();
    }

    private static boolean ok(String s) {
        return s != null && s.length() > 0;
    }

    /** Returns a String representation of the SMFService. It contains a new-line
    separated "name=value" String that contains the name and value of each of
    of the tokens that were set in the service.
    @return a String according to above description, never returns null
     */
    public String toString() {
        /* toString method useful for debugging */
        final StringBuilder sb = new StringBuilder();
        final String[] ka = new String[getTokenMap().size()];
        Arrays.sort(getTokenMap().keySet().toArray(ka));
        for (final String n : ka) {
            sb.append(n).append("=").append(getTokenMap().get(n)).append(System.getProperty("line.separator"));
        }
        return (sb.toString());
    }

    /** For safety -- this is similar to the subversion dry-run command.
     * It does everything except create the service.
     */
    public String getSuccessMessage() {
        String msg = Strings.get("SMFServiceCreated", info.smfFullServiceName, info.type.toString(),
                info.serverDirs.getServerParentDir(), getManifestFilePath(), info.serviceName);

        if (info.dryRun) {
            msg += Strings.get("dryrun");
        }

        return msg;
    }

    ////////////////////// PRIVATE METHODS ////////////////////
    @Override
    public void initializeInternal() {
        checkOSUser();
        checkServiceName();
    }

    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
    // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
    private void init_old_delete_me() {
        // ?????
        getTokenMap().put(START_INSTANCES_TN, START_INSTANCES_DEFAULT_VAL);
        getTokenMap().put(AS_ADMIN_USER_TN, AS_ADMIN_USER_DEF_VAL);
        getTokenMap().put(TIMEOUT_SECONDS_TN, TIMEOUT_SECONDS_DV);
        getTokenMap().put(PRIVILEGES_TN, BASIC_NETADDR_PRIV_VAL);
        getTokenMap().put(CREDENTIALS_TN, " ");
    }

    private Set<String> ps2Pairs(final String cds) {
        final StringTokenizer p = new StringTokenizer(cds, SP_DELIMITER);
        final Set<String> tokens = new HashSet<String>();
        while (p.hasMoreTokens()) {
            tokens.add(p.nextToken());
        }
        return (tokens);
    }

    private boolean canCreateManifest() {
        final File mh = new File(MANIFEST_HOME);
        boolean ok = true;
        if (!mh.exists()) {
            ok = mh.mkdirs();
        }
        if (ok) {
            if (!mh.canWrite()) {
                ok = false;
            }
        }
        return (ok);
    }

    private boolean isUserSmfAuthorized(final String user, final StringBuilder auths) {
        boolean authorized = false;
        String path2Auths = "auths";
        String at = ",";
        final String AUTH1 = "solaris.*";
        final String AUTH2 = "solaris.smf.*";
        final String AUTH3 = "solaris.smf.modify";
        if (System.getProperty("PATH_2_AUTHS") != null)
            path2Auths = System.getProperty("PATH_2_AUTHS");
        if (System.getProperty("AUTH_TOKEN") != null)
            at = System.getProperty("AUTH_TOKEN");
        try {
            final String[] cmd = new String[]{path2Auths, user};
            ProcessExecutor pe = new ProcessExecutor(cmd);
            pe.setExecutionRetentionFlag(true);
            pe.execute();
            auths.append(pe.getLastExecutionOutput());
            final StringTokenizer st = new StringTokenizer(pe.getLastExecutionOutput(), at);
            while (st.hasMoreTokens()) {
                String t = st.nextToken();
                if (t != null)
                    t = t.trim();
                if (AUTH1.equals(t) || AUTH2.equals(t) || AUTH3.equals(t)) {
                    authorized = true;
                    break;
                }
            }
            return (authorized);
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private boolean fileContainsToken(final String path, final String t, final Map<String, String> tv) throws RuntimeException {
        BufferedInputStream bis = null;
        try {
            boolean present = false;
            bis = new BufferedInputStream(new FileInputStream(path));
            final Properties p = new Properties();
            p.load(bis);
            if (p.containsKey(t)) {
                tv.put(t, (String) p.get(t));
                present = true;
            }
            return (present);
        }
        catch (final Exception e) {
            throw new RuntimeException(e);
        }
        finally {
            if (bis != null) {
                try {
                    bis.close();
                }
                catch (Exception ee) {
                    IGNORE_EXCEPTION(ee);
                }
            }
        }
    }

    private static void IGNORE_EXCEPTION(final Exception e) {
        // ignore
    }

    private boolean serviceNameExists(final String sn) {
        boolean exists = false;
        try {
            final String[] cmd = new String[]{"/usr/bin/svcs", sn};
            ProcessExecutor pe = new ProcessExecutor(cmd);
            pe.setExecutionRetentionFlag(true);
            pe.execute();
            exists = true;
        }
        catch (final Exception e) {
            //returns a non-zero status -- the service does not exist, status is already set
        }
        return (exists);
    }

    private void validateManifest(final String manifestPath) throws Exception {
        final File manifest = new File(manifestPath);
        final File manifestParent = manifest.getParentFile();
        final String msg = sm.getString("smfLeftoverFiles", manifest.getParentFile().getAbsolutePath());

        if (manifestParent != null && manifestParent.isDirectory()) {

            if (info.force) {
                FileUtils.whack(manifestParent);

                if (manifestParent != null && manifestParent.isDirectory()) {
                    throw new IllegalArgumentException(msg);
                }
            }
            else {
                throw new IllegalArgumentException(msg);
            }
        }
        manifest.getParentFile().mkdirs();
        if (info.trace)
            printOut("Manifest validated: " + manifestPath);
    }

    private void validateService() throws Exception {
        final String[] cmda = new String[]{SMFService.SVCCFG, "validate", getManifestFilePath()};
        final ProcessExecutor pe = new ProcessExecutor(cmda);
        pe.execute();
        if (info.trace)
            printOut("Validated the SMF Service: " + info.fqsn + " using: " + SMFService.SVCCFG);
    }

    private boolean importService() throws Exception {
        final String[] cmda = new String[]{SMFService.SVCCFG, "import", getManifestFilePath()};
        final ProcessExecutor pe = new ProcessExecutor(cmda);

        if (info.dryRun)
            cleanupManifest();
        else
            pe.execute(); //throws ExecException in case of an error

        if (info.trace)
            printOut("Imported the SMF Service: " + info.fqsn);
        return (true);
    }

    private void cleanupManifest() throws RuntimeException {
        final File manifest = new File(getManifestFilePath());
        if (manifest.exists()) {
            manifest.delete();
            manifest.deleteOnExit();
            if (info.trace)
                printOut("Attempted deleting failed service manifest: " + manifest.getAbsolutePath());
        }
        final File failedServiceNode = manifest.getParentFile();
        if (failedServiceNode.exists()) {
            failedServiceNode.delete();
            failedServiceNode.deleteOnExit();
            if (info.trace)
                printOut("Attempted deleting failed service folder: " + failedServiceNode.getAbsolutePath());
        }
    }

    private void printOut(final String s) {
        System.out.println(s);
    }

    // duplicated
    // todo -- fix the filename!!
    // todo
    // todo
    public void writeReadmeFile(String msg) {
        File f = new File(getServerDirs().getServerDir(), README);
        ServicesUtils.appendTextToFile(f, msg);
    }

    @Override
    public final String getLocationArgsStart() {
        if (isDomain())
            return " --domaindir " + getServerDirs().getServerParentDir().getPath() + " ";
        else
            return " --nodedir " + getServerDirs().getServerGrandParentDir().getPath()
                    + " --node " + getServerDirs().getServerParentDir().getName() + " ";
    }

    @Override
    public final String getLocationArgsStop() {
        return getLocationArgsStart()// same with SMF
    }
}
TOP

Related Classes of com.sun.enterprise.admin.servermgmt.services.SMFService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.