| /* |
| * 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 org.glassfish.loadbalancer.config; |
| |
| import org.glassfish.loadbalancer.config.customvalidators.RefConstraint; |
| import org.glassfish.loadbalancer.config.customvalidators.RefValidator; |
| import java.util.List; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Properties; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import java.beans.PropertyVetoException; |
| |
| import com.sun.enterprise.util.LocalStringManagerImpl; |
| import com.sun.enterprise.config.serverbeans.Domain; |
| import com.sun.enterprise.config.serverbeans.Ref; |
| import com.sun.enterprise.config.serverbeans.ServerRef; |
| import com.sun.enterprise.config.serverbeans.ClusterRef; |
| import com.sun.logging.LogDomains; |
| import java.util.Date; |
| |
| import org.glassfish.config.support.*; |
| import org.glassfish.quality.ToDo; |
| |
| |
| import org.jvnet.hk2.annotations.Service; |
| import org.glassfish.hk2.api.PerLookup; |
| import org.jvnet.hk2.config.*; |
| import org.jvnet.hk2.config.types.Property; |
| import org.jvnet.hk2.config.types.PropertyBag; |
| |
| import org.glassfish.api.Param; |
| import org.glassfish.api.admin.AdminCommandContext; |
| import org.glassfish.api.admin.config.PropertiesDesc; |
| |
| import jakarta.inject.Inject; |
| import jakarta.validation.constraints.NotNull; |
| import jakarta.validation.constraints.Min; |
| import jakarta.validation.constraints.Pattern; |
| import static org.glassfish.config.support.Constants.*; |
| import jakarta.validation.Payload; |
| |
| /** |
| * |
| */ |
| |
| /* @XmlType(name = "", propOrder = { |
| "clusterRefOrServerRef", |
| "property" |
| }) */ |
| |
| @Configured |
| @RefConstraint(message="{ref.invalid}", payload= RefValidator.class) |
| public interface LbConfig extends ConfigBeanProxy, PropertyBag, Payload { |
| |
| String LAST_APPLIED_PROPERTY = "last-applied"; |
| String LAST_EXPORTED_PROPERTY = "last-exported"; |
| |
| /** |
| * Gets the value of the name property. |
| * |
| * Name of the load balancer configuration |
| * |
| * @return possible object is |
| * {@link String } |
| */ |
| @Attribute(key=true) |
| @Pattern(regexp=NAME_REGEX, message="{lbconfig.invalid.name}", payload=LbConfig.class) |
| @NotNull |
| String getName(); |
| |
| /** |
| * Sets the value of the name property. |
| * |
| * @param value allowed object is |
| * {@link String } |
| */ |
| @Param (name = "name", primary=true) |
| void setName(String value) throws PropertyVetoException; |
| |
| /** |
| * Gets the value of the responseTimeoutInSeconds property. |
| * |
| * Period within which a server must return a response or otherwise it will |
| * be considered unhealthy. Default value is 60 seconds. Must be greater |
| * than or equal to 0. A value of 0 effectively turns off this check |
| * functionality, meaning the server will always be considered healthy |
| * |
| * @return possible object is |
| * {@link String } |
| */ |
| @Attribute (defaultValue="60") |
| @Min(value=0) |
| String getResponseTimeoutInSeconds(); |
| |
| /** |
| * Sets the value of the responseTimeoutInSeconds property. |
| * |
| * @param value allowed object is |
| * {@link String } |
| */ |
| @Param(name = "responsetimeout", optional=true) |
| void setResponseTimeoutInSeconds(String value) throws PropertyVetoException; |
| |
| /** |
| * Gets the value of the httpsRouting property. |
| * |
| * Boolean flag indicating how load-balancer will route https requests. |
| * If true then an https request to the load-balancer will result in an |
| * https request to the server; if false then https requests to the |
| * load-balancer result in http requests to the server. |
| * Default is to use http (i.e. value of false) |
| * |
| * @return possible object is |
| * {@link String } |
| */ |
| @Attribute (defaultValue="false",dataType=Boolean.class) |
| String getHttpsRouting(); |
| |
| /** |
| * Sets the value of the httpsRouting property. |
| * |
| * @param value allowed object is |
| * {@link String } |
| */ |
| @Param(name = "httpsrouting", optional=true) |
| void setHttpsRouting(String value) throws PropertyVetoException; |
| |
| /** |
| * Gets the value of the reloadPollIntervalInSeconds property. |
| * |
| * Maximum period, in seconds, that a change to the load balancer |
| * configuration file takes before it is detected by the load balancer and |
| * the file reloaded. A value of 0 indicates that reloading is disabled. |
| * Default period is 1 minute (60 sec) |
| * |
| * @return possible object is |
| * {@link String } |
| */ |
| @Attribute (defaultValue="60") |
| String getReloadPollIntervalInSeconds(); |
| |
| /** |
| * Sets the value of the reloadPollIntervalInSeconds property. |
| * |
| * @param value allowed object is |
| * {@link String } |
| */ |
| @Param(name = "reloadinterval", optional=true) |
| void setReloadPollIntervalInSeconds(String value) throws PropertyVetoException; |
| |
| /** |
| * Gets the value of the monitoringEnabled property. |
| * |
| * Boolean flag that determines whether monitoring is switched on or not. |
| * Default is that monitoring is switched off (false) |
| * |
| * @return possible object is |
| * {@link String } |
| */ |
| @Attribute (defaultValue="false",dataType=Boolean.class) |
| String getMonitoringEnabled(); |
| |
| /** |
| * Sets the value of the monitoringEnabled property. |
| * |
| * @param value allowed object is |
| * {@link String } |
| */ |
| @Param(name = "monitor", optional=true) |
| void setMonitoringEnabled(String value) throws PropertyVetoException; |
| |
| /** |
| * Gets the value of the routeCookieEnabled property. |
| * |
| * Boolean flag that determines whether a route cookie is or is not enabled. |
| * Default is enabled (true). |
| * |
| * @return possible object is |
| * {@link String } |
| */ |
| @Attribute (defaultValue="true",dataType=Boolean.class) |
| String getRouteCookieEnabled(); |
| |
| /** |
| * Sets the value of the routeCookieEnabled property. |
| * |
| * @param value allowed object is |
| * {@link String } |
| */ |
| @Param(name = "routecookie", optional=true) |
| void setRouteCookieEnabled(String value) throws PropertyVetoException; |
| |
| /** |
| * Gets the value of the clusterRefOrServerRef property. |
| * <p/> |
| * <p/> |
| * This accessor method returns a reference to the live list, |
| * not a snapshot. Therefore any modification you make to the |
| * returned list will be present inside the JAXB object. |
| * This is why there is not a <CODE>set</CODE> method for the clusterRefOrServerRef property. |
| * <p/> |
| * <p/> |
| * For example, to add a new item, do as follows: |
| * <pre> |
| * getClusterRefOrServerRef().add(newItem); |
| * </pre> |
| * <p/> |
| * <p/> |
| * <p/> |
| * Objects of the following type(s) are allowed in the list |
| * {@link ClusterRef } |
| * {@link ServerRef } |
| */ |
| @Element("*") |
| List<Ref> getClusterRefOrServerRef(); |
| |
| /** |
| Properties as per {@link PropertyBag} |
| */ |
| @Override |
| @ToDo(priority=ToDo.Priority.IMPORTANT, details="Provide PropertyDesc for legal props" ) |
| @PropertiesDesc(props={}) |
| @Element |
| List<Property> getProperty(); |
| |
| @DuckTyped |
| <T> List<T> getRefs(Class<T> type); |
| |
| @DuckTyped |
| <T> T getRefByRef(Class<T> type, String ref); |
| |
| @DuckTyped |
| Date getLastExported(); |
| |
| @DuckTyped |
| Date getLastApplied(); |
| |
| @DuckTyped |
| boolean setLastExported(); |
| |
| @DuckTyped |
| boolean setLastApplied(); |
| |
| public class Duck { |
| public static <T> List<T> getRefs(LbConfig lc, Class<T> type) { |
| List<T> refs = new ArrayList<T>(); |
| for (Object r : lc.getClusterRefOrServerRef()) { |
| if (type.isInstance(r)) { |
| refs.add(type.cast(r)); |
| } |
| } |
| // you have to return an umodifiable list since this list |
| // is not the real list of elements as maintained by this config bean |
| return Collections.unmodifiableList(refs); |
| } |
| |
| public static <T> T getRefByRef(LbConfig lc, Class<T> type, String ref) { |
| if (ref == null) { |
| return null; |
| } |
| |
| for (Ref r : lc.getClusterRefOrServerRef()) |
| if (type.isInstance(r) && r.getRef().equals(ref)) |
| return type.cast(r); |
| |
| return null; |
| } |
| |
| public static Date getLastExported(LbConfig lc) { |
| return getInternalPropertyValue(lc, LAST_EXPORTED_PROPERTY); |
| } |
| |
| public static Date getLastApplied(LbConfig lc) { |
| return getInternalPropertyValue(lc, LAST_APPLIED_PROPERTY); |
| } |
| |
| private static Date getInternalPropertyValue(LbConfig lc, |
| String propertyName) { |
| String propertyValue = lc.getPropertyValue(propertyName); |
| if(propertyValue == null){ |
| return null; |
| } |
| return new Date(Long.parseLong(propertyValue)); |
| } |
| |
| public static boolean setLastExported(LbConfig lc) { |
| return setInternalProperty(lc, LAST_EXPORTED_PROPERTY); |
| } |
| |
| public static boolean setLastApplied(LbConfig lc) { |
| return setInternalProperty(lc, LAST_APPLIED_PROPERTY); |
| } |
| |
| private static boolean setInternalProperty(LbConfig lc, |
| String propertyName) { |
| Property property = lc.getProperty(propertyName); |
| Transaction transaction = new Transaction(); |
| try { |
| if (property == null) { |
| ConfigBeanProxy lcProxy = transaction.enroll(lc); |
| property = lcProxy.createChild(Property.class); |
| property.setName(propertyName); |
| property.setValue(String.valueOf((new Date()).getTime())); |
| ((LbConfig)lcProxy).getProperty().add(property); |
| } else { |
| ConfigBeanProxy propertyProxy = transaction.enroll(property); |
| ((Property)propertyProxy).setValue(String.valueOf( |
| (new Date()).getTime())); |
| } |
| transaction.commit(); |
| } catch (Exception ex) { |
| transaction.rollback(); |
| Logger logger = LogDomains.getLogger(LbConfig.class, |
| LogDomains.ADMIN_LOGGER); |
| LocalStringManagerImpl localStrings = |
| new LocalStringManagerImpl(LbConfig.class); |
| String msg = localStrings.getLocalString( |
| "UnableToSetPropertyInLbconfig", |
| "Unable to set property {0} in lbconfig with name {1}", |
| new String[]{propertyName, lc.getName()}); |
| logger.log(Level.SEVERE, msg); |
| logger.log(Level.FINE, "Exception when trying to set property " |
| + propertyName + " in lbconfig " + lc.getName(), ex); |
| return false; |
| } |
| return true; |
| } |
| |
| } |
| |
| @Service |
| @PerLookup |
| class Decorator implements CreationDecorator<LbConfig> { |
| |
| @Param (name = "name", optional=true) |
| String config_name; |
| |
| @Param(optional=true) |
| String target; |
| |
| @Param (optional=true, defaultValue="60") |
| String responsetimeout; |
| |
| @Param (optional=true, defaultValue="false") |
| Boolean httpsrouting; |
| |
| @Param (optional=true, defaultValue="60") |
| String reloadinterval; |
| |
| @Param (optional=true, defaultValue="false") |
| Boolean monitor; |
| |
| @Param (optional=true, defaultValue="true") |
| Boolean routecookie; |
| |
| @Param(optional=true, name="property", separator=':') |
| Properties properties; |
| |
| @Inject |
| Domain domain; |
| |
| /** |
| * Create lb-config entries |
| * tasks : |
| * - ensures that it references an existing cluster |
| |
| * @param context administration command context |
| * @param instance newly created configuration element |
| * @throws TransactionFailure |
| * @throws PropertyVetoException |
| * |
| */ |
| @Override |
| public void decorate(AdminCommandContext context, final LbConfig instance) throws TransactionFailure, PropertyVetoException { |
| Logger logger = LogDomains.getLogger(LbConfig.class, LogDomains.ADMIN_LOGGER); |
| LocalStringManagerImpl localStrings = new LocalStringManagerImpl(LbConfig.class); |
| |
| if (config_name == null && target == null) { |
| String msg = localStrings.getLocalString("RequiredTargetOrConfig", "Neither LB config name nor target specified"); |
| throw new TransactionFailure(msg); |
| } |
| |
| // generate lb config name if not specified |
| if (config_name == null) { |
| config_name = target + "_LB_CONFIG"; |
| } |
| |
| LbConfigs lbconfigs = domain.getExtensionByType(LbConfigs.class); |
| //create load-balancers parent element if it does not exist |
| if (lbconfigs == null) { |
| Transaction transaction = new Transaction(); |
| try { |
| ConfigBeanProxy domainProxy = transaction.enroll(domain); |
| lbconfigs = domainProxy.createChild(LbConfigs.class); |
| ((Domain) domainProxy).getExtensions().add(lbconfigs); |
| transaction.commit(); |
| } catch (TransactionFailure ex) { |
| transaction.rollback(); |
| String msg = localStrings.getLocalString("LbConfigsCreationFailed", "Creation of parent element lb-configs failed"); |
| throw new TransactionFailure(msg, ex); |
| } catch (RetryableException ex) { |
| transaction.rollback(); |
| String msg = localStrings.getLocalString("LbConfigsCreationFailed", "Creation of parent element lb-configs failed"); |
| throw new TransactionFailure(msg, ex); |
| } |
| } |
| |
| if (lbconfigs.getLbConfig(config_name) != null) { |
| String msg = localStrings.getLocalString("LbConfigExists", config_name); |
| throw new TransactionFailure(msg); |
| } |
| |
| instance.setName(config_name); |
| instance.setResponseTimeoutInSeconds(responsetimeout); |
| instance.setReloadPollIntervalInSeconds(reloadinterval); |
| instance.setMonitoringEnabled(monitor==null ? null : monitor.toString()); |
| instance.setRouteCookieEnabled(routecookie==null ? null : routecookie.toString()); |
| instance.setHttpsRouting(httpsrouting==null ? null : httpsrouting.toString()); |
| |
| // creates a reference to the target |
| if (target != null) { |
| if (domain.getClusterNamed(target) != null) { |
| ClusterRef cRef = instance.createChild(ClusterRef.class); |
| cRef.setRef(target); |
| instance.getClusterRefOrServerRef().add(cRef); |
| } else if (domain.isServer(target)) { |
| ServerRef sRef = instance.createChild(ServerRef.class); |
| sRef.setRef(target); |
| instance.getClusterRefOrServerRef().add(sRef); |
| } else { |
| String msg = localStrings.getLocalString("InvalidTarget", target); |
| throw new TransactionFailure(msg); |
| } |
| } |
| |
| // add properties |
| if (properties != null) { |
| for (Object propname: properties.keySet()) { |
| Property newprop = instance.createChild(Property.class); |
| newprop.setName((String) propname); |
| newprop.setValue(properties.getProperty((String) propname)); |
| instance.getProperty().add(newprop); |
| } |
| } |
| logger.info(localStrings.getLocalString("http_lb_admin.LbConfigCreated", |
| "Load balancer configuration {0} created.", config_name)); |
| } |
| } |
| |
| @Service |
| @PerLookup |
| class DeleteDecorator implements DeletionDecorator<LbConfigs, LbConfig> { |
| @Inject |
| private Domain domain; |
| |
| @Override |
| public void decorate(AdminCommandContext context, LbConfigs parent, LbConfig child) |
| throws PropertyVetoException, TransactionFailure { |
| Logger logger = LogDomains.getLogger(LbConfig.class, LogDomains.ADMIN_LOGGER); |
| LocalStringManagerImpl localStrings = new LocalStringManagerImpl(LbConfig.class); |
| |
| String lbConfigName = child.getName(); |
| LbConfig lbConfig = domain.getExtensionByType(LbConfigs.class).getLbConfig(lbConfigName); |
| |
| //Ensure there are no refs |
| if ( (lbConfig.getClusterRefOrServerRef().size() != 0 ) ) { |
| String msg = localStrings.getLocalString("LbConfigNotEmpty", lbConfigName); |
| throw new TransactionFailure(msg); |
| } |
| logger.info(localStrings.getLocalString("http_lb_admin.LbConfigDeleted", lbConfigName)); |
| } |
| } |
| } |