| /* |
| * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package com.sun.enterprise.config.serverbeans; |
| |
| import com.sun.enterprise.config.serverbeans.customvalidators.NotTargetKeyword; |
| import com.sun.enterprise.config.serverbeans.customvalidators.NotDuplicateTargetName; |
| import com.sun.enterprise.config.util.ConfigApiLoggerInfo; |
| import com.sun.enterprise.util.LocalStringManagerImpl; |
| import com.sun.enterprise.util.net.NetUtils; |
| import com.sun.enterprise.util.StringUtils; |
| import com.sun.logging.LogDomains; |
| import org.glassfish.api.Param; |
| import org.glassfish.api.admin.AdminCommandContext; |
| import org.glassfish.api.admin.ServerEnvironment; |
| import org.glassfish.config.support.*; |
| import static org.glassfish.config.support.Constants.*; |
| |
| import org.jvnet.hk2.annotations.Service; |
| import org.glassfish.hk2.api.PerLookup; |
| import org.glassfish.hk2.api.ServiceLocator; |
| import org.jvnet.hk2.config.*; |
| import org.glassfish.api.admin.config.Named; |
| import org.glassfish.api.admin.config.ReferenceContainer; |
| |
| import jakarta.inject.Inject; |
| import jakarta.validation.Payload; |
| import java.beans.PropertyVetoException; |
| import java.io.File; |
| import java.util.List; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import jakarta.validation.constraints.Pattern; |
| |
| /** |
| * A cluster defines a homogeneous set of server instances that share the same applications, resources, and |
| * configuration. |
| */ |
| @Configured |
| @SuppressWarnings("unused") |
| @NotDuplicateTargetName(message = "{node.duplicate.name}", payload = Node.class) |
| public interface Node extends ConfigBeanProxy, Named, ReferenceContainer, RefContainer, Payload { |
| /** |
| * Sets the node name |
| * |
| * @param value node name |
| * @throws PropertyVetoException if a listener vetoes the change |
| */ |
| @Param(name = "name", primary = true) |
| @Override |
| public void setName(String value) throws PropertyVetoException; |
| |
| @NotTargetKeyword(message = "{node.reserved.name}", payload = Node.class) |
| @Pattern(regexp = NAME_SERVER_REGEX, message = "{node.invalid.name}", payload = Node.class) |
| @Override |
| public String getName(); |
| |
| /** |
| * points to the parent directory of the node(s) directory. |
| * |
| * @return path location of node-dir |
| */ |
| @Attribute |
| String getNodeDir(); |
| |
| /** |
| * Sets the value of the node-dir, top-level parent directory of node(s) |
| * |
| * @param value allowed object is {@link String } |
| * @throws PropertyVetoException if a listener vetoes the change |
| */ |
| @Param(name = "nodedir", optional = true) |
| void setNodeDir(String value) throws PropertyVetoException; |
| |
| /** |
| * points to a named host. |
| * |
| * @return a named host name |
| */ |
| @Attribute |
| @Pattern(regexp = NAME_REGEX, message = "{nodehost.invalid.name}", payload = Node.class) |
| String getNodeHost(); |
| |
| /** |
| * Sets the value of the name property. |
| * |
| * @param value allowed object is {@link String } |
| * @throws PropertyVetoException if a listener vetoes the change |
| */ |
| @Param(name = "nodehost", optional = true) |
| void setNodeHost(String value) throws PropertyVetoException; |
| |
| /** |
| * points to a GlassFish installation root |
| * |
| * @return value of install-dir |
| */ |
| @Attribute |
| String getInstallDir(); |
| |
| /** |
| * Sets the value of install-dir, the GlassFish installation root. |
| * |
| * @param value allowed object is {@link String } |
| * @throws PropertyVetoException if a listener vetoes the change |
| */ |
| @Param(name = "installdir", optional = true) |
| void setInstallDir(String value) throws PropertyVetoException; |
| |
| @Attribute() |
| String getType(); |
| |
| /** |
| * Sets the value of type of this node. |
| * |
| * @param value allowed object is {@link String } |
| * @throws PropertyVetoException if a listener vetoes the change |
| */ |
| @Param(name = "type") |
| void setType(String value) throws PropertyVetoException; |
| |
| /** |
| * specifies the windows domain if applicable |
| * |
| * @return the Windows domain name. |
| */ |
| @Attribute |
| @Pattern(regexp = NAME_REGEX, message = "{windowsdomain.invalid.name}", payload = Node.class) |
| String getWindowsDomain(); |
| |
| /** |
| * Sets the value of the windows domain property. |
| * |
| * @param value allowed object is {@link String } |
| * @throws PropertyVetoException if a listener vetoes the change |
| */ |
| @Param(name = "windowsdomain", optional = true) |
| void setWindowsDomain(String value) throws PropertyVetoException; |
| |
| /** |
| * true if node is frozen and we should not allow new instances to be created on the nod. |
| * |
| * @return "true" if node is frozen |
| */ |
| @Attribute(defaultValue = "false", dataType = Boolean.class) |
| String getFreeze(); |
| |
| /** |
| * Sets the value of the freeze |
| * |
| * @param value "true" to freeze node and not allow instances to be created |
| * |
| * @throws PropertyVetoException if a listener vetoes the change |
| */ |
| void setFreeze(String value) throws PropertyVetoException; |
| |
| @Element |
| SshConnector getSshConnector(); |
| |
| void setSshConnector(SshConnector connector); |
| |
| /** |
| * Returns the install dir with separators as forward slashes. This is needed to run commands over SSH tools on Windows |
| * where the backslashes are interpruted as escape chars. |
| * |
| * @return the install dir with separators as forward slashes |
| */ |
| @DuckTyped |
| String getInstallDirUnixStyle(); |
| |
| /** |
| * Returns the node dir with separators as forward slashes. This is needed to run commands over SSH tools on Windows |
| * where the backslashes are interpruted as escape chars. |
| * |
| * @return the node dir with separators as forward slashes |
| */ |
| @DuckTyped |
| String getNodeDirUnixStyle(); |
| |
| /** |
| * Returns the node dir as an absolute path. If the node dir path in the Node element is relative this will make it |
| * absolute relative to the node's installdir. |
| * |
| * @return the node's nodedir as an absolute path. Null if no nodedir. |
| */ |
| @DuckTyped |
| String getNodeDirAbsolute(); |
| |
| @DuckTyped |
| String getNodeDirAbsoluteUnixStyle(); |
| |
| /** |
| * Is a node being used by any server instance? |
| * |
| * @return true if node is referenced by any server instance, else false. |
| */ |
| @DuckTyped |
| boolean nodeInUse(); |
| |
| /** |
| * True if this is the default local node. Example: localhost-domain1 |
| * |
| * @return |
| */ |
| @DuckTyped |
| boolean isDefaultLocalNode(); |
| |
| /** |
| * True if the node's nodeHost is local to this |
| * |
| * @return |
| */ |
| @DuckTyped |
| boolean isLocal(); |
| |
| /** |
| * Does the node allow instance creation? |
| * |
| * @return true if node allows instance creation, else false |
| */ |
| @DuckTyped |
| boolean instanceCreationAllowed(); |
| |
| class Duck { |
| public static String getInstallDirUnixStyle(Node node) { |
| String installDir = node.getInstallDir(); |
| if (installDir == null) |
| return null; |
| return installDir.replaceAll("\\\\", "/"); |
| } |
| |
| public static String getNodeDirUnixStyle(Node node) { |
| String nodeDir = node.getNodeDir(); |
| if (nodeDir == null) |
| return null; |
| return nodeDir.replaceAll("\\\\", "/"); |
| } |
| |
| public static String getNodeDirAbsolute(Node node) { |
| // If nodedir is relative make it absolute relative to installRoot |
| String nodeDir = node.getNodeDir(); |
| if (nodeDir == null || nodeDir.length() == 0) |
| return null; |
| File nodeDirFile = new File(nodeDir); |
| if (nodeDirFile.isAbsolute()) { |
| return nodeDir; |
| } |
| // node-dir is relative. Make it absolute. We root it under the |
| // GlassFish root install directory. |
| String installDir = node.getInstallDir(); |
| File installRootFile = new File(installDir, "glassfish"); |
| File absoluteNodeDirFile = new File(installRootFile, nodeDir); |
| return absoluteNodeDirFile.getPath(); |
| } |
| |
| public static String getNodeDirAbsoluteUnixStyle(Node node) { |
| String nodeDirAbsolute = getNodeDirAbsolute(node); |
| if (nodeDirAbsolute == null) |
| return null; |
| return nodeDirAbsolute.replaceAll("\\\\", "/"); |
| } |
| |
| public static boolean isDefaultLocalNode(Node node) { |
| Dom serverDom = Dom.unwrap(node); |
| Domain domain = serverDom.getHabitat().getService(Domain.class); |
| if (node.getName().equals("localhost-" + domain.getName())) { |
| return true; |
| } |
| return false; |
| } |
| |
| public static boolean isLocal(Node node) { |
| // Short circuit common case for efficiency |
| Dom serverDom = Dom.unwrap(node); |
| Domain domain = serverDom.getHabitat().getService(Domain.class); |
| if (node.getName().equals("localhost-" + domain.getName())) { |
| return true; |
| } |
| String nodeHost = node.getNodeHost(); |
| if (nodeHost == null || nodeHost.length() == 0) { |
| return false; |
| } |
| return NetUtils.isThisHostLocal(nodeHost); |
| } |
| |
| public static boolean nodeInUse(Node node) { |
| //check if node is referenced by an instance |
| String nodeName = node.getName(); |
| Dom serverDom = Dom.unwrap(node); |
| Servers servers = serverDom.getHabitat().getService(Servers.class); |
| List<Server> serverList = servers.getServer(); |
| if (serverList != null) { |
| for (Server server : serverList) { |
| if (nodeName.equals(server.getNodeRef())) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public static boolean instanceCreationAllowed(Node node) { |
| return !Boolean.parseBoolean(node.getFreeze()); |
| } |
| } |
| |
| @Service |
| @PerLookup |
| class Decorator implements CreationDecorator<Node> { |
| @Param(name = "nodedir", optional = true) |
| String nodedir = null; |
| @Param(name = "nodehost", optional = true) |
| String nodehost = null; |
| @Param(name = "installdir", optional = true) |
| String installdir = null; |
| @Param(name = "type") |
| String type = null; |
| @Param(name = "sshport", optional = true) |
| String sshPort = null; |
| @Param(name = "sshnodehost", optional = true) |
| String sshHost = null; |
| @Param(name = "sshuser", optional = true) |
| String sshuser = null; |
| @Param(name = "sshkeyfile", optional = true) |
| String sshkeyfile; |
| @Param(name = "sshpassword", optional = true) |
| String sshpassword; |
| @Param(name = "sshkeypassphrase", optional = true) |
| String sshkeypassphrase; |
| @Param(name = "windowsdomain", optional = true) |
| String windowsdomain; |
| @Inject |
| ServiceLocator habitat; |
| @Inject |
| ServerEnvironment env; |
| @Inject |
| Domain domain; |
| @Inject |
| Nodes nodes; |
| |
| /** |
| * Decorates the newly CRUD created cluster configuration instance. tasks : - ensures that it references an existing |
| * configuration - creates a new config from the default-config if no config-ref was provided. - check for deprecated |
| * parameters. |
| * |
| * @param context administration command context |
| * @param instance newly created configuration element |
| * @throws TransactionFailure |
| * @throws PropertyVetoException |
| */ |
| @Override |
| public void decorate(AdminCommandContext context, final Node instance) throws TransactionFailure, PropertyVetoException { |
| |
| LocalStringManagerImpl localStrings = new LocalStringManagerImpl(Node.class); |
| |
| /* 16034: see if instance creation is turned off on node */ |
| if (!nodes.nodeCreationAllowed()) { |
| throw new TransactionFailure( |
| localStrings.getLocalString("nodeCreationNotAllowed", "Node creation is disabled. No new nodes may be created.")); |
| } |
| // If these options were passed a value of the empty string then |
| // we want to make sure they are null in the Node. The |
| // admin console often passes the empty string instead of null. |
| // See bug 14873 |
| if (!StringUtils.ok(nodedir)) |
| instance.setNodeDir(null); |
| if (!StringUtils.ok(installdir)) |
| instance.setInstallDir(null); |
| if (!StringUtils.ok(nodehost)) |
| instance.setNodeHost(null); |
| if (!StringUtils.ok(windowsdomain)) |
| instance.setWindowsDomain(null); |
| |
| //only create-node-ssh and update-node-ssh should be changing the type to SSH |
| instance.setType(type); |
| |
| if (type.equals("CONFIG")) |
| return; |
| |
| SshConnector sshC = instance.createChild(SshConnector.class); |
| |
| SshAuth sshA = sshC.createChild(SshAuth.class); |
| if (StringUtils.ok(sshuser)) |
| sshA.setUserName(sshuser); |
| if (StringUtils.ok(sshkeyfile)) |
| sshA.setKeyfile(sshkeyfile); |
| if (StringUtils.ok(sshpassword)) |
| sshA.setPassword(sshpassword); |
| if (StringUtils.ok(sshkeypassphrase)) |
| sshA.setKeyPassphrase(sshkeypassphrase); |
| sshC.setSshAuth(sshA); |
| |
| if (StringUtils.ok(sshPort)) |
| sshC.setSshPort(sshPort); |
| |
| if (StringUtils.ok(sshHost)) |
| sshC.setSshHost(sshHost); |
| |
| instance.setSshConnector(sshC); |
| } |
| } |
| |
| @Service |
| @PerLookup |
| class DeleteDecorator implements DeletionDecorator<Nodes, Node> { |
| @Inject |
| private Domain domain; |
| @Inject |
| Nodes nodes; |
| @Inject |
| Servers servers; |
| @Inject |
| private ServerEnvironment env; |
| |
| @Override |
| public void decorate(AdminCommandContext context, Nodes parent, Node child) throws PropertyVetoException, TransactionFailure { |
| Logger logger = ConfigApiLoggerInfo.getLogger(); |
| LocalStringManagerImpl localStrings = new LocalStringManagerImpl(Node.class); |
| String nodeName = child.getName(); |
| |
| if (nodeName.equals("localhost-" + domain.getName())) { // can't delete localhost node |
| final String msg = localStrings.getLocalString("Node.localhost", "Cannot remove Node {0}. ", child.getName()); |
| |
| logger.log(Level.SEVERE, ConfigApiLoggerInfo.cannotRemoveNode, child.getName()); |
| throw new TransactionFailure(msg); |
| } |
| |
| List<Node> nodeList = nodes.getNode(); |
| |
| // See if any servers are using this node |
| List<Server> serversOnNode = servers.getServersOnNode(child); |
| int n = 0; |
| if (serversOnNode != null && serversOnNode.size() > 0) { |
| StringBuilder sb = new StringBuilder(); |
| for (Server server : serversOnNode) { |
| if (n > 0) |
| sb.append(", "); |
| sb.append(server.getName()); |
| n++; |
| } |
| |
| final String msg = localStrings.getLocalString("Node.referencedByInstance", |
| "Node {0} referenced in server instance(s): {1}. Remove instances before removing node.", child.getName(), |
| sb.toString()); |
| logger.log(Level.SEVERE, ConfigApiLoggerInfo.referencedByInstance, new Object[] { child.getName(), sb.toString() }); |
| throw new TransactionFailure(msg); |
| } |
| |
| nodeList.remove(child); |
| } |
| } |
| } |