diff --git a/openam-core/pom.xml b/openam-core/pom.xml
index 059c613b3c..d4c7059010 100755
--- a/openam-core/pom.xml
+++ b/openam-core/pom.xml
@@ -257,15 +257,8 @@
- org.apache.click
- click-nodeps
- ${click.version}
-
-
-
- org.apache.click
- click-extras
- ${click.version}
+ org.freemarker
+ freemarker
diff --git a/openam-core/src/main/java/com/sun/identity/config/DefaultSummary.java b/openam-core/src/main/java/com/sun/identity/config/DefaultSummary.java
index 7288a3f45a..6fccbdccc3 100644
--- a/openam-core/src/main/java/com/sun/identity/config/DefaultSummary.java
+++ b/openam-core/src/main/java/com/sun/identity/config/DefaultSummary.java
@@ -30,7 +30,6 @@
package com.sun.identity.config;
-import com.sun.identity.config.util.ProtectedPage;
import com.sun.identity.setup.AMSetupServlet;
import com.sun.identity.setup.AMSetupUtils;
import com.sun.identity.setup.HttpServletRequestWrapper;
@@ -39,17 +38,12 @@
import jakarta.servlet.http.HttpServletRequest;
import com.sun.identity.shared.Constants;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
-public class DefaultSummary extends ProtectedPage {
-
- public ActionLink createConfig =
- new ActionLink("createDefaultConfig", this, "createDefaultConfig");
-
- public void onInit() {
- super.onInit();
- }
-
+public class DefaultSummary extends ProtectedSetupPage {
+
+ @ConfiguratorAction
public boolean createDefaultConfig() {
HttpServletRequest req = getContext().getRequest();
HttpServletRequestWrapper request =
@@ -134,7 +128,6 @@ SetupConstants.CONFIG_VAR_BASE_DIR, getBaseDir(
debug.error("DefaultSummary.createDefaultConfig()", e);
}
writeToResponse( ((responseString!=null)?responseString:"") );
- setPath(null);
return false;
}
}
diff --git a/openam-core/src/main/java/com/sun/identity/config/Options.java b/openam-core/src/main/java/com/sun/identity/config/Options.java
index 8c4a3e63d8..5a6890e8ad 100644
--- a/openam-core/src/main/java/com/sun/identity/config/Options.java
+++ b/openam-core/src/main/java/com/sun/identity/config/Options.java
@@ -25,53 +25,45 @@
* $Id: Options.java,v 1.7 2009/01/05 23:17:09 veiming Exp $
*
* Portions Copyrighted 2011-2015 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems LLC.
*/
package com.sun.identity.config;
-import com.sun.identity.config.util.TemplatedPage;
import com.sun.identity.setup.AMSetupServlet;
import com.sun.identity.setup.EmbeddedOpenDS;
-import org.openidentityplatform.openam.click.control.ActionLink;
import org.forgerock.openam.upgrade.VersionUtils;
+import org.openidentityplatform.openam.config.servlet.SetupPage;
-public class Options extends TemplatedPage {
+public class Options extends SetupPage {
- public ActionLink createConfigLink = new ActionLink("upgradeLink", this, "upgrade" );
- public ActionLink testUrlLink = new ActionLink("coexistLink", this, "coexist" );
- public ActionLink pushConfigLink = new ActionLink("olderUpgradeLink", this, "olderUpgrade" );
+ // Unlike the wizard/summary pages, this has no onSecurityCheck() override: the old Options
+ // extended TemplatedPage (not ProtectedPage), so it stayed reachable even once OpenAM is
+ // configured - that's how a completed install reaches the upgrade-options branch below.
+ // SetupPage's default onSecurityCheck() (always true) already matches that.
- protected boolean upgrade = false;
- protected boolean upgradeCompleted = false;
- protected boolean isOpenDS1x = false;
- protected boolean debugOn = false;
-
- private java.util.Locale configLocale = null;
-
- protected String getTitle() {
- return isNewInstall() ? "configuration.options.title" : "upgrade.title";
- }
+ @Override
+ public void onInit() {
+ super.onInit();
+
+ boolean upgrade = !isNewInstall();
+ addModel("upgrade", Boolean.valueOf(upgrade));
- public void doInit() {
- upgrade = !isNewInstall();
- upgradeCompleted = AMSetupServlet.isUpgradeCompleted();
+ boolean upgradeCompleted = AMSetupServlet.isUpgradeCompleted();
addModel("upgradeCompleted", Boolean.valueOf(upgradeCompleted));
- addModel( "upgrade", Boolean.valueOf( upgrade ) );
if (upgrade) {
- addModel("currentVersion", VersionUtils.getCurrentVersion());
+ addModel("currentVersion", VersionUtils.getCurrentVersion());
}
- isOpenDS1x = EmbeddedOpenDS.isOpenDSVer1Installed();
+ boolean isOpenDS1x = EmbeddedOpenDS.isOpenDSVer1Installed();
addModel("isOpenDS1x", Boolean.valueOf(isOpenDS1x));
if (isOpenDS1x) {
addModel("odsdir", AMSetupServlet.getBaseDir());
}
-
- debugOn = getContext().getRequest().getParameter( "debug" ) != null;
-
- if (debugOn) {
+
+ if (getContext().getRequest().getParameter("debug") != null) {
AMSetupServlet.enableDebug();
}
}
diff --git a/openam-core/src/main/java/com/sun/identity/config/util/AjaxPage.java b/openam-core/src/main/java/com/sun/identity/config/util/AjaxPage.java
deleted file mode 100644
index 76697d76c0..0000000000
--- a/openam-core/src/main/java/com/sun/identity/config/util/AjaxPage.java
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved
- *
- * The contents of this file are subject to the terms
- * of the Common Development and Distribution License
- * (the License). You may not use this file except in
- * compliance with the License.
- *
- * You can obtain a copy of the License at
- * https://opensso.dev.java.net/public/CDDLv1.0.html or
- * opensso/legal/CDDLv1.0.txt
- * See the License for the specific language governing
- * permission and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL
- * Header Notice in each file and include the License file
- * at opensso/legal/CDDLv1.0.txt.
- * If applicable, add the following below the CDDL Header,
- * with the fields enclosed by brackets [] replaced by
- * your own identifying information:
- * "Portions Copyrighted [year] [name of copyright owner]"
- *
- * $Id: AjaxPage.java,v 1.24 2010/01/04 19:15:16 veiming Exp $
- *
- * Portions Copyrighted 2011-2016 ForgeRock AS.
- * Portions Copyrighted 2025 3A Systems LLC.
- */
-
-package com.sun.identity.config.util;
-
-import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*;
-
-import com.iplanet.am.util.SystemProperties;
-import com.sun.identity.config.SessionAttributeNames;
-import com.sun.identity.setup.AMSetupServlet;
-import com.sun.identity.setup.AMSetupUtils;
-import com.sun.identity.setup.SetupConstants;
-import com.sun.identity.shared.Constants;
-import com.sun.identity.shared.debug.Debug;
-import com.sun.identity.shared.locale.Locale;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.security.GeneralSecurityException;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-import java.util.concurrent.TimeUnit;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-import org.openidentityplatform.openam.click.Page;
-import org.openidentityplatform.openam.click.control.ActionLink;
-import org.forgerock.openam.ldap.LDAPRequests;
-import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.ConnectionFactory;
-import org.forgerock.opendj.ldap.LDAPConnectionFactory;
-import org.forgerock.opendj.ldap.LdapException;
-import org.forgerock.opendj.ldap.ResultCode;
-import org.forgerock.opendj.ldap.SSLContextBuilder;
-import org.forgerock.util.Options;
-import org.forgerock.util.time.Duration;
-
-public abstract class AjaxPage extends Page {
-
- public ActionLink checkPasswordsLink =
- new ActionLink("checkPasswords", this, "checkPasswords");
-
- public ActionLink validateInputLink =
- new ActionLink("validateInput", this, "validateInput" );
-
- public ActionLink resetSessionAttributesLink =
- new ActionLink("resetSessionAttributes", this,
- "resetSessionAttributes");
-
- public static final String RESPONSE_TEMPLATE = "{\"valid\":\"${valid}\", \"body\":\"${body}\"}";
- public static final String OLD_RESPONSE_TEMPLATE = "{\"isValid\":${isValid}, \"errorMessage\":\"${errorMessage}\"}";
-
- private static int MIN_PASSWORD_SIZE = 8;
- private boolean rendering = false;
- private String hostName;
- protected java.util.Locale configLocale = null;
-
- // localization properties
- protected ResourceBundle rb = null;
- protected static final String RB_NAME = "amConfigurator";
-
- public String responseString = "true";
- public static Debug debug = Debug.getInstance("amConfigurator");
-
- public AjaxPage() {
- }
-
- @Override
- public void onInit() {
- super.onInit();
- initializeResourceBundle();
- addModel("page", this);
- }
-
- public boolean isRendering() {
- return rendering;
- }
-
- protected String toString( String paramName ) {
- String value = getContext().getRequest().getParameter( paramName );
- value = ( value != null ? value.trim() : null );
- value = ("".equals(value) ? null : value );
- return value;
- }
-
- protected boolean toBoolean(String paramName) {
- String value = toString(paramName);
- if (value == null) {
- return false;
- }
- if ((value.equalsIgnoreCase("on")) ||
- (value.equalsIgnoreCase("true")) ||
- (value.equalsIgnoreCase("yes")) ||
- (value.equalsIgnoreCase("checked"))) {
- return true;
- } else {
- return false;
- }
- }
-
- protected int toInt( String paramName ) {
- int intValue = 0;
- String value = toString( paramName );
- if ( value != null ) {
- try {
- intValue = Integer.parseInt( value );
- } catch ( NumberFormatException e ) {}
- }
- return intValue;
- }
-
- protected void writeValid() {
- writeValid( null );
- }
- protected void writeValid( String message ) {
- String out = ( message != null ? message : "" );
- writeJsonResponse(true, out);
- }
- protected void writeInvalid( String message ) {
- String out = ( message != null ? message : "" );
- writeJsonResponse( false, out );
- }
-
- protected void writeJsonResponse(String valid, String responseBody) {
- String response = RESPONSE_TEMPLATE;
- response = response.replaceFirst("\\$\\{" + "valid" + "\\}", valid);
- response = response.replaceFirst("\\$\\{" + "body" + "\\}", responseBody);
- writeToResponse(response);
- }
- protected void writeJsonResponse(boolean valid, String responseBody) {
- String response = RESPONSE_TEMPLATE;
- response = response.replaceFirst("\\$\\{" + "valid" + "\\}", String.valueOf(valid));
- response = response.replaceFirst("\\$\\{" + "body" + "\\}", responseBody);
- writeToResponse(response);
- }
-
- protected Connection getConnection(String host, int port, String bindDN, char[] bindPwd, int timeout, boolean isSSl)
- throws GeneralSecurityException, LdapException {
- Options ldapOptions = Options.defaultOptions()
- .set(CONNECT_TIMEOUT, new Duration((long)timeout, TimeUnit.SECONDS))
- .set(AUTHN_BIND_REQUEST, LDAPRequests.newSimpleBindRequest(bindDN, bindPwd));
-
- if (isSSl) {
- String defaultProtocolVersion = SystemProperties.get(Constants.LDAP_SERVER_TLS_VERSION, "TLS");
- ldapOptions = ldapOptions.set(SSL_CONTEXT,
- new SSLContextBuilder().setProtocol(defaultProtocolVersion).getSSLContext());
- }
-
- ConnectionFactory factory = new LDAPConnectionFactory(host, port, ldapOptions);
- return factory.getConnection();
- }
-
- protected boolean writeErrorToResponse(ResultCode resultCode) {
- String msg = getMessage(resultCode);
- if (msg != null) {
- writeToResponse(getLocalizedString(msg));
- return true;
- }
- return false;
- }
-
- private String getMessage(ResultCode resultCode) {
- if (ResultCode.CLIENT_SIDE_CONNECT_ERROR.equals(resultCode)) {
- return "ldap.connect.error";
- } else if (ResultCode.CLIENT_SIDE_SERVER_DOWN.equals(resultCode)) {
- return "ldap.server.down";
- } else if (ResultCode.INVALID_DN_SYNTAX.equals(resultCode)) {
- return "ldap.invalid.dn";
- } else if (ResultCode.NO_SUCH_OBJECT.equals(resultCode)) {
- return "ldap.nosuch.object";
- } else if (ResultCode.INVALID_CREDENTIALS.equals(resultCode)) {
- return "ldap.invalid.credentials";
- } else if (ResultCode.UNWILLING_TO_PERFORM.equals(resultCode)) {
- return "ldap.unwilling";
- } else if (ResultCode.INAPPROPRIATE_AUTHENTICATION.equals(resultCode)) {
- return "ldap.inappropriate";
- } else if (ResultCode.CONSTRAINT_VIOLATION.equals(resultCode)) {
- return "ldap.constraint";
- } else {
- return null;
- }
- }
-
- protected void writeToResponse( String text ) {
- try {
- // Note: this writer is obtained from the Apache Click context. Should this be triggered by a JSP this
- // may not be in compliance with the JSP spec regarding mandatory use of the buffered JspWriter.
- getContext().getResponse().getWriter().write( text );
- this.rendering = true;
- } catch ( IOException e ) {
- throw new RuntimeException( e );
- }
- }
-
- public void initializeResourceBundle() {
- HttpServletRequest req =
- (HttpServletRequest)getContext().getRequest();
- HttpServletResponse res =
- (HttpServletResponse)getContext().getResponse();
-
- setLocale(req);
- res.setContentType("text/html; charset=UTF-8");
- }
-
- private void setLocale (HttpServletRequest request) {
- if (request != null) {
- String superLocale = request.getParameter("locale");
- if (superLocale != null && superLocale.length() > 0) {
- configLocale = Locale.getLocaleObjFromAcceptLangHeader(
- superLocale);
- } else {
- String acceptLangHeader =
- (String)request.getHeader("Accept-Language");
- if ((acceptLangHeader != null) &&
- (acceptLangHeader.length() > 0))
- {
- configLocale = Locale.getLocaleObjFromAcceptLangHeader(
- acceptLangHeader);
- } else {
- configLocale = java.util.Locale.getDefault();
- }
- }
- try {
- rb = ResourceBundle.getBundle(RB_NAME, configLocale);
- } catch (MissingResourceException mre) {
- // do nothing
- }
- }
- }
-
- public String getQuoteEscapedLocalizedString(String i18nKey) {
- String value = getLocalizedString(i18nKey);
- return value.replace("'", "\\'");
- }
-
- public String getLocalizedString(String i18nKey) {
- if (rb == null) {
- initializeResourceBundle();
- }
-
- String localizedValue = null;
- try {
- localizedValue = Locale.getString(rb, i18nKey, debug);
- } catch (MissingResourceException mre) {
- // do nothing
- }
- return (localizedValue == null) ? i18nKey : localizedValue;
- }
-
- public String getHostName() {
- if (hostName == null) {
- hostName = getContext().getRequest().getServerName();
- }
- return hostName;
- }
-
- public String getHostName(String serverUrl, String defaultHostName) {
- URL url = null;
-
- try {
- url = new URL(serverUrl);
- } catch (MalformedURLException mue) {
- return defaultHostName;
- }
-
- return url.getHost();
- }
-
- public int getServerPort(String serverUrl, int defaultPort) {
- URL url = null;
-
- try {
- url = new URL(serverUrl);
- } catch (MalformedURLException mue) {
- return defaultPort;
- }
-
- return url.getPort();
- }
-
- public String getBaseDir(HttpServletRequest req) {
- String basedir = AMSetupServlet.getPresetConfigDir();
- if ((basedir == null) || (basedir.length() == 0)) {
- String tmp = System.getProperty("user.home");
- if (File.separatorChar == '\\') {
- tmp = tmp.replace('\\', '/');
- }
- String uri = req.getRequestURI();
- int idx = uri.indexOf("/", 1);
- if (idx != -1) {
- uri = uri.substring(0, idx);
- }
-
- basedir = (tmp.endsWith("/")) ? tmp.substring(0, tmp.length()-1) :
- tmp;
- basedir += uri;
- }
-
- return basedir;
- }
-
- public String getCookieDomain() {
- return getHostName();
- }
-
- public boolean validateInput() {
- String key = toString("key");
- String value = toString("value");
-
- if (value == null) {
- responseString = "missing.required.field";
- } else {
- getContext().setSessionAttribute(key, value);
- }
-
- writeToResponse(getLocalizedString(responseString));
- setPath(null);
- return false;
- }
-
- public boolean resetSessionAttributes() {
- try {
- Field[] fields = SessionAttributeNames.class.getDeclaredFields();
- for (int i = 0; i < fields.length; i++) {
- try {
- getContext().removeSessionAttribute(
- (String)fields[i].get(null));
- } catch (IllegalAccessException e) {
- //ingore
- }
- }
- } catch (SecurityException e) {
- writeToResponse(e.getMessage());
- }
- setPath(null);
- return false;
- }
-
- public String getAttribute(String attr, String defaultValue) {
- String value = (String)getContext().getSessionAttribute(attr);
- return (value != null) ? value : defaultValue;
- }
-
- public String getAvailablePort(int portNumber) {
- return Integer.toString(
- AMSetupUtils.getFirstUnusedPort(getHostName(), portNumber, 1000));
- }
-
- public boolean checkPasswords() {
- String confirm = toString("confirm");
- String password = toString("password");
- String otherPassword = toString("otherPassword");
- String type = toString("type");
-
- if (password == null) {
- responseString = getLocalizedString("missing.password");
- } else if (password.length() < MIN_PASSWORD_SIZE) {
- responseString = getLocalizedString("password.size.invalid");
- } else if (confirm == null) {
- responseString = getLocalizedString("missing.confirm.password");
- } else if (confirm.length() < MIN_PASSWORD_SIZE) {
- responseString = getLocalizedString("password.size.invalid");
- } else if (!password.equals(confirm)) {
- responseString = getLocalizedString("password.dont.match");
- } else if ((otherPassword != null) && (otherPassword.equals(password))) {
- responseString = getLocalizedString("agent.admin.password.same");
- } else {
- if (type.equals("agent")) {
- type = SessionAttributeNames.CONFIG_VAR_AMLDAPUSERPASSWD;
- } else {
- type = SetupConstants.CONFIG_VAR_ADMIN_PWD;
- }
- getContext().setSessionAttribute(type, password);
- }
-
- writeToResponse(responseString);
- setPath(null);
- return false;
- }
-}
diff --git a/openam-core/src/main/java/com/sun/identity/config/util/TemplatedForm.java b/openam-core/src/main/java/com/sun/identity/config/util/TemplatedForm.java
deleted file mode 100644
index d9c03bc22c..0000000000
--- a/openam-core/src/main/java/com/sun/identity/config/util/TemplatedForm.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/**
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved
- *
- * The contents of this file are subject to the terms
- * of the Common Development and Distribution License
- * (the License). You may not use this file except in
- * compliance with the License.
- *
- * You can obtain a copy of the License at
- * https://opensso.dev.java.net/public/CDDLv1.0.html or
- * opensso/legal/CDDLv1.0.txt
- * See the License for the specific language governing
- * permission and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL
- * Header Notice in each file and include the License file
- * at opensso/legal/CDDLv1.0.txt.
- * If applicable, add the following below the CDDL Header,
- * with the fields enclosed by brackets [] replaced by
- * your own identifying information:
- * "Portions Copyrighted [year] [name of copyright owner]"
- *
- * $Id: TemplatedForm.java,v 1.4 2008/06/25 05:42:41 qcheng Exp $
- *
- */
-
-/*
- * Portions Copyrighted 2011 ForgeRock AS
- */
-
-package com.sun.identity.config.util;
-
-import org.apache.click.control.Field;
-import org.apache.click.control.Form;
-
-import java.util.Iterator;
-
-/**
- * @author Jeffrey Bermudez
- */
-public class TemplatedForm extends Form {
-
- public TemplatedForm(String name) {
- super(name);
- }
-
- public TemplatedForm() {
- super();
- }
-
- public boolean doProcess() {
- // We can overwrite this method to put some specific login here
- return true;
- }
-
- public final boolean onProcess() {
- super.onProcess();
-
- Iterator it = getFieldList().iterator();
- while (it.hasNext()) {
- if (!((Field) it.next()).onProcess()) {
- return false;
- }
- }
-
- return doProcess();
- }
-
-}
diff --git a/openam-core/src/main/java/com/sun/identity/config/util/TemplatedPage.java b/openam-core/src/main/java/com/sun/identity/config/util/TemplatedPage.java
deleted file mode 100644
index 2b6669aa5d..0000000000
--- a/openam-core/src/main/java/com/sun/identity/config/util/TemplatedPage.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright (c) 2007 Sun Microsystems Inc. All Rights Reserved
- *
- * The contents of this file are subject to the terms
- * of the Common Development and Distribution License
- * (the License). You may not use this file except in
- * compliance with the License.
- *
- * You can obtain a copy of the License at
- * https://opensso.dev.java.net/public/CDDLv1.0.html or
- * opensso/legal/CDDLv1.0.txt
- * See the License for the specific language governing
- * permission and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL
- * Header Notice in each file and include the License file
- * at opensso/legal/CDDLv1.0.txt.
- * If applicable, add the following below the CDDL Header,
- * with the fields enclosed by brackets [] replaced by
- * your own identifying information:
- * "Portions Copyrighted [year] [name of copyright owner]"
- *
- * $Id: TemplatedPage.java,v 1.10 2009/01/05 23:17:10 veiming Exp $
- *
- * Portions Copyrighted 2016 ForgeRock AS.
- */
-package com.sun.identity.config.util;
-
-import static org.forgerock.openam.utils.Time.*;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- *
- */
-public abstract class TemplatedPage extends AjaxPage {
-
- public static final String STATUS_MESSAGE_CODES_SESSION_KEY = "statusMessageCodes";
-
- public int currentYear = getCalendarInstance().get( Calendar.YEAR );
-
- public List statusMessages = new ArrayList(); //of Strings
-
- public String getTemplate() {
- return "assets/templates/main.html";
- }
-
- /**
- * Return the i18n code of the page title.
- * @return the i18n code of the page title;
- */
- protected abstract String getTitle();
-
-
- protected List getStatusMessageCodes() {
- return (List)getContext().getSessionAttribute(
- STATUS_MESSAGE_CODES_SESSION_KEY);
- }
-
- protected void addStatusMessageCode(String statusMessageCode) {
- List codes = getStatusMessageCodes();
- if (codes == null) {
- codes = new ArrayList();
- }
- codes.add(statusMessageCode);
- getContext().setSessionAttribute(
- STATUS_MESSAGE_CODES_SESSION_KEY, codes);
- }
-
- protected void clearStatusMessageCodes() {
- getContext().removeSessionAttribute(STATUS_MESSAGE_CODES_SESSION_KEY);
- }
-
- public void onInit() {
- super.onInit();
- addModel("title", getLocalizedString(getTitle()));
- List sessionStatusMessages = getStatusMessageCodes();
- if (sessionStatusMessages != null && !sessionStatusMessages.isEmpty()) {
- Iterator i = sessionStatusMessages.iterator();
- while ( i.hasNext() ) {
- String messageCode = (String)i.next();
- statusMessages.add(getLocalizedString(messageCode) );
- }
- clearStatusMessageCodes();
- }
- doInit();
- }
-
- protected void doInit() {
- //subclass implementation hook.
- }
-
-}
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/LDAPStoreWizardPage.java b/openam-core/src/main/java/com/sun/identity/config/wizard/LDAPStoreWizardPage.java
index 5536149cb5..26b33e7703 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/LDAPStoreWizardPage.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/LDAPStoreWizardPage.java
@@ -25,23 +25,22 @@
* $Id: LDAPStoreWizardPage.java,v 1.7 2008/06/25 05:42:42 qcheng Exp $
*
* Portions Copyrighted 2011-2014 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems LLC.
*/
package com.sun.identity.config.wizard;
import com.sun.identity.config.pojos.LDAPStore;
-import com.sun.identity.config.util.ProtectedPage;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
/*
* LDAPStoreWizardPage is the base for Step 3.
*/
-public class LDAPStoreWizardPage extends ProtectedPage {
+public class LDAPStoreWizardPage extends ProtectedSetupPage {
public LDAPStore store = null;
- public ActionLink clearLink =
- new ActionLink("clearStore", this, "clearStore");
private String type = "config";
private String typeTitle = "Configuration";
private String storeSessionName = "customConfigStore";
@@ -82,6 +81,7 @@ public void setStoreSessionName( String storeSessionName ) {
this.storeSessionName = storeSessionName;
}
+ @Override
public void onInit() {
addModel("type", getType());
addModel("typeTitle", getTypeTitle());
@@ -91,12 +91,13 @@ public void onInit() {
store = ensureConfig();
addModel("store", store);
- super.onInit();
+ super.onInit();
}
+ @ConfiguratorAction
public boolean clearStore() {
getContext().removeSessionAttribute( getStoreSessionName());
- setPath(null);
+ skipRender();
return false;
}
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/Step1.java b/openam-core/src/main/java/com/sun/identity/config/wizard/Step1.java
index 004f4c1635..d809bb30bc 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/Step1.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/Step1.java
@@ -25,32 +25,30 @@
* $Id: Step1.java,v 1.9 2009/01/05 23:17:10 veiming Exp $
*
* Portions Copyrighted 2011-2014 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems LLC.
*/
package com.sun.identity.config.wizard;
import com.sun.identity.config.SessionAttributeNames;
-import com.sun.identity.config.util.ProtectedPage;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
/**
* This is the first step in the advanced configuration flow.
* The user will be required to add the default admin password and
* the agent passwords.
*/
-public class Step1 extends ProtectedPage {
-
- // these two links required for client side validation calls.
- public ActionLink validateLink =
- new ActionLink("checkAdminPassword", this, "checkAdminPassword" );
- public ActionLink validateAgent =
- new ActionLink("checkAgentPassword", this, "checkAgentPassword" );
-
+public class Step1 extends ProtectedSetupPage {
+
+ // Not referenced by step1.htm/step1.ftl (the template posts to the base checkPasswords
+ // handler instead), but kept web-invokable for byte-identical behavior with the old Click page.
+ @ConfiguratorAction
public boolean checkAdminPassword() {
String adminPassword = toString("admin");
String adminConfirm = toString("adminConfirm");
-
- if (adminPassword == null || adminConfirm == null) {
+
+ if (adminPassword == null || adminConfirm == null) {
writeInvalid(getLocalizedString("missing.required.field"));
} else {
if (adminPassword.length() < 8) {
@@ -61,34 +59,32 @@ public boolean checkAdminPassword() {
writeValid("OK");
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_VAR_ADMIN_PWD, adminPassword);
- }
+ }
}
- setPath(null);
return false;
}
-
+
+ @ConfiguratorAction
public boolean checkAgentPassword() {
String agentPassword = toString("agent");
String agentConfirm = toString("agentConfirm");
- String tmpadmin = (String)getContext().getSessionAttribute(
+ String tmpadmin = (String) getContext().getSessionAttribute(
SessionAttributeNames.CONFIG_VAR_ADMIN_PWD);
-
- if (agentPassword == null || agentConfirm == null) {
+
+ if (agentPassword == null || agentConfirm == null) {
writeInvalid(getLocalizedString("missing.required.field"));
} else if (agentPassword.equals(tmpadmin)) {
writeInvalid(getLocalizedString("agent.admin.passwords.match"));
} else if (agentPassword.length() < 8) {
writeInvalid(getLocalizedString("invalid.password.length"));
} else if (!agentPassword.equals(agentConfirm)) {
- writeInvalid(getLocalizedString("passwords.do.not.match"));
+ writeInvalid(getLocalizedString("passwords.do.not.match"));
} else {
writeValid("OK");
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_VAR_AMLDAPUSERPASSWD,
- agentPassword);
+ agentPassword);
}
- setPath(null);
return false;
- }
+ }
}
-
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/Step2.java b/openam-core/src/main/java/com/sun/identity/config/wizard/Step2.java
index 34cc0ce668..f9753cf5f0 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/Step2.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/Step2.java
@@ -25,12 +25,12 @@
* $Id: Step2.java,v 1.15 2010/01/04 19:08:36 veiming Exp $
*
* Portions Copyrighted 2011-2016 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems LLC.
*/
package com.sun.identity.config.wizard;
import com.sun.identity.config.SessionAttributeNames;
-import com.sun.identity.config.util.ProtectedPage;
import com.sun.identity.setup.AMSetupServlet;
import com.sun.identity.setup.ServicesDefaultValues;
import com.sun.identity.setup.SetupConstants;
@@ -38,14 +38,11 @@
import java.net.MalformedURLException;
import java.net.URL;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
import org.forgerock.openam.utils.StringUtils;
-public class Step2 extends ProtectedPage {
- public ActionLink validateConfigDirLink =
- new ActionLink("validateConfigDir", this, "validateConfigDir");
- public ActionLink validateCookieDomainLink =
- new ActionLink("validateCookieDomain", this, "validateCookieDomain");
+public class Step2 extends ProtectedSetupPage {
@Override
public void onInit() {
@@ -88,9 +85,9 @@ public void onInit() {
if (!hasWritePermission(baseDir)) {
String deployURI = getContext().getServletContext().getContextPath();
- add("initialCheck", "" +
- getLocalizedString("configuration.wizard.step2.no.write.permission.to.basedir") +
+ getLocalizedString("configuration.wizard.step2.no.write.permission.to.basedir") +
"");
} else if (alreadyHasContent(baseDir)) {
String deployURI = getContext().getServletContext().getContextPath();
@@ -101,17 +98,17 @@ public void onInit() {
} else {
add("initialCheck", "");
}
-
+
super.onInit();
}
-
+
private static boolean alreadyHasContent(String dirName) {
- File f = new File(dirName);
-
+ File f = new File(dirName);
+
if (f.exists() && f.isDirectory()) {
return (f.list().length > 0);
}
-
+
return false;
}
@@ -122,10 +119,11 @@ private static boolean hasWritePermission(String dirName) {
}
return (f == null) ? false : f.isDirectory() && f.canWrite();
}
-
+
+ @ConfiguratorAction
public boolean validateConfigDir() {
String configDir = toString("dir");
-
+
if (configDir == null) {
writeToResponse(getLocalizedString("missing.required.field"));
} else if (!hasWritePermission(configDir)) {
@@ -139,10 +137,10 @@ public boolean validateConfigDir() {
SessionAttributeNames.CONFIG_DIR, configDir);
writeToResponse("true");
}
- setPath(null);
- return false;
+ return false;
}
+ @ConfiguratorAction
public boolean validateCookieDomain() {
String serverUrl = toString("serverurl");
String domain = toString("domain");
@@ -155,10 +153,9 @@ public boolean validateCookieDomain() {
getContext().setSessionAttribute(SessionAttributeNames.COOKIE_DOMAIN, domain);
writeToResponse("true");
}
- setPath(null);
return false;
}
-
+
private boolean mismatchedCookieDomain(String serverUrl, String domain) {
if (StringUtils.isNotEmpty(serverUrl) && StringUtils.isNotEmpty(domain)) {
try {
@@ -173,14 +170,14 @@ private boolean mismatchedCookieDomain(String serverUrl, String domain) {
}
private String getServerURL() {
- String hostname = (String)getContext().getRequest().getServerName();
- int portnum = (int)getContext().getRequest().getServerPort();
- String protocol = (String)getContext().getRequest().getScheme();
+ String hostname = getContext().getRequest().getServerName();
+ int portnum = getContext().getRequest().getServerPort();
+ String protocol = getContext().getRequest().getScheme();
return protocol + "://" + hostname + ":" + portnum;
}
/**
- * used to add the key to the page and to the session so it can
+ * used to add the key to the page and to the session so it can
* be retrieved when the final store is done
*/
private void add(String key, String value) {
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/Step3.java b/openam-core/src/main/java/com/sun/identity/config/wizard/Step3.java
index e2efd62131..ad14cc8c8a 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/Step3.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/Step3.java
@@ -42,8 +42,8 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
-import org.openidentityplatform.openam.click.Context;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorContext;
import org.forgerock.openam.ldap.LDAPUtils;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.DN;
@@ -52,48 +52,28 @@
import org.forgerock.opendj.ldap.ResultCode;
/**
- * Step 3 is for selecting the embedded or external configuration store
+ * Step 3 is for selecting the embedded or external configuration store
*/
public class Step3 extends LDAPStoreWizardPage {
public static final String LDAP_STORE_SESSION_KEY =
"wizardCustomConfigStore";
- public ActionLink validateSMHostLink =
- new ActionLink("validateSMHost", this, "validateSMHost");
- public ActionLink validateRootSuffixLink =
- new ActionLink("validateRootSuffix", this, "validateRootSuffix");
- public ActionLink setReplicationLink =
- new ActionLink("setReplication", this, "setReplication");
- public ActionLink validateHostNameLink =
- new ActionLink("validateHostName", this, "validateHostName");
- public ActionLink validateConfigStoreHost =
- new ActionLink("validateConfigStoreHost", this, "validateConfigStoreHost");
- public ActionLink setConfigType =
- new ActionLink("setConfigType", this, "setConfigType");
- public ActionLink validateLocalPortLink =
- new ActionLink("validateLocalPort", this, "validateLocalPort");
- public ActionLink validateLocalAdminPortLink =
- new ActionLink("validateLocalAdminPort", this, "validateLocalAdminPort");
- public ActionLink validateLocalJmxPortLink =
- new ActionLink("validateLocalJmxPort", this, "validateLocalJmxPort");
- public ActionLink validateEncKey =
- new ActionLink("validateEncKey", this, "validateEncKey");
-
private static final String QUOTE = "\"";
private static final String SEPARATOR = "\" : \"";
private String localRepPort;
-
+
public Step3() {
}
-
+
+ @Override
public void onInit() {
String val = getAttribute("rootSuffix", Wizard.defaultRootSuffix);
addModel("rootSuffix", val);
val = getAttribute("encryptionKey", AMSetupUtils.getRandomString());
addModel("encryptionKey", val);
-
+
val = getAttribute("configStorePort", getAvailablePort(50389));
addModel("configStorePort", val);
addModel("localConfigPort", val);
@@ -109,7 +89,7 @@ public void onInit() {
localRepPort = getAttribute("localRepPort", getAvailablePort(58989));
addModel("localRepPort", localRepPort);
- val = getAttribute("existingPort", getAvailablePort(50389));
+ val = getAttribute("existingPort", getAvailablePort(50389));
addModel("existingPort", val);
val = getAttribute("existingRepPort", getAvailablePort(58990));
@@ -117,7 +97,7 @@ public void onInit() {
val = getAttribute("configStoreSSL", "SIMPLE");
addModel("configStoreSSL", val);
-
+
if (val.equals("SSL")) {
addModel("selectConfigStoreSSL", "checked=\"checked\"");
} else {
@@ -126,10 +106,10 @@ public void onInit() {
// initialize the data store type being used
val = getAttribute(
- SetupConstants.CONFIG_VAR_DATA_STORE,
+ SetupConstants.CONFIG_VAR_DATA_STORE,
SetupConstants.SMS_EMBED_DATASTORE);
addModel(SetupConstants.CONFIG_VAR_DATA_STORE, val);
-
+
if (val.equals(SetupConstants.SMS_EMBED_DATASTORE)) {
addModel("selectEmbedded", "checked=\"checked\"");
addModel("selectExternal", "");
@@ -159,8 +139,9 @@ public void onInit() {
}
super.onInit();
- }
+ }
+ @ConfiguratorAction
public boolean setConfigType() {
String type = toString("type");
if (type.equals("remote")) {
@@ -170,21 +151,34 @@ public boolean setConfigType() {
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_STORE_HOST, "localhost");
}
- getContext().setSessionAttribute(
+ getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_VAR_DATA_STORE, type);
+ // NOTE: the old Click ActionLink returned `true` here with no setPath(null), which in the
+ // Click framework would go on to run onGet() + render the full step3 page into this AJAX
+ // response body. ConfiguratorServlet's actionLink dispatch never renders after invoking a
+ // handler (see ConfiguratorServlet.invokeAction), regardless of the method's return value.
+ // The callers (enableRemote/disableRemote in step3.ftl) are fire-and-forget - no callback
+ // reads the response - and Step3.onInit() has no session side effects beyond what is set
+ // above, so skipping that render is behaviorally invisible to the wizard. See
+ // docs/migration/click-to-freemarker/04-implementation-notes.md.
+ skipRender();
return true;
}
+ @ConfiguratorAction
public boolean setReplication() {
String type = toString("multi");
if (type.equals("enable")) {
type = SetupConstants.DS_EMP_REPL_FLAG_VAL;
- }
+ }
getContext().setSessionAttribute(
SessionAttributeNames.DS_EMB_REPL_FLAG, type);
+ // See the NOTE in setConfigType() above - same fire-and-forget, no-render situation.
+ skipRender();
return true;
}
+ @ConfiguratorAction
public boolean validateRootSuffix() {
String rootsuffix = toString("rootSuffix");
if ((rootsuffix == null) || (rootsuffix.trim().length() == 0)) {
@@ -197,13 +191,13 @@ public boolean validateRootSuffix() {
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_STORE_ROOT_SUFFIX, rootsuffix);
}
- setPath(null);
- return false;
+ return false;
}
-
+
+ @ConfiguratorAction
public boolean validateLocalPort() {
String port = toString("port");
-
+
if (port == null) {
writeToResponse(getLocalizedString("missing.required.field"));
} else {
@@ -243,11 +237,11 @@ public boolean validateLocalPort() {
writeToResponse(getLocalizedString("invalid.port.number"));
}
}
- setPath(null);
- return false;
+ return false;
}
- public boolean validateLocalAdminPort() {
+ @ConfiguratorAction
+ public boolean validateLocalAdminPort() {
String port = toString("port");
if (port == null) {
@@ -289,10 +283,10 @@ public boolean validateLocalAdminPort() {
writeToResponse(getLocalizedString("invalid.port.number"));
}
}
- setPath(null);
return false;
}
+ @ConfiguratorAction
public boolean validateLocalJmxPort() {
String port = toString("port");
@@ -335,7 +329,6 @@ public boolean validateLocalJmxPort() {
writeToResponse(getLocalizedString("invalid.port.number"));
}
}
- setPath(null);
return false;
}
@@ -343,6 +336,7 @@ public boolean validateLocalJmxPort() {
* Returns false always. Length of encryption key
* must be at least 10 chars.
*/
+ @ConfiguratorAction
public boolean validateEncKey() {
String key = toString("encKey");
@@ -357,16 +351,15 @@ public boolean validateEncKey() {
writeToResponse("true");
}
}
- setPath(null);
return false;
- }
+ }
+ @ConfiguratorAction
public boolean validateConfigStoreHost() {
String host = toString("configStoreHost");
if (host == null) {
writeToResponse(getLocalizedString("missing.required.field"));
- setPath(null);
return false;
} else {
getContext().setSessionAttribute("configStoreHost", host);
@@ -378,7 +371,6 @@ public boolean validateConfigStoreHost() {
} catch (UnknownHostException uhe) {
writeToResponse(getLocalizedString("contact.host.unknown"));
}
- setPath(null);
return false;
}
@@ -386,21 +378,22 @@ public boolean validateConfigStoreHost() {
* a call is made to the OpenAM url entered in the browser. If
* the OpenAM server
* exists a Map of data will be returned which contains the
- * information about the existing servers data store, including any
+ * information about the existing servers data store, including any
* replication ports if its embedded.
* Information to control the UI is returned in a JSON object of the form
- * {
- * "param1" : "value1",
+ * {
+ * "param1" : "value1",
* "param2" : "value2"
* }
* The JS on the browser will interpret the above and make the necessary
* changes to prompt the user for any more details required.
*/
+ @ConfiguratorAction
public boolean validateHostName() {
StringBuffer sb = new StringBuffer();
String hostName = toString("hostName");
-
- if (hostName == null) {
+
+ if (hostName == null) {
addObject(sb, "code", "100");
addObject(sb, "message",
getLocalizedString("missing.required.field"));
@@ -409,18 +402,18 @@ public boolean validateHostName() {
String admin = "amadmin";
String password = (String)getContext().getSessionAttribute(
SessionAttributeNames.CONFIG_VAR_ADMIN_PWD);
-
- try {
+
+ try {
String dsType;
Map data = AMSetupUtils.getRemoteServerInfo(hostName, admin, password);
-
+
// data returned from existing OpenAM server
- if (data != null && !data.isEmpty()) {
+ if (data != null && !data.isEmpty()) {
addObject(sb, "code", "100");
addObject(sb, "message", getLocalizedString("ok.string"));
-
+
setupDSParams(data);
-
+
String key = (String)data.get("enckey");
getContext().setSessionAttribute(
SessionAttributeNames.ENCRYPTION_KEY, key);
@@ -428,27 +421,27 @@ public boolean validateHostName() {
getContext().setSessionAttribute(
SessionAttributeNames.ENCLDAPUSERPASSWD,
(String)data.get("ENCLDAPUSERPASSWD"));
-
+
// true for embedded, false for ODSEE
- String embedded =
+ String embedded =
(String)data.get(BootstrapData.DS_ISEMBEDDED);
- addObject(sb, "embedded", embedded);
+ addObject(sb, "embedded", embedded);
String host = (String)data.get(BootstrapData.DS_HOST);
if (embedded.equals("true")) {
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_STORE_HOST, getHostName());
addObject(sb, "configStoreHost", getHostName());
-
- // set the multi embedded flag
+
+ // set the multi embedded flag
getContext().setSessionAttribute(
- SessionAttributeNames.CONFIG_VAR_DATA_STORE,
- SetupConstants.SMS_EMBED_DATASTORE);
-
+ SessionAttributeNames.CONFIG_VAR_DATA_STORE,
+ SetupConstants.SMS_EMBED_DATASTORE);
+
getContext().setSessionAttribute(
SessionAttributeNames.DS_EMB_REPL_FLAG,
- SetupConstants.DS_EMP_REPL_FLAG_VAL);
-
+ SetupConstants.DS_EMP_REPL_FLAG_VAL);
+
// get the existing replication ports if any
String replAvailable = (String)data.get(
BootstrapData.DS_REPLICATIONPORT_AVAILABLE);
@@ -475,10 +468,10 @@ public boolean validateHostName() {
SessionAttributeNames.CONFIG_STORE_PWD, password);
} else {
getContext().setSessionAttribute(
- SessionAttributeNames.CONFIG_STORE_PORT,
+ SessionAttributeNames.CONFIG_STORE_PORT,
(String)data.get(BootstrapData.DS_PORT));
getContext().setSessionAttribute(
- SessionAttributeNames.CONFIG_STORE_HOST, host);
+ SessionAttributeNames.CONFIG_STORE_HOST, host);
addObject(sb, "configStoreHost", host);
String dsprot = (String)data.get(
@@ -488,7 +481,7 @@ public boolean validateHostName() {
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_STORE_SSL, dsSSL);
addObject(sb, "configStoreSSL", dsSSL);
-
+
String dspwd = (String)data.get(BootstrapData.DS_PWD);
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_STORE_PWD,
@@ -509,7 +502,7 @@ public boolean validateHostName() {
getContext().setSessionAttribute(
SessionAttributeNames.EXISTING_STORE_PORT, ds_existingStorePort);
addObject(sb, "existingStorePort", ds_existingStorePort);
-
+
getContext().setSessionAttribute(
SessionAttributeNames.EXISTING_HOST, host);
@@ -542,12 +535,11 @@ public boolean validateHostName() {
addObject(sb, "message", message);
}
}
- sb.append(" }");
+ sb.append(" }");
writeToResponse(sb.toString());
- setPath(null);
return false;
}
-
+
private void addObject(StringBuffer sb, String key, String value) {
if (sb.length() < 1) {
// add first object
@@ -559,16 +551,16 @@ private void addObject(StringBuffer sb, String key, String value) {
.append(key)
.append(SEPARATOR)
.append(value)
- .append(QUOTE);
+ .append(QUOTE);
}
-
+
/*
* the following value have been pulled from an existing OpenAM
- * server which was configured to use an external DS. We need to set the DS
+ * server which was configured to use an external DS. We need to set the DS
* values in the request so they can be used to configure the existing
* OpenAM server.
*/
- private void setupDSParams(Map data) {
+ private void setupDSParams(Map data) {
String tmp = (String)data.get(BootstrapData.DS_BASE_DN);
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_STORE_ROOT_SUFFIX, tmp);
@@ -576,22 +568,23 @@ private void setupDSParams(Map data) {
tmp = (String)data.get(BootstrapData.DS_MGR);
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_STORE_LOGIN_ID, tmp);
-
+
tmp = (String)data.get(BootstrapData.DS_PWD);
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_STORE_PWD, tmp);
-
+
getContext().setSessionAttribute(
- SessionAttributeNames.CONFIG_VAR_DATA_STORE,
- SetupConstants.SMS_DS_DATASTORE);
+ SessionAttributeNames.CONFIG_VAR_DATA_STORE,
+ SetupConstants.SMS_DS_DATASTORE);
}
/**
* Validate an Existing SM Host for Configuration Backend.
* @return
*/
+ @ConfiguratorAction
public boolean validateSMHost() {
- Context ctx = getContext();
+ ConfiguratorContext ctx = getContext();
String strSSL = (String)ctx.getSessionAttribute(
SessionAttributeNames.CONFIG_STORE_SSL);
boolean ssl = (strSSL != null) && (strSSL.equals("SSL"));
@@ -656,7 +649,6 @@ public boolean validateSMHost() {
writeToResponse(getLocalizedString("cannot.connect.to.SM.datastore"));
}
- setPath(null);
return false;
}
-}
\ No newline at end of file
+}
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/Step4.java b/openam-core/src/main/java/com/sun/identity/config/wizard/Step4.java
index 3d759a3ed2..b17f794de4 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/Step4.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/Step4.java
@@ -25,12 +25,12 @@
* $Id: Step4.java,v 1.20 2009/10/27 05:31:45 hengming Exp $
*
* Portions Copyrighted 2011-2016 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems LLC.
*/
package com.sun.identity.config.wizard;
import com.sun.identity.config.SessionAttributeNames;
-import com.sun.identity.config.util.ProtectedPage;
import com.sun.identity.setup.SetupConstants;
import java.io.IOException;
import java.net.Socket;
@@ -40,8 +40,9 @@
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
-import org.openidentityplatform.openam.click.Context;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorContext;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
import org.forgerock.openam.ldap.LDAPRequests;
import org.forgerock.openam.ldap.LDAPUtils;
import org.forgerock.opendj.ldap.Connection;
@@ -52,45 +53,18 @@
/**
* Step 4 is the input of the remote user data store properties.
*/
-public class Step4 extends ProtectedPage {
+public class Step4 extends ProtectedSetupPage {
public static final String LDAP_STORE_SESSION_KEY = "wizardCustomUserStore";
- public ActionLink validateUMHostLink =
- new ActionLink("validateUMHost", this, "validateUMHost");
- public ActionLink validateUMDomainNameLink =
- new ActionLink("validateUMDomainName", this,
- "validateUMDomainName");
- public ActionLink setSSLLink =
- new ActionLink("setSSL", this, "setSSL");
- public ActionLink setUMEmbedded =
- new ActionLink("setUMEmbedded", this, "setUMEmbedded");
- public ActionLink resetUMEmbedded =
- new ActionLink("resetUMEmbedded", this, "resetUMEmbedded");
- public ActionLink setHostLink =
- new ActionLink("setHost", this, "setHost");
- public ActionLink setDomainNameLink =
- new ActionLink("setDomainName", this, "setDomainName");
- public ActionLink setPortLink =
- new ActionLink("setPort", this, "setPort");
- public ActionLink setRootSuffixLink =
- new ActionLink("setRootSuffix", this, "setRootSuffix");
- public ActionLink setLoginIDLink =
- new ActionLink("setLoginID", this, "setLoginID");
- public ActionLink setPasswordLink =
- new ActionLink("setPassword", this, "setPassword");
- public ActionLink setStoreTypeLink =
- new ActionLink("setStoreType", this, "setStoreType");
private String responseString = "ok";
private static final String ObjectClassFilter = "(objectclass=*)";
-
- public Step4() {
- }
-
+
+ @Override
public void onInit() {
super.onInit();
- Context ctx = getContext();
-
+ ConfiguratorContext ctx = getContext();
+
if (ctx.getSessionAttribute(SessionAttributeNames.USER_STORE_HOST)
== null) {
String val = getAttribute(SetupConstants.CONFIG_VAR_DATA_STORE,
@@ -138,7 +112,7 @@ public void onInit() {
String val = getAttribute(SetupConstants.USER_STORE_HOST,getHostName());
ctx.setSessionAttribute(SessionAttributeNames.USER_STORE_HOST, val);
addModel("userStoreHost", val);
-
+
val = getAttribute(SetupConstants.USER_STORE_SSL, "SIMPLE");
ctx.setSessionAttribute(SessionAttributeNames.USER_STORE_SSL, val);
if (val.equals("SSL")) {
@@ -156,7 +130,7 @@ public void onInit() {
ctx.setSessionAttribute(SessionAttributeNames.USER_STORE_LOGIN_ID, val);
addModel("userStoreLoginId", val);
- val = getAttribute(SetupConstants.USER_STORE_ROOT_SUFFIX,
+ val = getAttribute(SetupConstants.USER_STORE_ROOT_SUFFIX,
Wizard.defaultRootSuffix);
ctx.setSessionAttribute(SessionAttributeNames.USER_STORE_ROOT_SUFFIX,
val);
@@ -185,6 +159,14 @@ public void onInit() {
addModel("selectLDAPv3opends", "");
addModel("selectLDAPv3tivoli", "");
} else if (val.equals("LDAPv3ForODSEE")) {
+ // NOTE: pre-existing bug ported verbatim from the old Click page (verified against
+ // source, see docs/migration/click-to-freemarker/04-implementation-notes.md): this
+ // branch sets selectLDAPv3odsee to "checked" and then immediately overwrites it back
+ // to "" instead of setting selectLDAPv3opends. Net effect (unchanged from Click):
+ // neither the ODSEE nor OpenDS radio renders checked when the store type is ODSEE,
+ // and selectLDAPv3opends is left unset. The template defaults these vars with !"" so
+ // FreeMarker's strict undefined-variable check doesn't turn this cosmetic bug into a
+ // 500 error.
addModel("selectLDAPv3odsee", "checked=\"checked\"");
addModel("selectLDAPv3ad", "");
addModel("selectLDAPv3addc", "");
@@ -217,12 +199,16 @@ public void onInit() {
addModel("selectExternalUM", "");
}
}
-
- public boolean setAll() {
- setPath(null);
+
+ // Not bound to any Click ActionLink and not called by step4.htm/step4.ftl in the original
+ // page either - genuinely unreachable before and after this port, so left un-annotated
+ // (no @ConfiguratorAction) to keep it that way. Kept only because it was pre-existing code.
+ public boolean setAll() {
+ skipRender();
return false;
}
-
+
+ @ConfiguratorAction
public boolean setSSL() {
String ssl = toString("ssl");
if ((ssl != null) && ssl.length() > 0) {
@@ -233,56 +219,58 @@ public boolean setSSL() {
SessionAttributeNames.USER_STORE_SSL, "SIMPLE");
}
writeToResponse(getLocalizedString(responseString));
- setPath(null);
return false;
}
+ @ConfiguratorAction
public boolean setDomainName() {
String domainname = toString("domainname");
if ((domainname != null) && domainname.length() > 0) {
getContext().setSessionAttribute(
- SessionAttributeNames.USER_STORE_DOMAINNAME,
+ SessionAttributeNames.USER_STORE_DOMAINNAME,
domainname);
getContext().setSessionAttribute(
SessionAttributeNames.EXT_DATA_STORE, "true");
} else {
- responseString = "missing.domain.name";
+ responseString = "missing.domain.name";
}
writeToResponse(getLocalizedString(responseString));
- setPath(null);
return false;
}
+ @ConfiguratorAction
public boolean setHost() {
String host = toString("host");
if ((host != null) && host.length() > 0) {
getContext().setSessionAttribute(
SessionAttributeNames.USER_STORE_HOST, host);
} else {
- responseString = "missing.host.name";
+ responseString = "missing.host.name";
}
writeToResponse(getLocalizedString(responseString));
- setPath(null);
return false;
}
+ @ConfiguratorAction
public boolean setUMEmbedded() {
getContext().setSessionAttribute(SessionAttributeNames.EXT_DATA_STORE,
"false");
- setPath(null);
+ skipRender();
return false;
}
+ @ConfiguratorAction
public boolean resetUMEmbedded() {
getContext().setSessionAttribute(SessionAttributeNames.EXT_DATA_STORE,
"true");
- setPath(null);
+ skipRender();
return false;
}
-
+
+ @ConfiguratorAction
public boolean setPort() {
String port = toString("port");
-
+
if ((port != null) && port.length() > 0) {
int intValue = Integer.parseInt(port);
if ((intValue > 0) && (intValue < 65535)) {
@@ -292,39 +280,39 @@ public boolean setPort() {
responseString = "invalid.port.number";
}
} else {
- responseString = "missing.host.port";
+ responseString = "missing.host.port";
}
writeToResponse(getLocalizedString(responseString));
- setPath(null);
return false;
}
-
+
+ @ConfiguratorAction
public boolean setLoginID() {
String dn = toString("dn");
if ((dn != null) && dn.length() > 0) {
getContext().setSessionAttribute(
SessionAttributeNames.USER_STORE_LOGIN_ID, dn);
} else {
- responseString = "missing.login.id";
+ responseString = "missing.login.id";
}
writeToResponse(getLocalizedString(responseString));
- setPath(null);
return false;
}
-
+
+ @ConfiguratorAction
public boolean setPassword() {
String pwd = toString("password");
if ((pwd != null) && pwd.length() > 0) {
getContext().setSessionAttribute(
SessionAttributeNames.USER_STORE_LOGIN_PWD, pwd);
} else {
- responseString = "missing.password";
+ responseString = "missing.password";
}
writeToResponse(getLocalizedString(responseString));
- setPath(null);
return false;
}
-
+
+ @ConfiguratorAction
public boolean setRootSuffix() {
String rootsuffix = toString("rootsuffix");
@@ -333,33 +321,33 @@ public boolean setRootSuffix() {
getContext().setSessionAttribute(
SessionAttributeNames.USER_STORE_ROOT_SUFFIX, rootsuffix);
} else {
- responseString = "invalid.dn";
+ responseString = "invalid.dn";
}
} else {
- responseString = "missing.root.suffix";
+ responseString = "missing.root.suffix";
}
writeToResponse(getLocalizedString(responseString));
- setPath(null);
return false;
}
-
+
+ @ConfiguratorAction
public boolean setStoreType() {
String type = toString("type");
if ((type != null) && type.length() > 0) {
getContext().setSessionAttribute(
SessionAttributeNames.USER_STORE_TYPE, type);
- }
+ }
writeToResponse(responseString);
- setPath(null);
return false;
}
-
+
+ @ConfiguratorAction
public boolean validateUMHost() {
- Context ctx = getContext();
+ ConfiguratorContext ctx = getContext();
String strSSL = (String)ctx.getSessionAttribute(
SessionAttributeNames.USER_STORE_SSL);
boolean ssl = (strSSL != null) && (strSSL.equals("SSL"));
-
+
String host = (String)ctx.getSessionAttribute(
SessionAttributeNames.USER_STORE_HOST);
String strPort = (String)ctx.getSessionAttribute(
@@ -371,7 +359,7 @@ public boolean validateUMHost() {
SessionAttributeNames.USER_STORE_ROOT_SUFFIX);
String bindPwd = (String)ctx.getSessionAttribute(
SessionAttributeNames.USER_STORE_LOGIN_PWD);
-
+
try (Connection conn = getConnection(host, port, bindDN, bindPwd.toCharArray(), 5, ssl)) {
//String filter = "cn=" + "\"" + rootSuffix + "\""; // NOT SURE Why "cn" is specified. would never work.
String[] attrs = {""};
@@ -381,18 +369,17 @@ public boolean validateUMHost() {
ResultCode resultCode = lex.getResult().getResultCode();
if (!writeErrorToResponse(resultCode)) {
writeToResponse(getLocalizedString("cannot.connect.to.SM.datastore"));
- }
+ }
} catch (Exception e) {
writeToResponse(getLocalizedString("cannot.connect.to.SM.datastore"));
}
- setPath(null);
return false;
}
+ @ConfiguratorAction
public boolean validateUMDomainName() {
- setPath(null);
- Context ctx = getContext();
+ ConfiguratorContext ctx = getContext();
String strSSL = (String)ctx.getSessionAttribute(
SessionAttributeNames.USER_STORE_SSL);
boolean ssl = (strSSL != null) && (strSSL.equals("SSL"));
@@ -401,7 +388,7 @@ public boolean validateUMDomainName() {
SessionAttributeNames.USER_STORE_DOMAINNAME);
String rootSuffixAD = dnsDomainToDN(domainName);
getContext().setSessionAttribute(
- SessionAttributeNames.USER_STORE_ROOT_SUFFIX,
+ SessionAttributeNames.USER_STORE_ROOT_SUFFIX,
rootSuffixAD);
String[] hostAndPort = {""};
try {
@@ -414,7 +401,7 @@ public boolean validateUMDomainName() {
writeToResponse(
getLocalizedString("cannot.connect.to.UM.datastore"));
return false;
- }
+ }
String host = hostAndPort[0];
int port = Integer.parseInt(hostAndPort[1]);
@@ -424,7 +411,7 @@ public boolean validateUMDomainName() {
SessionAttributeNames.USER_STORE_ROOT_SUFFIX);
String bindPwd = (String)ctx.getSessionAttribute(
SessionAttributeNames.USER_STORE_LOGIN_PWD);
-
+
try (Connection conn = getConnection(host, port, bindDN, bindPwd.toCharArray(), 3, ssl)) {
//String filter = "cn=" + "\"" + rootSuffix + "\"";
String[] attrs = {""};
@@ -443,20 +430,20 @@ public boolean validateUMDomainName() {
// Method to get hostname and port number with the
// provided Domain Name for Active Directory user data store.
- private String[] getLdapHostAndPort(String domainName)
+ private String[] getLdapHostAndPort(String domainName)
throws NamingException, IOException {
if (!domainName.endsWith(".")) {
domainName+='.';
}
DirContext ictx = null;
// Check if domain name is a valid one.
- // The resource record type A is defined in RFC 1035.
+ // The resource record type A is defined in RFC 1035.
try {
Hashtable env = new Hashtable();
- env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
+ env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.dns.DnsContextFactory");
ictx = new InitialDirContext(env);
- Attributes attributes =
+ Attributes attributes =
ictx.getAttributes(domainName, new String[]{"A"});
Attribute attrib = attributes.get("A");
if (attrib == null) {
@@ -474,10 +461,10 @@ private String[] getLdapHostAndPort(String domainName)
final String ldapServer = "_ldap._tcp." + domainName;
try {
// Attempting to resolve ldapServer to SRV record.
- // This is a mechanism defined in MSDN, querying
+ // This is a mechanism defined in MSDN, querying
// SRV records for _ldap._tcp.DOMAINNAME.
// and get host and port from domain.
- Attributes attributes =
+ Attributes attributes =
ictx.getAttributes(ldapServer, new String[]{"SRV"});
Attribute attr = attributes.get("SRV");
if (attr == null) {
@@ -485,12 +472,12 @@ private String[] getLdapHostAndPort(String domainName)
}
String[] srv = attr.get().toString().split(" ");
String hostNam = srv[3];
- serverHostName =
+ serverHostName =
hostNam.substring(0, hostNam.length() -1);
- if ((serverHostName != null) &&
+ if ((serverHostName != null) &&
serverHostName.length() > 0) {
getContext().setSessionAttribute(
- SessionAttributeNames.USER_STORE_HOST,
+ SessionAttributeNames.USER_STORE_HOST,
serverHostName);
}
serverPortStr = srv[2];
@@ -500,7 +487,7 @@ private String[] getLdapHostAndPort(String domainName)
throw e;
}
- // try to connect to LDAP port to make sure this machine
+ // try to connect to LDAP port to make sure this machine
// has LDAP service
int serverPort = Integer.parseInt(serverPortStr);
if ((serverPort > 0) && (serverPort < 65535)) {
@@ -521,7 +508,7 @@ private String[] getLdapHostAndPort(String domainName)
}
// Method to convert the domain name to the root suffix.
- // eg., Domain Name amqa.test.com is converted to root suffix
+ // eg., Domain Name amqa.test.com is converted to root suffix
// DC=amqa,DC=test,DC=com
static String dnsDomainToDN(String domainName) {
StringBuilder buf = new StringBuilder();
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/Step5.java b/openam-core/src/main/java/com/sun/identity/config/wizard/Step5.java
index 49877744ad..bd41bae70a 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/Step5.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/Step5.java
@@ -25,6 +25,7 @@
* $Id: Step5.java,v 1.9 2009/01/05 23:17:10 veiming Exp $
*
* Portions Copyrighted 2011-2016 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems LLC.
*/
package com.sun.identity.config.wizard;
@@ -32,10 +33,10 @@
import java.net.MalformedURLException;
import java.net.URL;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
import com.sun.identity.config.SessionAttributeNames;
-import com.sun.identity.config.util.ProtectedPage;
/**
* Wizard Step # 5: Site Name, URL and Session HA Failover indicator.
@@ -45,18 +46,9 @@
* as this information will be replicated by the underlying store.
*
*/
-public class Step5 extends ProtectedPage {
-
- public ActionLink clearLink = new ActionLink(
- "clear", this, "clear");
- public ActionLink validateLink = new ActionLink(
- "validateURL", this, "validateURL");
- public ActionLink validateSiteLink = new ActionLink(
- "validateSite", this, "validateSite");
-
- public Step5() {
- }
+public class Step5 extends ProtectedSetupPage {
+ @Override
public void onInit() {
String host = (String) getContext().getSessionAttribute(
SessionAttributeNames.LB_SITE_NAME);
@@ -77,10 +69,11 @@ public void onInit() {
*
* @return boolean indicator to view.
*/
+ @ConfiguratorAction
public boolean clear() {
getContext().removeSessionAttribute(SessionAttributeNames.LB_SITE_NAME);
getContext().removeSessionAttribute(SessionAttributeNames.LB_PRIMARY_URL);
- setPath(null);
+ skipRender();
return false;
}
@@ -91,6 +84,7 @@ public boolean clear() {
* port = primary loadURL
* Just a little confusing!
*/
+ @ConfiguratorAction
public boolean validateSite() {
boolean returnVal = false;
String siteName = toString("host");
@@ -102,7 +96,6 @@ public boolean validateSite() {
SessionAttributeNames.LB_SITE_NAME, siteName);
writeValid("ok.label");
}
- setPath(null);
return returnVal;
}
@@ -113,6 +106,7 @@ public boolean validateSite() {
* port = primary loadURL
* Just a little confusing!
*/
+ @ConfiguratorAction
public boolean validateURL() {
boolean returnVal = false;
String primaryURL = toString("port");
@@ -139,7 +133,6 @@ public boolean validateURL() {
returnVal = true;
}
}
- setPath(null);
return returnVal;
}
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/Step6.java b/openam-core/src/main/java/com/sun/identity/config/wizard/Step6.java
index 012cd0b07c..21fb2c462e 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/Step6.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/Step6.java
@@ -25,63 +25,63 @@
* $Id: Step6.java,v 1.13 2009/01/05 23:17:10 veiming Exp $
*
* Portions Copyrighted 2011-2014 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems LLC.
*/
package com.sun.identity.config.wizard;
import com.sun.identity.config.SessionAttributeNames;
-import com.sun.identity.config.util.ProtectedPage;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
/**
* This is the first step in the advanced configuration flow.
* The user will be required to add the default admin password and
* the agent passwords.
*/
-public class Step6 extends ProtectedPage {
-
- // this links required for client side validation calls
- public ActionLink validateAgent =
- new ActionLink("checkAgentPassword", this, "checkAgentPassword" );
-
- public void onInit() {
+public class Step6 extends ProtectedSetupPage {
+
+ @Override
+ public void onInit() {
String agentPwd = (String)getContext().getSessionAttribute(
SessionAttributeNames.CONFIG_VAR_AMLDAPUSERPASSWD);
if (agentPwd != null) {
addModel("agentPassword",agentPwd);
}
-
+
String confirmPwd = (String)getContext().getSessionAttribute(
SessionAttributeNames.CONFIG_VAR_AMLDAPUSERPASSWD_CONFIRM);
if (confirmPwd != null) {
addModel("agentConfirm", confirmPwd);
}
-
+
super.onInit();
}
-
+
+ // Not referenced by step6.htm/step6.ftl (the template posts to the base checkPasswords
+ // handler instead, same as Step1), but kept web-invokable for byte-identical behavior with
+ // the old Click page - see docs/migration/click-to-freemarker/04-implementation-notes.md.
+ @ConfiguratorAction
public boolean checkAgentPassword() {
String agentPassword = toString("agent");
String agentConfirm = toString("agentConfirm");
String tmpadmin = (String)getContext().getSessionAttribute(
SessionAttributeNames.CONFIG_VAR_ADMIN_PWD);
-
- if (agentPassword == null || agentConfirm == null) {
+
+ if (agentPassword == null || agentConfirm == null) {
writeInvalid(getLocalizedString("missing.required.field"));
} else if (agentPassword.equals(tmpadmin)) {
writeInvalid(getLocalizedString("agent.admin.passwords.match"));
} else if (agentPassword.length() < 8) {
writeInvalid(getLocalizedString("invalid.password.length"));
} else if (!agentPassword.equals(agentConfirm)) {
- writeInvalid(getLocalizedString("passwords.do.not.match"));
+ writeInvalid(getLocalizedString("passwords.do.not.match"));
} else {
writeValid("OK");
getContext().setSessionAttribute(
SessionAttributeNames.CONFIG_VAR_AMLDAPUSERPASSWD,
agentPassword);
}
- setPath(null);
return false;
- }
+ }
}
-
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/Step7.java b/openam-core/src/main/java/com/sun/identity/config/wizard/Step7.java
index 1dd7506dd5..998cf77c41 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/Step7.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/Step7.java
@@ -25,27 +25,25 @@
* $Id: Step7.java,v 1.15 2009/10/27 05:31:45 hengming Exp $
*
* Portions Copyrighted 2010-2016 ForgeRock AS.
+ * Portions Copyrighted 2026 3A Systems LLC.
*/
package com.sun.identity.config.wizard;
-import org.openidentityplatform.openam.click.Context;
-
import com.sun.identity.config.SessionAttributeNames;
-import com.sun.identity.config.util.ProtectedPage;
import com.sun.identity.setup.SetupConstants;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorContext;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
/**
* This is the summary page for the values entered during the configuration
* process. No actual work is done here except setting the page elements.
*/
-public class Step7 extends ProtectedPage {
-
- private static final String DISABLED = "Disabled";
- private static final String ENABLED = "Enabled";
+public class Step7 extends ProtectedSetupPage {
+ @Override
public void onInit() {
- Context ctx = getContext();
+ ConfiguratorContext ctx = getContext();
String tmp = getAttribute(
SetupConstants.CONFIG_VAR_DATA_STORE,
SetupConstants.SMS_EMBED_DATASTORE);
@@ -141,7 +139,6 @@ public void onInit() {
(String) ctx.getSessionAttribute(
SessionAttributeNames.LB_PRIMARY_URL));
- // Initialize our Parent Object.
super.onInit();
}
diff --git a/openam-core/src/main/java/com/sun/identity/config/wizard/Wizard.java b/openam-core/src/main/java/com/sun/identity/config/wizard/Wizard.java
index 24e87770d0..e43145907d 100644
--- a/openam-core/src/main/java/com/sun/identity/config/wizard/Wizard.java
+++ b/openam-core/src/main/java/com/sun/identity/config/wizard/Wizard.java
@@ -25,7 +25,7 @@
* $Id: Wizard.java,v 1.27 2009/01/17 02:05:35 kevinserwin Exp $
*
* Portions Copyrighted 2010-2016 ForgeRock AS.
- * Portions Copyrighted 2025 3A Systems LLC.
+ * Portions Copyrighted 2025-2026 3A Systems LLC.
*/
package com.sun.identity.config.wizard;
@@ -35,10 +35,10 @@
import jakarta.servlet.http.HttpServletRequest;
-import org.openidentityplatform.openam.click.control.ActionLink;
+import org.openidentityplatform.openam.config.servlet.ConfiguratorAction;
+import org.openidentityplatform.openam.config.servlet.ProtectedSetupPage;
import com.sun.identity.config.SessionAttributeNames;
-import com.sun.identity.config.util.ProtectedPage;
import com.sun.identity.setup.AMSetupServlet;
import com.sun.identity.setup.AMSetupUtils;
import com.sun.identity.setup.ConfiguratorException;
@@ -47,37 +47,42 @@
import com.sun.identity.setup.SetupConstants;
import com.sun.identity.shared.Constants;
-public class Wizard extends ProtectedPage implements Constants {
+public class Wizard extends ProtectedSetupPage implements Constants {
public int startingTab = 1;
- public ActionLink createConfigLink =
- new ActionLink("createConfig", this, "createConfig" );
- public ActionLink testUrlLink =
- new ActionLink("testNewInstanceUrl", this, "testNewInstanceUrl" );
- public ActionLink pushConfigLink =
- new ActionLink("pushConfig", this, "pushConfig" );
-
private String cookieDomain = null;
- private String hostName = getHostName();
+ private String hostName;
private String dataStore = SetupConstants.SMS_EMBED_DATASTORE;
-
+
public static String defaultUserName = "cn=Directory Manager";
public static String defaultPassword = "";
public static String defaultRootSuffix = DEFAULT_ROOT_SUFFIX;
- public String defaultPort = Integer.toString(
- AMSetupUtils.getFirstUnusedPort(hostName, 50389, 1000));
- public String defaultAdminPort = Integer.toString(
- AMSetupUtils.getFirstUnusedPort(hostName, 4444, 1000));
- public String defaultJmxPort = Integer.toString(
- AMSetupUtils.getFirstUnusedPort(hostName, 1689, 1000));
-
+ public String defaultPort;
+ public String defaultAdminPort;
+ public String defaultJmxPort;
+
+ @Override
+ public void onInit() {
+ super.onInit();
+ // The old Click Wizard computed these in eager field initializers, which ran fine there
+ // because Click binds Context.getThreadLocalContext() before constructing the page.
+ // ConfiguratorContext is only attached after construction here (see ConfiguratorServlet),
+ // so the same "recompute on every request" behavior moves to onInit(), which runs
+ // unconditionally before both render and action dispatch.
+ hostName = getHostName();
+ defaultPort = Integer.toString(AMSetupUtils.getFirstUnusedPort(hostName, 50389, 1000));
+ defaultAdminPort = Integer.toString(AMSetupUtils.getFirstUnusedPort(hostName, 4444, 1000));
+ defaultJmxPort = Integer.toString(AMSetupUtils.getFirstUnusedPort(hostName, 1689, 1000));
+ }
+
/**
- * This is the 'execute' operation for the entire wizard. This method
- * aggregates all data submitted across the wizard pages here in one lump
+ * This is the 'execute' operation for the entire wizard. This method
+ * aggregates all data submitted across the wizard pages here in one lump
* and hands it off to the back-end for processing.
*/
+ @ConfiguratorAction
public boolean createConfig() {
HttpServletRequest req = getContext().getRequest();
@@ -302,10 +307,8 @@ public boolean createConfig() {
} catch (ConfiguratorException cfe) {
writeToResponse(cfe.getMessage());
}
-
- setPath(null);
+
return false;
}
-
}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionEventDispatcher.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionEventDispatcher.java
deleted file mode 100644
index 6081167fdc..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionEventDispatcher.java
+++ /dev/null
@@ -1,620 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.openidentityplatform.openam.click;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-
-import java.util.List;
-import java.util.Set;
-
-import org.openidentityplatform.openam.click.ajax.AjaxBehavior;
-import org.openidentityplatform.openam.click.service.ConfigService;
-import org.openidentityplatform.openam.click.service.LogService;
-import org.apache.click.util.HtmlStringBuffer;
-import org.apache.commons.lang.ClassUtils;
-import org.apache.commons.lang.Validate;
-
-/**
- * Provides a control ActionListener and AjaxBehavior dispatcher. The
- * ClickServlet will dispatch registered ActionListeners and AjaxBehaviors after
- * page controls have been processed.
- *
- *
Example Usage
- * The following example shows how to register an ActionListener with a custom
- * Control:
- *
- *
- * public class MyControl extends AbstractControl {
- * ...
- *
- * public boolean onProcess() {
- * bindRequestValue();
- *
- * if (isClicked()) {
- * // Dispatch an action listener event for invocation after
- * // control processing has finished
- * dispatchActionEvent();
- * }
- *
- * return true;
- * }
- * }
- *
- * When the link is clicked it invokes the method
- * {@link org.openidentityplatform.openam.click.control.AbstractControl#dispatchActionEvent()}.
- * This method registers the Control's action listener with the
- * ActionEventDispatcher. The ClickServlet will subsequently invoke the registered
- * {@link ActionListener#onAction(Control)} method after all the Page controls
- * onProcess() method have been invoked.
- */
-public class ActionEventDispatcher {
-
- // Constants --------------------------------------------------------------
-
- /** The thread local dispatcher holder. */
- private static final ThreadLocal THREAD_LOCAL_DISPATCHER_STACK
- = new ThreadLocal<>();
-
- // Variables --------------------------------------------------------------
-
- /** The list of registered event sources. */
- List eventSourceList;
-
- /** The list of registered event listeners. */
- List eventListenerList;
-
- /** The set of Controls with attached AjaxBehaviors. */
- Set ajaxBehaviorSourceSet;
-
- /**
- * The {@link org.apache.click.ActionResult} to render. This action result is
- * returned from the target Behavior.
- */
- ActionResult actionResult;
-
- /** The application log service. */
- LogService logger;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * Construct the ActionEventDispatcher with the given ConfigService.
- *
- * @param configService the click application configuration service
- */
- public ActionEventDispatcher(ConfigService configService) {
- this.logger = configService.getLogService();
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * Register the event source and event ActionListener to be fired by the
- * ClickServlet once all the controls have been processed.
- *
- * @param source the action event source
- * @param listener the event action listener
- */
- public static void dispatchActionEvent(Control source, ActionListener listener) {
- Validate.notNull(source, "Null source parameter");
- Validate.notNull(listener, "Null listener parameter");
-
- ActionEventDispatcher instance = getThreadLocalDispatcher();
- instance.registerActionEvent(source, listener);
- }
-
- /**
- * Register the source control which AjaxBehaviors should be fired by the
- * ClickServlet.
- *
- * @param source the source control which behaviors should be fired
- */
- public static void dispatchAjaxBehaviors(Control source) {
- Validate.notNull(source, "Null source parameter");
-
- ActionEventDispatcher instance = getThreadLocalDispatcher();
- instance.registerAjaxBehaviorSource(source);
- }
-
- /**
- * Return the thread local ActionEventDispatcher instance.
- *
- * @return the thread local ActionEventDispatcher instance.
- * @throws RuntimeException if an ActionEventDispatcher is not available on
- * the thread
- */
- public static ActionEventDispatcher getThreadLocalDispatcher() {
- return getDispatcherStack().peek();
- }
-
- /**
- * Returns true if an ActionEventDispatcher instance is available on the
- * current thread, false otherwise.
- *
- * Unlike {@link #getThreadLocalDispatcher()} this method can safely be used
- * and will not throw an exception if an ActionEventDispatcher is not
- * available on the current thread.
- *
- * @return true if an ActionEventDispatcher instance is available on the
- * current thread, false otherwise
- */
- public static boolean hasThreadLocalDispatcher() {
- DispatcherStack dispatcherStack = THREAD_LOCAL_DISPATCHER_STACK.get();
- if (dispatcherStack == null) {
- return false;
- }
- return !dispatcherStack.isEmpty();
- }
-
- /**
- * Fire all the registered action events after the Page Controls have been
- * processed and return true if the page should continue processing.
- *
- * @param context the request context
- *
- * @return true if the page should continue processing, false otherwise
- */
- public boolean fireActionEvents(Context context) {
-
- if (!hasActionEvents()) {
- return true;
- }
-
- return fireActionEvents(context, getEventSourceList(), getEventListenerList());
- }
-
- /**
- * Fire all the registered AjaxBehaviors and return true if the page should
- * continue processing, false otherwise.
- *
- * @see #fireAjaxBehaviors(Context, java.util.Set)
- *
- * @param context the request context
- *
- * @return true if the page should continue processing, false otherwise
- */
- public boolean fireAjaxBehaviors(Context context) {
-
- if (!hasAjaxBehaviorSourceSet()) {
- return true;
- }
-
- return fireAjaxBehaviors(context, getAjaxBehaviorSourceSet());
- }
-
- // Protected Methods ------------------------------------------------------
-
- /**
- * Allow the dispatcher to handle the error that occurred.
- *
- * @param throwable the error which occurred during processing
- */
- protected void errorOccurred(Throwable throwable) {
- // Clear the control listeners and behaviors from the dispatcher
- clear();
- }
-
- /**
- * Fire the actions for the given listener list and event source list which
- * return true if the page should continue processing.
- *
- * This method can be overridden if you need to customize the way events
- * are fired.
- *
- * @param context the request context
- * @param eventSourceList the list of source controls
- * @param eventListenerList the list of listeners to fire
- *
- * @return true if the page should continue processing or false otherwise
- */
- protected boolean fireActionEvents(Context context,
- List eventSourceList, List eventListenerList) {
-
- boolean continueProcessing = true;
-
- for (int i = 0, size = eventSourceList.size(); i < size; i++) {
- Control source = eventSourceList.remove(0);
- ActionListener listener = eventListenerList.remove(0);
-
- if (!fireActionEvent(context, source, listener)) {
- continueProcessing = false;
- }
- }
-
- return continueProcessing;
- }
-
- /**
- * Fire the action for the given listener and event source which
- * return true if the page should continue processing.
- *
- * This method can be overridden if you need to customize the way events
- * are fired.
- *
- * @param context the request context
- * @param source the source control
- * @param listener the listener to fire
- *
- * @return true if the page should continue processing, false otherwise
- */
- protected boolean fireActionEvent(Context context, Control source,
- ActionListener listener) {
- return listener.onAction(source);
- }
-
- /**
- * Fire the AjaxBehaviors for the given control set and return true if the page
- * should continue processing, false otherwise.
- *
- * This method can be overridden if you need to customize the way
- * AjaxBehaviors are fired.
- *
- * @see #fireAjaxBehaviors(Context, Control)
- *
- * @param context the request context
- * @param ajaxBbehaviorSourceSet the set of controls with attached AjaxBehaviors
- *
- * @return true if the page should continue processing, false otherwise
- */
- protected boolean fireAjaxBehaviors(Context context, Set ajaxBbehaviorSourceSet) {
-
- boolean continueProcessing = true;
-
- for (Iterator it = ajaxBbehaviorSourceSet.iterator(); it.hasNext();) {
- Control source = it.next();
-
- // Pop the first entry in the set
- it.remove();
-
- if (!fireAjaxBehaviors(context, source)) {
- continueProcessing = false;
- }
- }
-
- return continueProcessing;
- }
-
- /**
- * Fire the AjaxBehaviors for the given control and return true if the
- * page should continue processing, false otherwise. AjaxBehaviors will
- * only fire if their {@link org.openidentityplatform.openam.click.ajax.AjaxBehavior#isAjaxTarget(Context) isAjaxTarget()}
- * method returns true.
- *
- * This method can be overridden if you need to customize the way
- * AjaxBehaviors are fired.
- *
- * @param context the request context
- * @param source the control which attached behaviors should be fired
- *
- * @return true if the page should continue processing, false otherwise
- */
- protected boolean fireAjaxBehaviors(Context context, Control source) {
-
- boolean continueProcessing = true;
-
- if (logger.isTraceEnabled()) {
- String sourceClassName = ClassUtils.getShortClassName(source.getClass());
- HtmlStringBuffer buffer = new HtmlStringBuffer();
- buffer.append(" processing AjaxBehaviors for control: '");
- buffer.append(source.getName()).append("' ");
- buffer.append(sourceClassName);
- logger.trace(buffer.toString());
- }
-
- for (Behavior behavior : source.getBehaviors()) {
-
- if (behavior instanceof AjaxBehavior) {
- AjaxBehavior ajaxBehavior = (AjaxBehavior) behavior;
-
- boolean isAjaxTarget = ajaxBehavior.isAjaxTarget(context);
-
- if (logger.isTraceEnabled()) {
- String behaviorClassName = ClassUtils.getShortClassName(
- ajaxBehavior.getClass());
- HtmlStringBuffer buffer = new HtmlStringBuffer();
- buffer.append(" invoked: ");
- buffer.append(behaviorClassName);
- buffer.append(".isAjaxTarget() : ");
- buffer.append(isAjaxTarget);
- logger.trace(buffer.toString());
- }
-
- if (isAjaxTarget) {
-
- // The first non-null ActionResult returned will be rendered, other
- // ActionResult instances are ignored
- ActionResult behaviorActionResult =
- ajaxBehavior.onAction(source);
- if (actionResult == null && behaviorActionResult != null) {
- actionResult = behaviorActionResult;
- }
-
- if (logger.isTraceEnabled()) {
- String behaviorClassName = ClassUtils.getShortClassName(
- ajaxBehavior.getClass());
- String actionResultClassName = null;
-
- if (behaviorActionResult != null) {
- actionResultClassName = ClassUtils.getShortClassName(
- behaviorActionResult.getClass());
- }
-
- HtmlStringBuffer buffer = new HtmlStringBuffer();
- buffer.append(" invoked: ");
- buffer.append(behaviorClassName);
- buffer.append(".onAction() : ");
- buffer.append(actionResultClassName);
-
- if (actionResult == behaviorActionResult
- && behaviorActionResult != null) {
- buffer.append(" (ActionResult will be rendered)");
- } else {
- if (behaviorActionResult == null) {
- buffer.append(" (ActionResult is null and will be ignored)");
- } else {
- buffer.append(" (ActionResult will be ignored since another AjaxBehavior already retuned a non-null ActionResult)");
- }
- }
-
- logger.trace(buffer.toString());
- }
-
- continueProcessing = false;
- break;
- }
- }
- }
-
- if (logger.isTraceEnabled()) {
-
- // continueProcessing is true if no AjaxBehavior was the target
- // of the request
- if (continueProcessing) {
- HtmlStringBuffer buffer = new HtmlStringBuffer();
- String sourceClassName = ClassUtils.getShortClassName(
- source.getClass());
- buffer.append(" *no* target AjaxBehavior found for '");
- buffer.append(source.getName()).append("' ");
- buffer.append(sourceClassName);
- buffer.append(" - invoking AjaxBehavior.isAjaxTarget() returned false for all AjaxBehaviors");
- logger.trace(buffer.toString());
- }
- }
-
- // Ajax requests stops further processing
- return continueProcessing;
- }
-
- // Package Private Methods ------------------------------------------------
-
- /**
- * Register the event source and event ActionListener.
- *
- * @param source the action event source
- * @param listener the event action listener
- */
- void registerActionEvent(Control source, ActionListener listener) {
- Validate.notNull(source, "Null source parameter");
- Validate.notNull(listener, "Null listener parameter");
-
- getEventSourceList().add(source);
- getEventListenerList().add(listener);
- }
-
- /**
- * Register the AjaxBehavior source control.
- *
- * @param source the AjaxBehavior source control
- */
- void registerAjaxBehaviorSource(Control source) {
- Validate.notNull(source, "Null source parameter");
-
- getAjaxBehaviorSourceSet().add(source);
- }
-
- /**
- * Checks if any Action Events have been registered.
- *
- * @return true if the dispatcher has any Action Events registered
- */
- boolean hasActionEvents() {
- if (eventListenerList == null || eventListenerList.isEmpty()) {
- return false;
- }
- return true;
- }
-
- /**
- * Return the list of event listeners.
- *
- * @return list of event listeners
- */
- List getEventListenerList() {
- if (eventListenerList == null) {
- eventListenerList = new ArrayList();
- }
- return eventListenerList;
- }
-
- /**
- * Return the list of event sources.
- *
- * @return list of event sources
- */
- List getEventSourceList() {
- if (eventSourceList == null) {
- eventSourceList = new ArrayList();
- }
- return eventSourceList;
- }
-
- /**
- * Clear the events and behaviors.
- */
- void clear() {
- if (hasActionEvents()) {
- getEventSourceList().clear();
- getEventListenerList().clear();
- }
-
- if (hasAjaxBehaviorSourceSet()) {
- getAjaxBehaviorSourceSet().clear();
- }
- }
-
- /**
- * Return the Behavior's action result or null if no behavior was dispatched.
- *
- * @return the Behavior's action result or null if no behavior was dispatched
- */
- ActionResult getActionResult() {
- return actionResult;
- }
-
- /**
- * Return true if a control with AjaxBehaviors was registered, false otherwise.
- *
- * @return true if a control with AjaxBehaviors was registered, false otherwise.
- */
- boolean hasAjaxBehaviorSourceSet() {
- if (ajaxBehaviorSourceSet == null || ajaxBehaviorSourceSet.isEmpty()) {
- return false;
- }
- return true;
- }
-
- /**
- * Return the set of controls with attached AjaxBehaviors.
- *
- * @return set of control with attached AjaxBehaviors
- */
- Set getAjaxBehaviorSourceSet() {
- if (ajaxBehaviorSourceSet == null) {
- ajaxBehaviorSourceSet = new LinkedHashSet();
- }
- return ajaxBehaviorSourceSet;
- }
-
- /**
- * Adds the specified ActionEventDispatcher on top of the dispatcher stack.
- *
- * @param actionEventDispatcher the ActionEventDispatcher to add
- */
- static void pushThreadLocalDispatcher(ActionEventDispatcher actionEventDispatcher) {
- getDispatcherStack().push(actionEventDispatcher);
- }
-
- /**
- * Remove and return the actionEventDispatcher instance on top of the
- * dispatcher stack.
- *
- * @return the actionEventDispatcher instance on top of the dispatcher stack
- */
- static ActionEventDispatcher popThreadLocalDispatcher() {
- DispatcherStack dispatcherStack = getDispatcherStack();
- ActionEventDispatcher actionEventDispatcher = dispatcherStack.pop();
-
- if (dispatcherStack.isEmpty()) {
- THREAD_LOCAL_DISPATCHER_STACK.set(null);
- }
-
- return actionEventDispatcher;
- }
-
- /**
- * Return the stack data structure where ActionEventDispatchers are stored.
- *
- * @return stack data structure where ActionEventDispatcher are stored
- */
- static ActionEventDispatcher.DispatcherStack getDispatcherStack() {
- DispatcherStack dispatcherStack = THREAD_LOCAL_DISPATCHER_STACK.get();
-
- if (dispatcherStack == null) {
- dispatcherStack = new ActionEventDispatcher.DispatcherStack(2);
- THREAD_LOCAL_DISPATCHER_STACK.set(dispatcherStack);
- }
-
- return dispatcherStack;
- }
-
- // Inner Classes ----------------------------------------------------------
-
- /**
- * Provides an unsynchronized Stack.
- */
- static class DispatcherStack extends ArrayList {
-
- /** Serialization version indicator. */
- private static final long serialVersionUID = 1L;
-
- /**
- * Create a new DispatcherStack with the given initial capacity.
- *
- * @param initialCapacity specify initial capacity of this stack
- */
- private DispatcherStack(int initialCapacity) {
- super(initialCapacity);
- }
-
- /**
- * Pushes the ActionEventDispatcher onto the top of this stack.
- *
- * @param actionEventDispatcher the ActionEventDispatcher to push onto this stack
- * @return the ActionEventDispatcher pushed on this stack
- */
- private ActionEventDispatcher push(ActionEventDispatcher actionEventDispatcher) {
- add(actionEventDispatcher);
-
- return actionEventDispatcher;
- }
-
- /**
- * Removes and return the ActionEventDispatcher at the top of this stack.
- *
- * @return the ActionEventDispatcher at the top of this stack
- */
- private ActionEventDispatcher pop() {
- ActionEventDispatcher actionEventDispatcher = peek();
-
- remove(size() - 1);
-
- return actionEventDispatcher;
- }
-
- /**
- * Looks at the ActionEventDispatcher at the top of this stack without
- * removing it.
- *
- * @return the ActionEventDispatcher at the top of this stack
- */
- private ActionEventDispatcher peek() {
- int length = size();
-
- if (length == 0) {
- String msg = "No ActionEventDispatcher available on ThreadLocal Dispatcher Stack";
- throw new RuntimeException(msg);
- }
-
- return get(length - 1);
- }
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionListener.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionListener.java
deleted file mode 100644
index a7ca79b482..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionListener.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.openidentityplatform.openam.click;
-
-import java.io.Serializable;
-
-/**
- * Provides a listener interface for receiving control action events.
- * The usage model is similar to the java.awt.event.ActionListener
- * interface.
- *
- * The class that is interested in processing an action event
- * implements this interface, and the object created with that class is
- * registered with a control, using the control's setActionListener
- * method. When the action event occurs, that object's onAction method
- * is invoked.
- *
- *
Listener Example
- *
- * An ActionListener example is provided below:
- *
- *
- */
-public interface ActionListener extends Serializable {
-
- /**
- * Return true if the control and page processing should continue, or false
- * otherwise.
- *
- * @param source the source of the action event
- * @return true if control and page processing should continue or false
- * otherwise.
- */
- public boolean onAction(Control source);
-
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionResult.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionResult.java
deleted file mode 100644
index 60ac4b9c70..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/ActionResult.java
+++ /dev/null
@@ -1,580 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click;
-
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.Writer;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import jakarta.servlet.http.HttpServletResponse;
-
-import org.openidentityplatform.openam.click.util.ClickUtils;
-
-/**
- * Provides an ActionResult that is returned by Page Actions and AjaxBehaviors.
- * ActionResults are often used to return a partial response to the browser
- * instead of the full page content.
- *
- * An ActionResult can consist of a String (HTML, JSON, XML, plain text) or a byte
- * array (jpg, gif, png, pdf or excel documents). The ActionResult {@link #contentType}
- * must be set appropriately in order for the browser to recognize the action result.
- *
- * ActionResults are returned by {@link org.apache.click.ajax.AjaxBehavior Ajax Behaviors}
- * and Page Action methods.
- *
- *
Ajax Behavior
- *
- * Ajax requests are handled by adding an {@link org.apache.click.ajax.AjaxBehavior Ajax Behavior}
- * to a control. The AjaxBehavior {@link org.apache.click.ajax.AjaxBehavior#onAction(org.apache.click.Control) onAction}
- * method will handle the request and return a ActionResult instance that contains
- * the response, thus bypassing the rendering of the Page template. For example:
- *
- *
- * private ActionLink link = new ActionLink("link");
- *
- * public void onInit() {
- * addControl(link);
- *
- * link.addBehavior(new AjaxBehavior() {
- *
- * // The onAction method must return a ActionResult
- * public ActionResult onAction(Control source) {
- * // Create a new action result containing an HTML snippet and HTML content type
- * ActionResult actionResult = new ActionResult("<span>Hello World</span>", ActionResult.HTML);
- * return actionResult;
- * }
- * });
- * }
- *
- *
Page Action
- *
- * A Page Action is a method on a Page that can be invoked directly
- * from the browser. The Page Action method returns an ActionResult instance that
- * is rendered to the browser, thus bypassing the rendering of the Page template.
- *
- *
- * private ActionLink link = new ActionLink("link");
- *
- * public void onInit() {
- * link.addControl(link);
- *
- * // A "pageAction" is set as a parameter on the link. The "pageAction"
- * // value is set to the Page method: "renderHelloWorld"
- * link.setParameter(PAGE_ACTION, "renderHelloWorld");
- * }
- *
- * /**
- * * This is a "pageAction" method that will render an HTML response.
- * *
- * * Note the signature of the pageAction: a public, no-argument method
- * * returning a ActionResult instance.
- * */
- * public ActionResult renderHelloWorld() {
- * ActionResult actionResult = new ActionResult("<span>Hello World</span>", ActionResult.HTML);
- * return actionResult;
- * }
- *
- *
Content Type
- *
- * The {@link #contentType} of the ActionResult must be set to the appropriate type
- * in order for the client to recognize the response. ActionResult provides constants
- * for some of the common content types, including: {@link #XML text/xml},
- * {@link #HTML text/html}, {@link #JSON application/json}, {@link #TEXT text/plain}.
- *
- * For example:
- *
- * ActionResult actionResult = new ActionResult("alert('hello world');", ActionResult.JAVASCRIPT);
- *
- * ...
- *
- * // content type can also be set through the setContentType method
- * actionResult.setContentType(ActionResult.JAVASCRIPT);
- *
- * ...
- *
- *
- * More content types can be retrieved through {@link org.apache.click.util.ClickUtils#getMimeType(java.lang.String)}:
- *
- * // lookup content type for PNG
- * String contentType = ClickUtils.getMimeType("png");
- * actionResult.setContentType(contentType);
- */
-public class ActionResult {
-
- // Constants --------------------------------------------------------------
-
- /** The plain text content type constant: text/plain. */
- public static final String TEXT = "text/plain";
-
- /** The html content type constant: text/html. */
- public static final String HTML = "text/html";
-
- /** The The xhtml content type constant: application/xhtml+xml. */
- public static final String XHTML = "application/xhtml+xml";
-
- /** The json content type constant: text/json. */
- public static final String JSON = "application/json";
-
- /** The javascript content type constant: text/javascript. */
- public static final String JAVASCRIPT = "text/javascript";
-
- /** The xml content type constant: text/xml. */
- public static final String XML = "text/xml";
-
- /** The ActionResult writer buffer size. */
- private static final int WRITER_BUFFER_SIZE = 256; // For text, set a small response size
-
- /** The ActionResult output buffer size. */
- private static final int OUTPUT_BUFFER_SIZE = 4 * 1024; // For binary, set a a large response size
-
- // Variables --------------------------------------------------------------
-
- /** The content to render. */
- private String content;
-
- /** The content as a byte array. */
- private byte[] bytes;
-
- /** The servlet response reader. */
- private Reader reader;
-
- /** The servlet response input stream. */
- private InputStream inputStream;
-
- /** The response content type. */
- private String contentType;
-
- /** The response character encoding. */
- private String characterEncoding;
-
- /** Indicates whether the ActionResult should be cached by browser. */
- private boolean cacheActionResult = false;
-
- /** The path of the actionResult template to render. */
- private String template;
-
- /** The model for the ActionResult {@link #template}. */
- private Map model;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * Construct the ActionResult for the given template and model.
- *
- * When the ActionResult is rendered the template and model will be merged and
- * the result will be streamed back to the client.
- *
- * For example:
- *
- * public class MyPage extends Page {
- * public void onInit() {
- *
- * Behavior behavior = new DefaultAjaxBehavior() {
- *
- * public ActionResult onAction() {
- *
- * Map model = new HashMap();
- * model.put("id", "link");
- *
- * // Note: we set XML as the content type
- * ActionResult actionResult = new ActionResult("/js/actionResult.xml", model, ActionResult.XML);
- *
- * return actionResult;
- * }
- * }
- * }
- * }
- *
- * @param template the template to render and stream back to the client
- * @param model the template data model
- * @param contentType the response content type
- */
- public ActionResult(String template, Map model, String contentType) {
- this.template = template;
- this.model = model;
- this.contentType = contentType;
- }
-
- /**
- * Construct the ActionResult for the given reader and content type.
- *
- * @param reader the reader which characters must be streamed back to the
- * client
- * @param contentType the response content type
- */
- public ActionResult(Reader reader, String contentType) {
- this.reader = reader;
- this.contentType = contentType;
- }
-
- /**
- * Construct the ActionResult for the given inputStream and content type.
- *
- * @param inputStream the input stream to stream back to the client
- * @param contentType the response content type
- */
- public ActionResult(InputStream inputStream, String contentType) {
- this.inputStream = inputStream;
- this.contentType = contentType;
- }
-
- /**
- * Construct the ActionResult for the given String content and content type.
- *
- * @param content the String content to stream back to the client
- * @param contentType the response content type
- */
- public ActionResult(String content, String contentType) {
- this.content = content;
- this.contentType = contentType;
- }
-
- /**
- * Construct the ActionResult for the given byte array and content type.
- *
- * @param bytes the byte array to stream back to the client
- * @param contentType the response content type
- */
- public ActionResult(byte[] bytes, String contentType) {
- this.bytes = bytes;
- this.contentType = contentType;
- }
-
- /**
- * Construct the ActionResult for the given content. The
- * {@link jakarta.servlet.http.HttpServletResponse#setContentType(java.lang.String) response content type}
- * will default to {@link #TEXT}, unless overridden.
- *
- * @param content the content to stream back to the client
- */
- public ActionResult(String content) {
- this.content = content;
- }
-
- /**
- * Construct a new empty ActionResult. The
- * {@link jakarta.servlet.http.HttpServletResponse#setContentType(java.lang.String) response content type}
- * will default to {@link #TEXT}, unless overridden.
- */
- public ActionResult() {
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * Set whether the action result should be cached by the client browser or
- * not.
- *
- * If false, Click will set the following headers to prevent browsers
- * from caching the result:
- *
- *
- * @param cacheActionResult indicates whether the action result should be cached
- * by the client browser or not
- */
- public void setCacheActionResult(boolean cacheActionResult) {
- this.cacheActionResult = cacheActionResult;
- }
-
- /**
- * Return true if the action result should be cached by the client browser,
- * defaults to false. It is highly unlikely that you would turn action result
- * caching on.
- *
- * @return true if the action result should be cached by the client browser,
- * false otherwise
- */
- public boolean isCacheActionRestul() {
- return cacheActionResult;
- }
-
- /**
- * Return the action result character encoding. If no character encoding is specified
- * the request character encoding will be used.
- *
- * @return the action result character encoding
- */
- public String getCharacterEncoding() {
- return characterEncoding;
- }
-
- /**
- * Set the action result character encoding. If no character encoding is set the
- * request character encoding will be used.
- *
- * @param characterEncoding the action result character encoding
- */
- public void setCharacterEncoding(String characterEncoding) {
- this.characterEncoding = characterEncoding;
- }
-
- /**
- * Set the action result response content type. If no content type is set it will
- * default to {@value #TEXT}.
- *
- * @param contentType the action result response content type
- */
- public void setContentType(String contentType) {
- this.contentType = contentType;
- }
-
- /**
- * Return the action result content type, default is {@value #TEXT}.
- *
- * @return the response content type
- */
- public String getContentType() {
- if (contentType == null) {
- contentType = TEXT;
- }
- return contentType;
- }
-
- /**
- * Set the content String to stream back to the client.
- *
- * @param content the content String to stream back to the client
- */
- public void setContent(String content) {
- this.content = content;
- }
-
- /**
- * Return the content String to stream back to the client.
- *
- * @return the content String to stream back to the client
- */
- public String getContent() {
- return content;
- }
-
- /**
- * Set the byte array to stream back to the client.
- *
- * @param bytes the byte array to stream back to the client
- */
- public void setBytes(byte[] bytes, String contentType) {
- this.bytes = bytes;
- this.contentType = contentType;
- }
-
- /**
- * Return the byte array to stream back to the client.
- *
- * @return the byte array to stream back to the client
- */
- public byte[] getBytes() {
- return bytes;
- }
-
- /**
- * Set the content to stream back to the client.
- *
- * @param inputStream the inputStream to stream back to the client
- */
- public void setInputStream(InputStream inputStream) {
- this.inputStream = inputStream;
- }
-
- /**
- * Return the inputStream to stream back to the client.
- *
- * @return the inputStream to stream back to the client
- */
- public InputStream getInputStream() {
- return inputStream;
- }
-
- /**
- * Set the reader which characters are streamed back to the client.
- *
- * @param reader the reader which characters are streamed back to the client.
- */
- public void setReader(Reader reader) {
- this.reader = reader;
- }
-
- /**
- * Return the reader which characters are streamed back to the client.
- *
- * @return the reader which characters are streamed back to the client.
- */
- public Reader getReader() {
- return reader;
- }
-
- /**
- * Return the data model for the ActionResult {@link #template}.
- *
- * @return the data model for the ActionResult template
- */
- public Map getModel() {
- if (model == null) {
- model = new HashMap();
- }
- return model;
- }
-
- /**
- * Set the model of the ActionResult template to render.
- *
- * If the {@link #template} property is set, the template and {@link #model}
- * will be merged and the result will be streamed back to the client.
- *
- * @param model the model of the template to render
- */
- public void setModel(Map model) {
- this.model = model;
- }
-
- /**
- * Return the template to render for this ActionResult.
- *
- * @return the template to render for this ActionResult
- */
- public String getTemplate() {
- return template;
- }
-
- /**
- * Set the template to render for this ActionResult.
- *
- * @param template the template to render for this ActionResult
- */
- public void setTemplate(String template) {
- this.template = template;
- }
-
- /**
- * Render the ActionResult to the client.
- *
- * @param context the request context
- */
- public final void render(Context context) {
- prepare(context);
- renderActionResult(context);
- }
-
- // Protected Methods ------------------------------------------------------
-
- /**
- * Render the ActionResult to the client. This method can be overridden
- * by subclasses if custom rendering or direct access to the
- * HttpServletResponse is required.
- *
- * @param context the request context
- */
- protected void renderActionResult(Context context) {
-
- HttpServletResponse response = context.getResponse();
-
- Reader localReader = getReader();
- InputStream localInputStream = getInputStream();
-
- try {
- String localContent = getContent();
- byte[] localBytes = getBytes();
-
- String localTemplate = getTemplate();
- if (localTemplate != null) {
- Map templateModel = getModel();
- if (templateModel == null) {
- templateModel = new HashMap();
- }
- String result = context.renderTemplate(localTemplate, templateModel);
- localReader = new StringReader(result);
-
- } else if (localContent != null) {
- localReader = new StringReader(localContent);
- } else if (localBytes != null) {
- localInputStream = new ByteArrayInputStream(localBytes);
- }
-
- if (localReader != null) {
- Writer writer = response.getWriter();
- char[] buffer = new char[WRITER_BUFFER_SIZE];
- int len = 0;
- while (-1 != (len = localReader.read(buffer))) {
- writer.write(buffer, 0, len);
- }
- writer.flush();
- writer.close();
-
- } else if (localInputStream != null) {
- byte[] buffer = new byte[OUTPUT_BUFFER_SIZE];
- int len = 0;
- OutputStream outputStream = response.getOutputStream();
- while (-1 != (len = localInputStream.read(buffer))) {
- outputStream.write(buffer, 0, len);
- }
- outputStream.flush();
- outputStream.close();
- }
-
- } catch (Exception e) {
- throw new RuntimeException(e);
-
- } finally {
- ClickUtils.close(localInputStream);
- ClickUtils.close(localReader);
- }
- }
-
- // Private Methods --------------------------------------------------------
-
- /**
- * Prepare the ActionResult for rendering.
- *
- * @param context the request context
- */
- private void prepare(Context context) {
- HttpServletResponse response = context.getResponse();
-
- if (!isCacheActionRestul()) {
- // Set headers to disable cache
- response.setHeader("Pragma", "no-cache");
- response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
- response.setDateHeader("Expires", new Date(1L).getTime());
- }
-
- String localContentType = getContentType();
-
- if (getCharacterEncoding() == null) {
-
- // Fallback to request character encoding
- if (context.getRequest().getCharacterEncoding() != null) {
- response.setContentType(localContentType + "; charset="
- + context.getRequest().getCharacterEncoding());
- } else {
- response.setContentType(localContentType);
- }
-
- } else {
- response.setContentType(localContentType + "; charset=" + getCharacterEncoding());
- }
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/Behavior.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/Behavior.java
deleted file mode 100644
index 58ac8527e0..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/Behavior.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click;
-
-/**
- * Behaviors provide a mechanism for changing how Controls behave at runtime.
- * Behaviors are added to a Control and provides interceptor methods to decorate
- * and enhance the source Control.
- *
- * Behaviors provide interceptor methods for specific Control life cycle events.
- * These interceptor methods can be implemented to further process and decorate
- * the control or its children.
- *
- * The following interceptor methods are defined:
- *
- *
- *
preResponse - occurs before the control markup is written to the response
- *
preRenderHeadElements - occurs after preResponse but before the control
- * {@link Control#getHeadElements() HEAD elements} are written to the response
- *
preDestroy - occurs before the Control {@link Control#onDestroy() onDestroy}
- * event handler.
- *
- *
- * These interceptor methods allow the Behavior to decorate a control,
- * for example:
- *
- *
- *
add or remove Control HEAD elements such as JavaScript and CSS dependencies
- * and setup scripts
- *
add or remove Control attributes such as "class", "style" etc.
- *
- */
-public interface Behavior {
-
- /**
- * This event occurs before the markup is written to the HttpServletResponse.
- *
- * @param source the control the behavior is registered with
- */
- public void preResponse(Control source);
-
- /**
- * This event occurs after {@link #preResponse(Control)},
- * but before the Control's {@link Control#getHeadElements()} is called.
- *
- * @param source the control the behavior is registered with
- */
- public void preRenderHeadElements(Control source);
-
- /**
- * This event occurs before the Control {@link Control#onDestroy() onDestroy}
- * event handler. This event allows the behavior to cleanup or store Control
- * state in the Session.
- *
- * @param source the control the behavior is registered with
- */
- public void preDestroy(Control source);
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/ClickRequestWrapper.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/ClickRequestWrapper.java
deleted file mode 100644
index 482c832c77..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/ClickRequestWrapper.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click;
-
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletRequestWrapper;
-
-import org.openidentityplatform.openam.click.service.FileUploadService;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-
-import org.apache.commons.fileupload.FileItem;
-import org.apache.commons.fileupload.FileUploadException;
-
-/**
- * Provides a custom HttpServletRequest class for shielding users from
- * multipart request parameters. Thus calling request.getParameter(String)
- * will still work properly.
- */
-class ClickRequestWrapper extends HttpServletRequestWrapper {
-
- /**
- * The FileItem objects for "multipart" POST requests.
- */
- private final Map fileItemMap;
-
- /** The request is a multi-part file upload POST request. */
- private final boolean isMultipartRequest;
-
- /** The map of "multipart" request parameter values. */
- private final Map multipartParameterMap;
-
- /** The wrapped servlet request. */
- private final HttpServletRequest request;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * @see HttpServletRequestWrapper(HttpServletRequest)
- */
- ClickRequestWrapper(final HttpServletRequest request,
- final FileUploadService fileUploadService) {
- super(request);
-
- this.isMultipartRequest = ClickUtils.isMultipartRequest(request);
- this.request = request;
-
- if (isMultipartRequest) {
-
- Map requestParams = new HashMap();
- Map fileItems = new HashMap();
-
- try {
- List itemsList = new ArrayList();
-
- try {
-
- itemsList = fileUploadService.parseRequest(request);
-
- } catch (FileUploadException fue) {
- request.setAttribute(FileUploadService.UPLOAD_EXCEPTION, fue);
- }
-
- for (FileItem fileItem : itemsList) {
- String name = fileItem.getFieldName();
- String value = null;
-
- // Form fields are placed in the request parameter map,
- // while file uploads are placed in the file item map.
- if (fileItem.isFormField()) {
-
- if (request.getCharacterEncoding() == null) {
- value = fileItem.getString();
-
- } else {
- try {
- value = fileItem.getString(request.getCharacterEncoding());
-
- } catch (UnsupportedEncodingException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- // Add the form field value to the parameters.
- addToMapAsString(requestParams, name, value);
-
- } else {
- // Add the file item to the list of file items.
- addToMapAsFileItem(fileItems, name, fileItem);
- }
- }
-
- } catch (Throwable t) {
-
- // Don't throw error here as it will break Context creation.
- // Instead add the error as a request attribute.
- request.setAttribute(Context.CONTEXT_FATAL_ERROR, t);
-
- } finally {
- fileItemMap = Collections.unmodifiableMap(fileItems);
- multipartParameterMap = Collections.unmodifiableMap(requestParams);
- }
-
- } else {
- fileItemMap = Collections.emptyMap();
- multipartParameterMap = Collections.emptyMap();
- }
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * Returns a map of FileItem arrays keyed on request parameter
- * name for "multipart" POST requests (file uploads). Thus each map entry
- * will consist of one or more FileItem objects.
- *
- * @return map of FileItem arrays keyed on request parameter name
- * for "multipart" POST requests
- */
- public Map getFileItemMap() {
- return fileItemMap;
- }
-
- /**
- * @see jakarta.servlet.ServletRequest#getParameter(String)
- */
- @Override
- public String getParameter(String name) {
- if (isMultipartRequest) {
- Object value = getMultipartParameterMap().get(name);
-
- if (value instanceof String) {
- return (String) value;
- }
-
- if (value instanceof String[]) {
- String[] array = (String[]) value;
- if (array.length >= 1) {
- return array[0];
- } else {
- return null;
- }
- }
-
- return (value == null ? null : value.toString());
-
- } else {
- return request.getParameter(name);
- }
- }
-
- /**
- * @see jakarta.servlet.ServletRequest#getParameterNames()
- */
- @Override
- @SuppressWarnings("unchecked")
- public Enumeration getParameterNames() {
- if (isMultipartRequest) {
- return Collections.enumeration(getMultipartParameterMap().keySet());
-
- } else {
- return request.getParameterNames();
- }
- }
-
- /**
- * @see jakarta.servlet.ServletRequest#getParameterValues(String)
- */
- @Override
- public String[] getParameterValues(String name) {
- if (isMultipartRequest) {
- Object values = getMultipartParameterMap().get(name);
- if (values instanceof String) {
- return new String[] { values.toString() };
- }
- if (values instanceof String[]) {
- return (String[]) values;
- } else {
- return null;
- }
-
- } else {
- return request.getParameterValues(name);
- }
- }
-
- /**
- * @see jakarta.servlet.ServletRequest#getParameterMap()
- */
- @Override
- @SuppressWarnings("unchecked")
- public Map getParameterMap() {
- if (isMultipartRequest) {
- return getMultipartParameterMap();
- } else {
- return request.getParameterMap();
- }
- }
-
- // Package Private Methods ------------------------------------------------
-
- /**
- * Return the map of "multipart" request parameter map.
- *
- * @return the "multipart" request parameter map
- */
- @SuppressWarnings("unchecked")
- Map getMultipartParameterMap() {
- if (request.getAttribute(ClickServlet.MOCK_MODE_ENABLED) == null) {
- return multipartParameterMap;
- } else {
- // In mock mode return the request parameter map. This ensures
- // calling request.setParameter(x,y) works for both normal and
- // multipart requests.
- return request.getParameterMap();
- }
- }
-
- // Private Methods --------------------------------------------------------
-
- /**
- * Stores the specified value in a FileItem array in the map, under the
- * specified name. Thus two values stored under the same name will be
- * stored in the same array.
- *
- * @param map the map to add the specified name and value to
- * @param name the name of the map key
- * @param value the value to add to the FileItem array
- */
- private void addToMapAsFileItem(Map map, String name, FileItem value) {
- FileItem[] oldValues = map.get(name);
- FileItem[] newValues = null;
- if (oldValues == null) {
- newValues = new FileItem[] {value};
- } else {
- newValues = new FileItem[oldValues.length + 1];
- System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
- newValues[oldValues.length] = value;
- }
- map.put(name, newValues);
- }
-
- /**
- * Stores the specified value in an String array in the map, under the
- * specified name. Thus two values stored under the same name will be
- * stored in the same array.
- *
- * @param map the map to add the specified name and value to
- * @param name the name of the map key
- * @param value the value to add to the string array
- */
- private void addToMapAsString(Map map, String name, String value) {
- String[] oldValues = map.get(name);
- String[] newValues = null;
- if (oldValues == null) {
- newValues = new String[] {value};
- } else {
- newValues = new String[oldValues.length + 1];
- System.arraycopy(oldValues, 0, newValues, 0, oldValues.length);
- newValues[oldValues.length] = value;
- }
- map.put(name, newValues);
- }
-
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/ClickServlet.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/ClickServlet.java
deleted file mode 100644
index e8ff6b2fe8..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/ClickServlet.java
+++ /dev/null
@@ -1,2305 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.openidentityplatform.openam.click;
-
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.io.Writer;
-import java.lang.reflect.Field;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import jakarta.servlet.RequestDispatcher;
-import jakarta.servlet.ServletContext;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.UnavailableException;
-import jakarta.servlet.http.HttpServlet;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.servlet.http.HttpSession;
-
-import ognl.DefaultMemberAccess;
-import ognl.MemberAccess;
-import ognl.Ognl;
-import ognl.OgnlException;
-import ognl.TypeConverter;
-
-import org.openidentityplatform.openam.click.service.ConfigService;
-import org.openidentityplatform.openam.click.service.LogService;
-import org.openidentityplatform.openam.click.service.ResourceService;
-import org.apache.click.service.TemplateException;
-import org.openidentityplatform.openam.click.service.XmlConfigService;
-import org.openidentityplatform.openam.click.service.ConfigService.AutoBinding;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-import org.openidentityplatform.openam.click.util.ErrorPage;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-import org.openidentityplatform.openam.click.util.PageImports;
-import org.apache.click.util.PropertyUtils;
-import org.apache.click.util.RequestTypeConverter;
-import org.apache.commons.lang.ClassUtils;
-import org.apache.commons.lang.StringUtils;
-
-/**
- * Provides the Click application HttpServlet.
- *
- * Generally developers will simply configure the ClickServlet and
- * will not use it directly in their code. For a Click web application to
- * function the ClickServlet must be configured in the web
- * application's /WEB-INF/web.xml file. A simple web application which
- * maps all *.htm requests to a ClickServlet is provided below.
- *
- *
- *
- * By default the ClickServlet will attempt to load an application
- * configuration file using the path: /WEB-INF/click.xml
- *
- *
Servlet Mapping
- * By convention all Click page templates should have a .htm extension, and
- * the ClickServlet should be mapped to process all *.htm URL requests. With
- * this convention you have all the static HTML pages use a .html extension
- * and they will not be processed as Click pages.
- *
- *
Load On Startup
- * Note you should always set load-on-startup element to be 0 so the
- * servlet is initialized when the server is started. This will prevent any
- * delay for the first client which uses the application.
- *
- * The ClickServlet performs as much work as possible at startup to
- * improve performance later on. The Click start up and caching strategy is
- * configured with the Click application mode in the "click.xml" file.
- * See the User Guide for information on how to configure the application mode.
- *
- *
ConfigService
- *
- * A single application {@link ConfigService} instance is created by the ClickServlet at
- * startup. Once the ConfigService has been initialized it is stored in the
- * ServletContext using the key {@value org.apache.click.service.ConfigService#CONTEXT_NAME}.
- */
-public class ClickServlet extends HttpServlet {
-
- // -------------------------------------------------------------- Constants
-
- private static final long serialVersionUID = 1L;
-
- /**
- * The mock page reference request attribute: key:
- * mock_page_reference.
- *
- * This attribute stores the each Page instance as a request attribute.
- *
- * Note: a page is only stored as a request attribute
- * if the {@link #MOCK_MODE_ENABLED} attribute is set.
- */
- static final String MOCK_PAGE_REFERENCE = "mock_page_reference";
-
- /**
- * The mock mode request attribute: key:
- * mock_mode_enabled.
- *
- * If this attribute is set (the value does not matter) certain features
- * will be enabled which is needed for running Click in a mock environment.
- */
- static final String MOCK_MODE_ENABLED = "mock_mode_enabled";
-
- /**
- * The click application configuration service classname init parameter name:
- * "config-service-class".
- */
- protected final static String CONFIG_SERVICE_CLASS = "config-service-class";
-
- /**
- * The custom TypeConverter classname as an init parameter name:
- * &nbps; "type-converter-class".
- */
- protected final static String TYPE_CONVERTER_CLASS = "type-converter-class";
-
- /**
- * The forwarded request marker attribute: "click-forward".
- */
- protected final static String CLICK_FORWARD = "click-forward";
-
- /**
- * The Page to forward to request attribute: "click-page".
- */
- protected final static String FORWARD_PAGE = "forward-page";
-
- // ----------------------------------------------------- Instance Variables
-
- /** The click application configuration service. */
- protected ConfigService configService;
-
- /** The application log service. */
- protected LogService logger;
-
- /** The OGNL member access handler. */
- protected MemberAccess memberAccess;
-
- /** The application resource service. */
- protected ResourceService resourceService;
-
- /** The request parameters OGNL type converter. */
- protected TypeConverter typeConverter;
-
- /** The thread local page listeners. */
- private static final ThreadLocal>
- THREAD_LOCAL_INTERCEPTORS = new ThreadLocal<>();
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Initialize the Click servlet and the Velocity runtime.
- *
- * @see jakarta.servlet.GenericServlet#init()
- *
- * @throws ServletException if the application configuration service could
- * not be initialized
- */
- @Override
- public void init() throws ServletException {
-
- try {
-
- // Create and initialize the application config service
- configService = createConfigService(getServletContext());
- initConfigService(getServletContext());
- logger = configService.getLogService();
-
- if (logger.isInfoEnabled()) {
- logger.info("Click " + ClickUtils.getClickVersion()
- + " initialized in " + configService.getApplicationMode()
- + " mode");
- }
-
- resourceService = configService.getResourceService();
-
- } catch (Throwable e) {
- // In mock mode this exception can occur if click.xml is not
- // available.
- if (getServletContext().getAttribute(MOCK_MODE_ENABLED) != null) {
- return;
- }
-
- e.printStackTrace();
-
- String msg = "error while initializing Click servlet; throwing "
- + "jakarta.servlet.UnavailableException";
-
- log(msg, e);
-
- throw new UnavailableException(e.toString());
- }
- }
-
- /**
- * @see jakarta.servlet.GenericServlet#destroy()
- */
- @Override
- public void destroy() {
-
- try {
-
- // Destroy the application config service
- destroyConfigService(getServletContext());
-
- } catch (Throwable e) {
- // In mock mode this exception can occur if click.xml is not
- // available.
- if (getServletContext().getAttribute(MOCK_MODE_ENABLED) != null) {
- return;
- }
-
- e.printStackTrace();
-
- String msg = "error while destroying Click servlet, throwing "
- + "jakarta.servlet.UnavailableException";
-
- log(msg, e);
-
- } finally {
- // Dereference the application config service
- configService = null;
- }
-
- super.destroy();
- }
-
- // ------------------------------------------------------ Protected Methods
-
- /**
- * Handle HTTP GET requests. This method will delegate the request to
- * {@link #handleRequest(HttpServletRequest, HttpServletResponse, boolean)}.
- *
- * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
- *
- * @param request the servlet request
- * @param response the servlet response
- * @throws ServletException if click app has not been initialized
- * @throws IOException if an I/O error occurs
- */
- @Override
- protected void doGet(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
-
- handleRequest(request, response, false);
- }
-
- /**
- * Handle HTTP POST requests. This method will delegate the request to
- * {@link #handleRequest(HttpServletRequest, HttpServletResponse, boolean)}.
- *
- * @see HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
- *
- * @param request the servlet request
- * @param response the servlet response
- * @throws ServletException if click app has not been initialized
- * @throws IOException if an I/O error occurs
- */
- @Override
- protected void doPost(HttpServletRequest request,
- HttpServletResponse response) throws ServletException, IOException {
-
- handleRequest(request, response, true);
- }
-
- /**
- * Handle the given servlet request and render the results to the
- * servlet response.
- *
- * If an exception occurs within this method the exception will be delegated
- * to:
- *
- * {@link #handleException(HttpServletRequest, HttpServletResponse, boolean, Throwable, Class)}
- *
- * @param request the servlet request to process
- * @param response the servlet response to render the results to
- * @param isPost determines whether the request is a POST
- * @throws IOException if resource request could not be served
- */
- protected void handleRequest(HttpServletRequest request,
- HttpServletResponse response, boolean isPost) throws IOException {
-
- // Handle requests for click resources, i.e. CSS, JS and image files
- if (resourceService.isResourceRequest(request)) {
- resourceService.renderResource(request, response);
- return;
- }
-
- long startTime = System.currentTimeMillis();
-
- if (logger.isDebugEnabled()) {
- HtmlStringBuffer buffer = new HtmlStringBuffer(200);
- buffer.append(request.getMethod());
- buffer.append(" ");
- buffer.append(request.getRequestURL());
- logger.debug(buffer);
- }
-
- // Handle click page requests
- Page page = null;
- try {
-
- ActionEventDispatcher eventDispatcher = createActionEventDispatcher();
- // Bind ActionEventDispatcher to current thread
- ActionEventDispatcher.pushThreadLocalDispatcher(eventDispatcher);
-
- ControlRegistry controlRegistry = createControlRegistry();
- // Bind ControlRegistry to current thread
- ControlRegistry.pushThreadLocalRegistry(controlRegistry);
-
- Context context = createContext(request, response, isPost);
- // Bind context to current thread
- Context.pushThreadLocalContext(context);
-
- // Check for fatal error that occurred while creating Context
- Throwable error = (Throwable) request.getAttribute(Context.CONTEXT_FATAL_ERROR);
-
- if (error != null) {
- // Process exception through Click's exception handler.
- if (error instanceof Exception) {
- throw (Exception) error;
- }
- // Errors are not handled by Click, let the server handle it
- if (error instanceof Error) {
- throw (Error) error;
- } else {
- // Throwables are not handled by Click, let the server handle it
- throw new RuntimeException(error);
- }
- }
-
- page = createPage(context);
-
- // If no page created, then an PageInterceptor has aborted processing
- if (page == null) {
- return;
- }
-
- if (page.isStateful()) {
- synchronized (page) {
- processPage(page);
- processPageOnDestroy(page, startTime);
- // Mark page as already destroyed for finally block
- page = null;
- }
-
- } else {
- processPage(page);
- }
-
- } catch (Exception e) {
- Class extends Page> pageClass =
- configService.getPageClass(ClickUtils.getResourcePath(request));
-
- handleException(request, response, isPost, e, pageClass);
-
- } catch (ExceptionInInitializerError eiie) {
- Throwable cause = eiie.getException();
- cause = (cause != null) ? cause : eiie;
-
- Class extends Page> pageClass =
- configService.getPageClass(ClickUtils.getResourcePath(request));
-
- handleException(request, response, isPost, cause, pageClass);
-
- } finally {
-
- try {
- if (page != null) {
- if (page.isStateful()) {
- synchronized (page) {
- processPageOnDestroy(page, startTime);
- }
-
- } else {
- processPageOnDestroy(page, startTime);
- }
- }
-
- for (PageInterceptor interceptor : getThreadLocalInterceptors()) {
- interceptor.postDestroy(page);
- }
-
- setThreadLocalInterceptors(null);
-
- } finally {
- // Only clear the context when running in normal mode.
- if (request.getAttribute(MOCK_MODE_ENABLED) == null) {
- Context.popThreadLocalContext();
- }
- ControlRegistry.popThreadLocalRegistry();
- ActionEventDispatcher.popThreadLocalDispatcher();
- }
- }
- }
-
- /**
- * Provides the application exception handler. The application exception
- * will be delegated to the configured error page. The default error page is
- * {@link ErrorPage} and the page template is "click/error.htm"
- * Applications which wish to provide their own customized error handling
- * must subclass ErrorPage and specify their page in the
- * "/WEB-INF/click.xml" application configuration file. For example:
- *
- *
- *
- * This method is designed to be overridden by applications providing their
- * own page creation patterns.
- *
- * A typical example of this would be with Inversion of Control (IoC)
- * frameworks such as Spring or HiveMind. For example a Spring application
- * could override this method and use a ApplicationContext to instantiate
- * new Page objects:
- *
- *
- * @param path the request page path
- * @param pageClass the page Class the request is mapped to
- * @param request the page request
- * @return a new Page object
- * @throws Exception if an error occurs creating the Page
- */
- protected Page newPageInstance(String path, Class extends Page> pageClass,
- HttpServletRequest request) throws Exception {
-
- return pageClass.newInstance();
- }
-
- /**
- * Provides an extension point for ClickServlet sub classes to activate
- * stateful page which may have been deserialized.
- *
- * This method does nothing and is designed for extension.
- *
- * @param page the page instance to activate
- */
- protected void activatePageInstance(Page page) {
- }
-
- /**
- * Return a new VelocityContext for the given pages model and Context.
- *
- * The following values automatically added to the VelocityContext:
- *
- *
any public Page fields using the fields name
- *
context - the Servlet context path, e.g. /mycorp
- *
format - the {@link org.apache.click.util.Format} object for formatting
- * the display of objects
- *
imports - the {@link org.apache.click.util.PageImports} object
- *
messages - the page messages bundle
- *
path - the page of the page template to render
- *
request - the pages servlet request
- *
response - the pages servlet request
- *
session - the {@link org.apache.click.util.SessionMap} adaptor for the
- * users HttpSession
- *
- *
- * @see org.openidentityplatform.openam.click.util.ClickUtils#createTemplateModel(Page, Context)
- *
- * @param page the page to create a VelocityContext for
- * @return a new VelocityContext
- */
- @SuppressWarnings("deprecation")
- protected Map createTemplateModel(final Page page) {
-
- if (configService.getAutoBindingMode() != AutoBinding.NONE) {
-
- processPageFields(page, new ClickServlet.FieldCallback() {
- public void processField(String fieldName, Object fieldValue) {
- if (fieldValue instanceof Control == false) {
- page.addModel(fieldName, fieldValue);
-
- } else {
- // Add any controls not already added to model
- Control control = (Control) fieldValue;
- if (!page.getModel().containsKey(control.getName())) {
- page.addControl(control);
- }
- }
- }
- });
- }
-
- final Context context = page.getContext();
- final Map model = ClickUtils.createTemplateModel(page, context);
-
- PageImports pageImports = page.getPageImports();
- pageImports.populateTemplateModel(model);
-
- return model;
- }
-
- /**
- * Set the HTTP headers in the servlet response. The Page response headers
- * are defined in {@link Page#getHeaders()}.
- *
- * @param response the response to set the headers in
- * @param headers the map of HTTP headers to set in the response
- */
- protected void setPageResponseHeaders(HttpServletResponse response,
- Map headers) {
-
- for (Map.Entry entry : headers.entrySet()) {
- String name = entry.getKey();
- Object value = entry.getValue();
-
- if (value instanceof String) {
- String strValue = (String) value;
- if (!strValue.equalsIgnoreCase("Content-Encoding")) {
- response.setHeader(name, strValue);
- }
-
- } else if (value instanceof Date) {
- long time = ((Date) value).getTime();
- response.setDateHeader(name, time);
-
- } else if (value instanceof Integer) {
- int intValue = (Integer) value;
- response.setIntHeader(name, intValue);
-
- } else if (value != null) {
- throw new IllegalStateException("Invalid Page header value type: "
- + value.getClass() + ". Header value must of type String, Date or Integer.");
- }
- }
- }
-
- /**
- * Set the page model, context, format, messages and path as request
- * attributes to support JSP rendering. These request attributes include:
- *
- *
any public Page fields using the fields name
- *
context - the Servlet context path, e.g. /mycorp
- *
format - the {@link org.apache.click.util.Format} object for
- * formatting the display of objects
- *
forward - the page forward path, if defined
- *
imports - the {@link org.apache.click.util.PageImports} object
- *
messages - the page messages bundle
- *
path - the page of the page template to render
- *
- *
- * @param page the page to set the request attributes on
- */
- @SuppressWarnings("deprecation")
- protected void setRequestAttributes(final Page page) {
- final HttpServletRequest request = page.getContext().getRequest();
-
- processPageFields(page, new ClickServlet.FieldCallback() {
- public void processField(String fieldName, Object fieldValue) {
- if (fieldValue instanceof Control == false) {
- request.setAttribute(fieldName, fieldValue);
- } else {
- // Add any controls not already added to model
- Control control = (Control) fieldValue;
- if (!page.getModel().containsKey(control.getName())) {
- page.addControl(control);
- }
- }
- }
- });
-
- Map model = page.getModel();
- for (Map.Entry entry : model.entrySet()) {
- String name = entry.getKey();
- Object value = entry.getValue();
-
- request.setAttribute(name, value);
- }
-
- request.setAttribute("context", request.getContextPath());
- if (model.containsKey("context")) {
- String msg = page.getClass().getName() + " on " + page.getPath()
- + " model contains an object keyed with reserved "
- + "name \"context\". The request attribute "
- + "has been replaced with the request "
- + "context path";
- logger.warn(msg);
- }
-
- request.setAttribute("format", page.getFormat());
- if (model.containsKey("format")) {
- String msg = page.getClass().getName() + " on " + page.getPath()
- + " model contains an object keyed with reserved "
- + "name \"format\". The request attribute "
- + "has been replaced with the format object";
- logger.warn(msg);
- }
-
- request.setAttribute("forward", page.getForward());
- if (model.containsKey("forward")) {
- String msg = page.getClass().getName() + " on " + page.getPath()
- + " model contains an object keyed with reserved "
- + "name \"forward\". The request attribute "
- + "has been replaced with the page path";
- logger.warn(msg);
- }
-
- request.setAttribute("path", page.getPath());
- if (model.containsKey("path")) {
- String msg = page.getClass().getName() + " on " + page.getPath()
- + " model contains an object keyed with reserved "
- + "name \"path\". The request attribute "
- + "has been replaced with the page path";
- logger.warn(msg);
- }
-
- request.setAttribute("messages", page.getMessages());
- if (model.containsKey("messages")) {
- String msg = page.getClass().getName() + " on " + page.getPath()
- + " model contains an object keyed with reserved "
- + "name \"messages\". The request attribute "
- + "has been replaced with the page messages";
- logger.warn(msg);
- }
-
- PageImports pageImports = page.getPageImports();
- pageImports.populateRequest(request, model);
- }
-
- /**
- * Return the request parameters OGNL TypeConverter. This method
- * performs a lazy load of the TypeConverter object, using the classname
- * defined in the Servlet init parameter type-converter-class,
- * if this parameter is not defined this method will return a
- * {@link RequestTypeConverter} instance.
- *
- * @return the request parameters OGNL TypeConverter
- * @throws RuntimeException if the TypeConverter instance could not be created
- */
- @SuppressWarnings("unchecked")
- protected TypeConverter getTypeConverter() throws RuntimeException {
- if (typeConverter == null) {
- Class extends TypeConverter> converter = RequestTypeConverter.class;
-
- try {
- String classname = getInitParameter(TYPE_CONVERTER_CLASS);
- if (StringUtils.isNotBlank(classname)) {
- converter = ClickUtils.classForName(classname);
- }
-
- typeConverter = converter.newInstance();
-
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- return typeConverter;
- }
-
- /**
- * Creates and returns a new Context instance for this path, class and
- * request.
- *
- * The default implementation of this method simply creates a new Context
- * instance.
- *
- * Subclasses can override this method to provide a custom Context.
- *
- * @param request the page request
- * @param response the page response
- * @param isPost true if this is a post request, false otherwise
- * @return a Context instance
- */
- protected Context createContext(HttpServletRequest request,
- HttpServletResponse response, boolean isPost) {
-
- Context context = new Context(getServletContext(),
- getServletConfig(),
- request,
- response,
- isPost,
- this);
- return context;
- }
-
- /**
- * Creates and returns a new ActionEventDispatcher instance.
- *
- * @return the new ActionEventDispatcher instance
- */
- protected ActionEventDispatcher createActionEventDispatcher() {
- return new ActionEventDispatcher(configService);
- }
-
- /**
- * Creates and returns a new ControlRegistry instance.
- *
- * @return the new ControlRegistry instance
- */
- protected ControlRegistry createControlRegistry() {
- return new ControlRegistry(configService);
- }
-
- /**
- * Creates and returns a new ErrorPage instance.
- *
- * This method creates the custom page as specified in click.xml,
- * otherwise the default ErrorPage instance.
- *
- * Subclasses can override this method to provide custom ErrorPages tailored
- * for specific exceptions.
- *
- * Note you can safely use {@link Context} in this
- * method.
- *
- * @param pageClass the page class with the error
- * @param exception the error causing exception
- * @return a new ErrorPage instance
- */
- protected ErrorPage createErrorPage(Class extends Page> pageClass, Throwable exception) {
- try {
- return (ErrorPage) configService.getErrorPageClass().newInstance();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Return the application configuration service instance.
- *
- * @return the application configuration service instance
- */
- protected ConfigService getConfigService() {
- return configService;
- }
-
- /**
- * Return a new Page instance for the given path. The path must start with
- * a "/".
- *
- * @param path the path which maps to a Page class
- * @param request the Page request
- * @return a new Page object
- * @throws IllegalArgumentException if the Page is not found
- */
- @SuppressWarnings("unchecked")
- protected T createPage(String path, HttpServletRequest request) {
- Class extends Page> pageClass = getConfigService().getPageClass(path);
-
- if (pageClass == null) {
- String msg = "No Page class configured for path: " + path;
- throw new IllegalArgumentException(msg);
- }
-
- return (T) initPage(path, pageClass, request);
- }
-
- /**
- * Return a new Page instance for the page Class.
- *
- * @param pageClass the class of the Page to create
- * @param request the Page request
- * @return a new Page object
- * @throws IllegalArgumentException if the Page Class is not configured
- * with a unique path
- */
- @SuppressWarnings("unchecked")
- protected T createPage(Class pageClass, HttpServletRequest request) {
- String path = getConfigService().getPagePath(pageClass);
-
- if (path == null) {
- String msg =
- "No path configured for Page class: " + pageClass.getName();
- throw new IllegalArgumentException(msg);
- }
-
- return (T) initPage(path, pageClass, request);
- }
-
- /**
- * Creates and returns a new PageImports instance for the specified page.
- *
- * @param page the page to create a new PageImports instance for
- * @return the new PageImports instance
- */
- protected PageImports createPageImports(Page page) {
- return new PageImports(page);
- }
-
- // TODO refactor Page events into its a separate Livecycle class. This will
- // take some of the responsibility off ClickServlet and remove code duplication
-
- /**
- * Process the given page events, invoking the "on" event callback methods
- * and directing the response.
- *
- * @param page the page which events to process
- * @param context the request context
- * @throws Exception if an error occurs
- */
- protected void processAjaxPageEvents(Page page, Context context) throws Exception {
-
- ActionEventDispatcher eventDispatcher = ActionEventDispatcher.getThreadLocalDispatcher();
-
- ControlRegistry controlRegistry = ControlRegistry.getThreadLocalRegistry();
-
- // TODO Ajax requests shouldn't reach this code path since errors
- // are rendered directly
- if (page instanceof ErrorPage) {
- ErrorPage errorPage = (ErrorPage) page;
- errorPage.setMode(configService.getApplicationMode());
-
- // Notify the dispatcher and registry of the error
- eventDispatcher.errorOccurred(errorPage.getError());
- controlRegistry.errorOccurred(errorPage.getError());
- }
-
- boolean continueProcessing = performOnSecurityCheck(page, context);
-
- ActionResult actionResult = null;
- if (continueProcessing) {
-
- // Handle page method
- String pageAction = context.getRequestParameter(Page.PAGE_ACTION);
- if (pageAction != null) {
- continueProcessing = false;
-
- // Returned action result could be null
- actionResult = performPageAction(page, pageAction, context);
-
- controlRegistry.processPreResponse(context);
- controlRegistry.processPreRenderHeadElements(context);
-
- renderActionResult(actionResult, page, context);
- }
- }
-
- if (continueProcessing) {
- performOnInit(page, context);
-
- // TODO: Ajax doesn't support forward. Is it still necessary to
- // check isForward?
- if (controlRegistry.hasAjaxTargetControls() && !context.isForward()) {
-
- // Perform onProcess for regsitered Ajax target controls
- processAjaxTargetControls(context, eventDispatcher, controlRegistry);
-
- // Fire AjaxBehaviors registered during the onProcess event
- // The target AjaxBehavior will set the eventDispatcher action
- // result instance to render
- eventDispatcher.fireAjaxBehaviors(context);
-
- // Ensure we execute the beforeResponse and beforeGetHeadElements
- // for Ajax requests
- controlRegistry.processPreResponse(context);
- controlRegistry.processPreRenderHeadElements(context);
-
- actionResult = eventDispatcher.getActionResult();
-
- // Render the actionResult
- renderActionResult(actionResult, page, context);
-
- } else {
-
- // If no Ajax target controls have been registered fallback to
- // the old behavior of processing and rendering the page template
- if (logger.isTraceEnabled()) {
- String msg = " *no* Ajax target controls have been registered."
- + " Will process the page as a normal non Ajax request.";
- logger.trace(msg);
- }
-
- continueProcessing = performOnProcess(page, context, eventDispatcher);
-
- if (continueProcessing) {
- performOnPostOrGet(page, context, context.isPost());
-
- performOnRender(page, context);
- }
-
- // If Ajax request does not target a valid page, return a 404
- // repsonse status, allowing JavaScript to display a proper message
- if (ConfigService.NOT_FOUND_PATH.equals(page.getPath())) {
- HttpServletResponse response = context.getResponse();
- response.setStatus(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
-
- controlRegistry.processPreResponse(context);
- controlRegistry.processPreRenderHeadElements(context);
- performRender(page, context);
- }
- } else {
- // If security check fails for an Ajax request, Click returns without
- // any rendering. It is up to the user to render an ActionResult
- // in the onSecurityCheck event
- // Note: this code path is also followed if a pageAction is invoked
- }
- }
-
- /**
- * Process all Ajax target controls and return true if the page should continue
- * processing, false otherwise.
- *
- * @param context the request context
- * @param eventDispatcher the event dispatcher
- * @param controlRegistry the control registry
- * @return true if the page should continue processing, false otherwise
- */
- protected boolean processAjaxTargetControls(Context context,
- ActionEventDispatcher eventDispatcher, ControlRegistry controlRegistry) {
-
- boolean continueProcessing = true;
-
- // Resolve the Ajax target control for this request
- Control ajaxTarget = resolveAjaxTargetControl(context, controlRegistry);
-
- if (ajaxTarget != null) {
-
- // Process the control
- if (!ajaxTarget.onProcess()) {
- continueProcessing = false;
- }
-
- // Log a trace if no behavior was registered after processing the control
- if (logger.isTraceEnabled()) {
-
- HtmlStringBuffer buffer = new HtmlStringBuffer();
- String controlClassName = ClassUtils.getShortClassName(ajaxTarget.getClass());
- buffer.append(" invoked: '");
- buffer.append(ajaxTarget.getName());
- buffer.append("' ").append(controlClassName);
- buffer.append(".onProcess() : ").append(continueProcessing);
- logger.trace(buffer.toString());
-
- if (!eventDispatcher.hasAjaxBehaviorSourceSet()) {
- logger.trace(" *no* AjaxBehavior was registered while processing the control");
- }
- }
- }
-
- return continueProcessing;
- }
-
- /**
- * Provides an Ajax exception handler. Exceptions are wrapped inside a
- * div element and streamed back to the browser. The response status
- * is set to an {@link jakarta.servlet.http.HttpServletResponse#SC_INTERNAL_SERVER_ERROR HTTP 500 error}
- * which allows the JavaScript that initiated the Ajax request to handle
- * the error as appropriate.
- *
- * If Click is running in development modes the exception stackTrace
- * will be rendered, in production modes an error message is
- * rendered.
- *
- * Below is an example error response:
- *
- *
- * <div id='errorReport' class='errorReport'>
- * The application encountered an unexpected error.
- * </div>
- *
- *
- * @param request the servlet request
- * @param response the servlet response
- * @param isPost determines whether the request is a POST
- * @param exception the error causing exception
- * @param pageClass the page class with the error
- */
- protected void handleAjaxException(HttpServletRequest request,
- HttpServletResponse response, boolean isPost, Throwable exception,
- Class extends Page> pageClass) {
-
- // If an exception occurs during an Ajax request, stream
- // the exception instead of creating an ErrorPage
- try {
-
- PrintWriter writer = null;
-
- try {
- writer = getPrintWriter(response);
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-
- // TODO: use an ErrorReport instance instead?
- writer.write("
");
- } finally {
- if (writer != null) {
- writer.flush();
- }
- }
- } catch (Throwable error) {
- logger.error(error.getMessage(), error);
- throw new RuntimeException(error);
- }
- logger.error("handleException: ", exception);
- }
-
- // ------------------------------------------------ Package Private Methods
-
- /**
- * Return the OGNL MemberAccess. This method performs a lazy load
- * of the MemberAccess object, using a {@link DefaultMemberAccess} instance.
- *
- * @return the OGNL MemberAccess
- */
- MemberAccess getMemberAccess() {
- if (memberAccess == null) {
- memberAccess = new DefaultMemberAccess(true);
- }
- return memberAccess;
- }
-
- /**
- * Create a Click application ConfigService instance.
- *
- * @param servletContext the Servlet Context
- * @return a new application ConfigService instance
- * @throws Exception if an initialization error occurs
- */
- @SuppressWarnings("unchecked")
- ConfigService createConfigService(ServletContext servletContext)
- throws Exception {
-
- Class extends ConfigService> serviceClass = XmlConfigService.class;
-
- String classname = servletContext.getInitParameter(CONFIG_SERVICE_CLASS);
- if (StringUtils.isNotBlank(classname)) {
- serviceClass = ClickUtils.classForName(classname);
- }
-
- return serviceClass.newInstance();
- }
-
- /**
- * Initialize the Click application ConfigService instance and bind
- * it as a ServletContext attribute using the key
- * "org.apache.click.service.ConfigService".
- *
- * This method will use the configuration service class specified by the
- * {@link #CONFIG_SERVICE_CLASS} parameter, otherwise it will create a
- * {@link org.apache.click.service.XmlConfigService} instance.
- *
- * @param servletContext the servlet context to retrieve the
- * {@link #CONFIG_SERVICE_CLASS} from
- * @throws RuntimeException if the configuration service cannot be
- * initialized
- */
- void initConfigService(ServletContext servletContext) {
-
- if (configService != null) {
- try {
-
- // Note this order is very important as components need to lookup
- // the configService out of the ServletContext while the service
- // is being initialized.
- servletContext.setAttribute(ConfigService.CONTEXT_NAME, configService);
-
- // Initialize the ConfigService instance
- configService.onInit(servletContext);
-
- } catch (Exception e) {
-
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- /**
- * Destroy the application configuration service instance and remove
- * it from the ServletContext attribute.
- *
- * @param servletContext the servlet context
- * @throws RuntimeException if the configuration service cannot be
- * destroyed
- */
- void destroyConfigService(ServletContext servletContext) {
-
- if (configService != null) {
-
- try {
- configService.onDestroy();
-
- } catch (Exception e) {
-
- if (e instanceof RuntimeException) {
- throw (RuntimeException) e;
- } else {
- throw new RuntimeException(e);
- }
- } finally {
- servletContext.setAttribute(ConfigService.CONTEXT_NAME, null);
- }
- }
- }
-
- /**
- * Process all the Pages public fields using the given callback.
- *
- * @param page the page to obtain the fields from
- * @param callback the fields iterator callback
- */
- void processPageFields(Page page, ClickServlet.FieldCallback callback) {
-
- Field[] fields = configService.getPageFieldArray(page.getClass());
-
- if (fields != null) {
- for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
-
- try {
- Object fieldValue = field.get(page);
-
- if (fieldValue != null) {
- callback.processField(field.getName(), fieldValue);
- }
-
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- List getThreadLocalInterceptors() {
- List listeners =
- THREAD_LOCAL_INTERCEPTORS.get();
-
- if (listeners != null) {
- return listeners;
- } else {
- return Collections.emptyList();
- }
- }
-
- void setThreadLocalInterceptors(List listeners) {
- THREAD_LOCAL_INTERCEPTORS.set(listeners);
- }
-
- /**
- * Retrieve a writer instance for the given context.
- *
- * @param response the servlet response
- * @return a writer instance
- * @throws IOException if an input or output exception occurred
- */
- Writer getWriter(HttpServletResponse response) throws IOException {
- try {
-
- return response.getWriter();
-
- } catch (IllegalStateException ignore) {
- // If writer cannot be retrieved fallback to OutputStream. CLK-644
- return new OutputStreamWriter(response.getOutputStream(),
- response.getCharacterEncoding());
- }
- }
-
- /**
- * Return a PrintWriter instance for the given response.
- *
- * @param response the servlet response
- * @return a PrintWriter instance
- */
- PrintWriter getPrintWriter(HttpServletResponse response) throws IOException {
- Writer writer = getWriter(response);
- if (writer instanceof PrintWriter) {
- return (PrintWriter) writer;
- }
- return new PrintWriter(writer);
- }
-
- /**
- * Return true if this is an ajax request, false otherwise.
- *
- * @param request the servlet request
- * @return true if this is an ajax request, false otherwise
- */
- boolean isAjaxRequest(HttpServletRequest request) {
- boolean isAjaxRequest = false;
- if (Context.hasThreadLocalContext()) {
- Context context = Context.getThreadLocalContext();
- if (context.isAjaxRequest()) {
- isAjaxRequest = true;
- }
- } else {
- isAjaxRequest = ClickUtils.isAjaxRequest(request);
- }
- return isAjaxRequest;
- }
-
- // ---------------------------------------------------------- Inner Classes
-
- /**
- * Field iterator callback.
- */
- static interface FieldCallback {
-
- /**
- * Callback method invoked for each field.
- *
- * @param fieldName the field name
- * @param fieldValue the field value
- */
- public void processField(String fieldName, Object fieldValue);
-
- }
-
- // Private methods --------------------------------------------------------
-
- /**
- * Resolve and return the Ajax target control for this request or null if no
- * Ajax target was found.
- *
- * @param context the request context
- * @param controlRegistry the control registry
- * @return the target Ajax target control or null if no Ajax target was found
- */
- private Control resolveAjaxTargetControl(Context context, ControlRegistry controlRegistry) {
-
- Control ajaxTarget = null;
-
- if (logger.isTraceEnabled()) {
- logger.trace(" the following controls have been registered as potential Ajax targets:");
- if (controlRegistry.hasAjaxTargetControls()) {
- for (Control control : controlRegistry.getAjaxTargetControls()) {
- HtmlStringBuffer buffer = new HtmlStringBuffer();
- String controlClassName = ClassUtils.getShortClassName(control.getClass());
- buffer.append(" ").append(controlClassName);
- buffer.append(": name='").append(control.getName()).append("'");
- logger.trace(buffer.toString());
- }
- } else {
- logger.trace(" *no* control has been registered");
- }
- }
-
- for (Control control : controlRegistry.getAjaxTargetControls()) {
-
- if (control.isAjaxTarget(context)) {
- ajaxTarget = control;
- // The first matching control will be processed. Multiple matching
- // controls are not supported
- break;
- }
- }
-
- if (logger.isTraceEnabled()) {
- if (ajaxTarget == null) {
- String msg = " *no* target control was found for the Ajax request";
- logger.trace(msg);
-
- } else {
- HtmlStringBuffer buffer = new HtmlStringBuffer();
- buffer.append(" invoked: '");
- buffer.append(ajaxTarget.getName()).append("' ");
- String className = ClassUtils.getShortClassName(ajaxTarget.getClass());
- buffer.append(className);
- buffer.append(".isAjaxTarget() : true (Ajax target control found)");
- logger.trace(buffer.toString());
- }
- }
-
- return ajaxTarget;
- }
-
- /**
- * Log the request parameter names and values.
- *
- * @param request the HTTP servlet request
- */
- private void logRequestParameters(HttpServletRequest request) {
-
- Map requestParams = new TreeMap();
-
- Enumeration> e = request.getParameterNames();
- while (e.hasMoreElements()) {
- String name = e.nextElement().toString();
- String[] values = request.getParameterValues(name);
- requestParams.put(name, values);
- }
-
- for (Map.Entry entry : requestParams.entrySet()) {
- String name = entry.getKey();
- String[] values = entry.getValue();
-
- HtmlStringBuffer buffer = new HtmlStringBuffer(40);
- buffer.append(" request param: " + name + "=");
-
- if (values == null) {
- // ignore
- } else if (values.length == 1) {
-
- buffer.append(ClickUtils.limitLength(values[0], 40));
- } else {
-
- for (int i = 0; i < values.length; i++) {
- if (i == 0) {
- buffer.append('[');
- } else {
- buffer.append(", ");
- }
- buffer.append(ClickUtils.limitLength(values[i], 40));
- }
- buffer.append("]");
- }
-
- logger.trace(buffer.toString());
- }
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/Context.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/Context.java
deleted file mode 100644
index ac6ddbe9b4..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/Context.java
+++ /dev/null
@@ -1,973 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click;
-
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.Map;
-
-import jakarta.servlet.ServletConfig;
-import jakarta.servlet.ServletContext;
-import jakarta.servlet.ServletRequest;
-import jakarta.servlet.http.Cookie;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletRequestWrapper;
-import jakarta.servlet.http.HttpServletResponse;
-import jakarta.servlet.http.HttpSession;
-
-import org.openidentityplatform.openam.click.service.FileUploadService;
-import org.openidentityplatform.openam.click.service.LogService;
-import org.openidentityplatform.openam.click.service.MessagesMapService;
-import org.openidentityplatform.openam.click.service.TemplateService;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-import org.apache.click.util.FlashAttribute;
-import org.apache.commons.fileupload.FileItem;
-
-/**
- * Provides the HTTP request context information for pages and controls.
- * A new Context object is created for each Page request. The request Context
- * object can be obtained from the thread local variable via the
- * {@link org.openidentityplatform.openam.click.Context#getThreadLocalContext()} method.
- */
-public class Context {
-
- // -------------------------------------------------------------- Constants
-
- /** The user's session Locale key: locale. */
- public static final String LOCALE = "locale";
-
- /**
- * The attribute key used for storing any error that occurred while
- * Context is created.
- */
- static final String CONTEXT_FATAL_ERROR = "_context_fatal_error";
-
- /** The thread local context stack. */
- private static final ThreadLocal THREAD_LOCAL_CONTEXT_STACK = new ThreadLocal<>();
-
- // ----------------------------------------------------- Instance Variables
-
- /** The servlet context. */
- protected final ServletContext context;
-
- /** The servlet config. */
- protected final ServletConfig config;
-
- /** The HTTP method is POST flag. */
- protected final boolean isPost;
-
- /** The servlet response. */
- protected final HttpServletResponse response;
-
- /** The click services interface. */
- final ClickServlet clickServlet;
-
- /** The servlet request. */
- final HttpServletRequest request;
-
- // ----------------------------------------------------------- Constructors
-
- /**
- * Create a new request context.
- *
- * @param context the servlet context
- * @param config the servlet config
- * @param request the servlet request
- * @param response the servlet response
- * @param isPost the servlet request is a POST
- * @param clickServlet the click servlet instance
- */
- public Context(ServletContext context, ServletConfig config,
- HttpServletRequest request, HttpServletResponse response,
- boolean isPost, ClickServlet clickServlet) {
-
- this.clickServlet = clickServlet;
- this.context = context;
- this.config = config;
- this.isPost = isPost;
-
- Context.ContextStack contextStack = getContextStack();
-
- if (contextStack.size() == 0) {
-
- // CLK-312. Apply request.setCharacterEncoding before wrapping
- // request in ClickRequestWrapper
- String charset = clickServlet.getConfigService().getCharset();
- if (charset != null) {
-
- try {
- request.setCharacterEncoding(charset);
-
- } catch (UnsupportedEncodingException ex) {
- String msg =
- "The character encoding " + charset + " is invalid.";
- LogService logService =
- clickServlet.getConfigService().getLogService();
- logService.warn(msg, ex);
- }
- }
-
- FileUploadService fus =
- clickServlet.getConfigService().getFileUploadService();
-
- this.request = new ClickRequestWrapper(request, fus);
-
- } else {
- this.request = request;
- }
- this.response = response;
- }
-
- /**
- * Package private constructor to support Mock objects.
- *
- * @param request the servlet request
- * @param response the servlet response
- */
- Context(HttpServletRequest request, HttpServletResponse response) {
- // This method should be removed once the deprecated MockContext
- // constructor is removed
- this.request = request;
- this.response = response;
- this.clickServlet = null;
- this.context = null;
- this.config = null;
- this.isPost = "POST".equalsIgnoreCase(request.getMethod());
- }
-
- // --------------------------------------------------------- Public Methods
-
- /**
- * Return the thread local request context instance.
- *
- * @return the thread local request context instance.
- * @throws RuntimeException if a Context is not available on the thread.
- */
- public static Context getThreadLocalContext() {
- return getContextStack().peek();
- }
-
- /**
- * Returns true if a Context instance is available on the current thread,
- * false otherwise. Unlike {@link #getThreadLocalContext()} this method
- * can safely be used and will not throw an exception if Context is not
- * available on the current thread.
- *
- * This method is very useful inside a {@link Control} constructor which
- * might need access to the Context. As Controls could potentially be
- * instantiated during Click startup (in order to deploy their resources),
- * this check can be used to determine whether Context is available or not.
- *
- * For example:
- *
- *
- * public MyControl extends AbstractControl {
- * public MyControl(String name) {
- * if (Context.hasThreadLocalContext()) {
- * // Context is available, meaning a user initiated a web
- * // request
- * Context context = getContext();
- * String state = (String) context.getSessionAttribute(name);
- * setValue(state);
- * } else {
- * // No Context is available, meaning this is most probably
- * // the application startup and deployment phase.
- * }
- * }
- * }
- *
- *
- * @return true if a Context instance is available on the current thread,
- * false otherwise
- */
- public static boolean hasThreadLocalContext() {
- Context.ContextStack contextStack = THREAD_LOCAL_CONTEXT_STACK.get();
- if (contextStack == null) {
- return false;
- }
- return !contextStack.isEmpty();
- }
-
- /**
- * Returns the servlet request.
- *
- * @return HttpServletRequest
- */
- public HttpServletRequest getRequest() {
- return request;
- }
-
- /**
- * Returns the servlet response.
- *
- * @return HttpServletResponse
- */
- public HttpServletResponse getResponse() {
- return response;
- }
-
- /**
- * Returns the servlet config.
- *
- * @return ServletConfig
- */
- public ServletConfig getServletConfig() {
- return config;
- }
-
- /**
- * Returns the servlet context.
- *
- * @return ServletContext
- */
- public ServletContext getServletContext() {
- return context;
- }
-
- /**
- * Return the user's HttpSession, creating one if necessary.
- *
- * @return the user's HttpSession, creating one if necessary.
- */
- public HttpSession getSession() {
- return request.getSession();
- }
-
- /**
- * Return the page resource path from the request. For example:
- *
- *
- * @return the page resource path from the request
- */
- public String getResourcePath() {
- return ClickUtils.getResourcePath(request);
- }
-
- /**
- * Return true if the request has been forwarded. A forwarded request
- * will contain a {@link ClickServlet#CLICK_FORWARD} request attribute.
- *
- * @return true if the request has been forwarded
- */
- public boolean isForward() {
- return (request.getAttribute(ClickServlet.CLICK_FORWARD) != null);
- }
-
- /**
- * Return true if the HTTP request method is "POST".
- *
- * @return true if the HTTP request method is "POST"
- */
- public boolean isPost() {
- return isPost;
- }
-
- /**
- * Return true if the HTTP request method is "GET".
- *
- * @return true if the HTTP request method is "GET"
- */
- public boolean isGet() {
- return !isPost && getRequest().getMethod().equalsIgnoreCase("GET");
- }
-
- /**
- * Return true is this is an Ajax request, false otherwise.
- *
- * An Ajax request is identified by the presence of the request header
- * or request parameter: "X-Requested-With".
- * "X-Requested-With" is the de-facto standard identifier used by
- * Ajax libraries.
- *
- * Note: incoming requests that contains a request parameter
- * "X-Requested-With" will result in this method returning true, even
- * though the request itself was not initiated through a XmlHttpRequest
- * object. This allows one to programmatically enable Ajax requests. A common
- * use case for this feature is when uploading files through an IFrame element.
- * By specifying "X-Requested-With" as a request parameter the IFrame
- * request will be handled like a normal Ajax request.
- *
- * @return true if this is an Ajax request, false otherwise
- */
- public boolean isAjaxRequest() {
- return ClickUtils.isAjaxRequest(getRequest());
- }
-
- /**
- * Return true if the request contains the named attribute.
- *
- * @param name the name of the request attribute
- * @return true if the request contains the named attribute
- */
- public boolean hasRequestAttribute(String name) {
- return (getRequestAttribute(name) != null);
- }
-
- /**
- * Return the named request attribute, or null if not defined.
- *
- * @param name the name of the request attribute
- * @return the named request attribute, or null if not defined
- */
- public Object getRequestAttribute(String name) {
- return request.getAttribute(name);
- }
-
- /**
- * This method will set the named object in the HTTP request.
- *
- * @param name the storage name for the object in the request
- * @param value the object to store in the request
- */
- public void setRequestAttribute(String name, Object value) {
- request.setAttribute(name, value);
- }
-
- /**
- * Return true if the request contains the named parameter.
- *
- * @param name the name of the request parameter
- * @return true if the request contains the named parameter
- */
- public boolean hasRequestParameter(String name) {
- if (name == null) {
- throw new IllegalArgumentException("hasRequestParameter was called"
- + " with null name argument. This is often caused when a"
- + " Control binds to a request parameter, but its name was not"
- + " set.");
- }
- return (getRequestParameter(name) != null);
- }
-
- /**
- * Return the named request parameter. This method supports
- * "multipart/form-data" POST requests and should be used in
- * preference to the HttpServletRequest method
- * getParameter().
- *
- * @see org.apache.click.control.Form#onProcess()
- * @see #isMultipartRequest()
- * @see #getFileItemMap()
- *
- * @param name the name of the request parameter
- * @return the value of the request parameter.
- */
- public String getRequestParameter(String name) {
- if (name == null) {
- throw new IllegalArgumentException("getRequestParameter was called"
- + " with null name argument. This is often caused when a"
- + " Control binds to a request parameter, but its name was not"
- + " set.");
- }
- return request.getParameter(name);
- }
-
- /**
- * Returns an array of String objects containing all of the values the given
- * request parameter has, or null if the parameter does not exist.
- *
- * @param name a String containing the name of the parameter whose
- * value is requested
- * @return an array of String objects containing the parameter's values
- */
- public String[] getRequestParameterValues(String name) {
- if (name == null) {
- throw new IllegalArgumentException("getRequestParameter was called"
- + " with null name argument. This is often caused when a"
- + " Control binds to a request parameter, but its name was not"
- + " set.");
- }
- return request.getParameterValues(name);
- }
-
- /**
- * Return the named session attribute, or null if not defined.
- *
- * If the session is not defined this method will return null, and a
- * session will not be created.
- *
- * This method supports {@link FlashAttribute} which when accessed are then
- * removed from the session.
- *
- * @param name the name of the session attribute
- * @return the named session attribute, or null if not defined
- */
- public Object getSessionAttribute(String name) {
- if (hasSession()) {
- Object object = getSession().getAttribute(name);
-
- if (object instanceof FlashAttribute) {
- FlashAttribute flashObject = (FlashAttribute) object;
- object = flashObject.getValue();
- removeSessionAttribute(name);
- }
-
- return object;
- } else {
- return null;
- }
- }
-
- /**
- * This method will set the named object in the HttpSession.
- *
- * This method will create a session if one does not already exist.
- *
- * @param name the storage name for the object in the session
- * @param value the object to store in the session
- */
- public void setSessionAttribute(String name, Object value) {
- getSession().setAttribute(name, value);
- }
-
- /**
- * Remove the named attribute from the session. If the session does not
- * exist or the name is null, this method does nothing.
- *
- * @param name of the attribute to remove from the session
- */
- public void removeSessionAttribute(String name) {
- if (hasSession() && name != null) {
- getSession().removeAttribute(name);
- }
- }
-
- /**
- * Return true if there is a session and it contains the named attribute.
- *
- * @param name the name of the attribute
- * @return true if the session contains the named attribute
- */
- public boolean hasSessionAttribute(String name) {
- return (getSessionAttribute(name) != null);
- }
-
- /**
- * Return true if a HttpSession exists, or false otherwise.
- *
- * @return true if a HttpSession exists, or false otherwise
- */
- public boolean hasSession() {
- return (request.getSession(false) != null);
- }
-
- /**
- * This method will set the named object as a flash HttpSession object.
- *
- * The flash object will exist in the session until it is accessed once,
- * and then removed. Flash objects are typically used to display a message
- * once.
- *
- * @param name the storage name for the object in the session
- * @param value the object to store in the session
- */
- public void setFlashAttribute(String name, Object value) {
- getSession().setAttribute(name, new FlashAttribute(value));
- }
-
- /**
- * Return the cookie for the given name or null if not found.
- *
- * @param name the name of the cookie
- * @return the cookie for the given name or null if not found
- */
- public Cookie getCookie(String name) {
- return ClickUtils.getCookie(getRequest(), name);
- }
-
- /**
- * Return the cookie value for the given name or null if not found.
- *
- * @param name the name of the cookie
- * @return the cookie value for the given name or null if not found
- */
- public String getCookieValue(String name) {
- return ClickUtils.getCookieValue(getRequest(), name);
- }
-
- /**
- * Sets the given cookie value in the servlet response with the path "/".
- *
- * @see ClickUtils#setCookie(HttpServletRequest, HttpServletResponse, String, String, int, String)
- *
- * @param name the cookie name
- * @param value the cookie value
- * @param maxAge the maximum age of the cookie in seconds. A negative
- * value will expire the cookie at the end of the session, while 0 will delete
- * the cookie.
- * @return the Cookie object created and set in the response
- */
- public Cookie setCookie(String name, String value, int maxAge) {
- return ClickUtils.setCookie(getRequest(),
- getResponse(),
- name,
- value,
- maxAge,
- "/");
- }
-
- /**
- * Invalidate the specified cookie and delete it from the response object.
- * Deletes only cookies mapped against the root "/" path.
- *
- * @see ClickUtils#invalidateCookie(HttpServletRequest, HttpServletResponse, String)
- *
- * @param name the name of the cookie you want to delete.
- */
- public void invalidateCookie(String name) {
- ClickUtils.invalidateCookie(getRequest(), getResponse(), name);
- }
-
- /**
- * Return a new Page instance for the given path.
- *
- * This method can be used to create a target page for the
- * {@link org.apache.click.Page#setForward(org.apache.click.Page)}, for example:
- *
- *
- *
- * The given page path must start with a '/'.
- *
- * @param path the Page path as configured in the click.xml file
- * @return a new Page object
- * @throws IllegalArgumentException if the Page is not found
- */
- @SuppressWarnings("unchecked")
- public T createPage(String path) {
- if (path == null || path.length() == 0) {
- throw new IllegalArgumentException("page path cannot be null or empty");
- }
-
- if (path.charAt(0) != '/') {
- throw new IllegalArgumentException("page path must start with a '/'");
- }
- return (T) clickServlet.createPage(path, request);
- }
-
- /**
- * Return a new Page instance for the given class.
- *
- * This method can be used to create a target page for the
- * {@link org.apache.click.Page#setForward(org.apache.click.Page)}, for example:
- *
- *
- *
- * @param pageClass the Page class as configured in the click.xml file
- * @return a new Page object
- * @throws IllegalArgumentException if the Page is not found, or is not
- * configured with a unique path
- */
- public T createPage(Class pageClass) {
- return clickServlet.createPage(pageClass, request);
- }
-
- /**
- * Return the path for the given page Class.
- *
- * @param pageClass the class of the Page to lookup the path for
- * @return the path for the given page Class
- * @throws IllegalArgumentException if the Page Class is not configured
- * with a unique path
- */
- public String getPagePath(Class extends Page> pageClass) {
- return clickServlet.getConfigService().getPagePath(pageClass);
- }
-
- /**
- * Return the page Class for the given path.
- *
- * @param path the page path
- * @return the page class for the given path
- * @throws IllegalArgumentException if the Page Class for the path is not
- * found
- */
- public Class extends Page> getPageClass(String path) {
- return clickServlet.getConfigService().getPageClass(path);
- }
-
- /**
- * Return the Click application mode value:
- * ["production", "profile", "development", "debug", "trace"].
- *
- * @return the application mode value
- */
- public String getApplicationMode() {
- return clickServlet.getConfigService().getApplicationMode();
- }
-
- /**
- * Return the Click application charset or ISO-8859-1 if not is defined.
- *
- * The charset is defined in click.xml through the charset attribute
- * on the click-app element.
- *
- *
- *
- * @return the application charset or ISO-8859-1 if not defined
- */
- public String getCharset() {
- String charset = clickServlet.getConfigService().getCharset();
- if (charset == null) {
- charset = "ISO-8859-1";
- }
- return charset;
- }
-
- /**
- * Return a new messages map for the given baseClass (a page or control)
- * and the given global resource bundle name.
- *
- * @param baseClass the target class
- * @param globalResource the global resource bundle name
- * @return a new messages map with the messages for the target.
- */
- public Map createMessagesMap(Class> baseClass, String globalResource) {
- MessagesMapService messagesMapService =
- clickServlet.getConfigService().getMessagesMapService();
-
- return messagesMapService.createMessagesMap(baseClass, globalResource, getLocale());
- }
-
- /**
- * Returns a map of FileItem arrays keyed on request parameter
- * name for "multipart" POST requests (file uploads). Thus each map entry
- * will consist of one or more FileItem objects.
- *
- * @return map of FileItem arrays keyed on request parameter name
- * for "multipart" POST requests
- */
- public Map getFileItemMap() {
- return findClickRequestWrapper(request).getFileItemMap();
- }
-
- /**
- * Returns the value of a request parameter as a FileItem, for
- * "multipart" POST requests (file uploads), or null if the parameter
- * is not found.
- *
- * If there were multivalued parameters in the request (ie two or more
- * file upload fields with the same name), the first fileItem
- * in the array is returned.
- *
- * @param name the name of the parameter of the fileItem to retrieve
- *
- * @return the fileItem for the specified name
- */
- public FileItem getFileItem(String name) {
- Object value = findClickRequestWrapper(request).getFileItemMap().get(name);
-
- if (value != null) {
- if (value instanceof FileItem[]) {
- FileItem[] array = (FileItem[]) value;
- if (array.length >= 1) {
- return array[0];
- }
- } else if (value instanceof FileItem) {
- return (FileItem) value;
- }
- }
- return null;
- }
-
- /**
- * Return the users Locale.
- *
- * If the users Locale is stored in their session this will be returned.
- * Else if the click-app configuration defines a default Locale this
- * value will be returned, otherwise the request's Locale will be returned.
- *
- * To override the default request Locale set the users Locale using the
- * {@link #setLocale(Locale)} method.
- *
- * Pages and Controls obtain the users Locale using this method.
- *
- * @return the users Locale in the session, or if null the request Locale
- */
- public Locale getLocale() {
- Locale locale = (Locale) getSessionAttribute(LOCALE);
-
- if (locale == null) {
-
- if (clickServlet.getConfigService().getLocale() != null) {
- locale = clickServlet.getConfigService().getLocale();
-
- } else {
- locale = getRequest().getLocale();
- }
- }
-
- return locale;
- }
-
- /**
- * This method stores the given Locale in the users session. If the given
- * Locale is null, the "locale" attribute will be removed from the session.
- *
- * The Locale object is stored in the session using the {@link #LOCALE}
- * key.
- *
- * @param locale the Locale to store in the users session using the key
- * "locale"
- */
- public void setLocale(Locale locale) {
- if (locale == null && hasSession()) {
- getSession().removeAttribute(LOCALE);
- } else {
- setSessionAttribute(LOCALE, locale);
- }
- }
-
- /**
- * Return true if the request is a multi-part content type POST request.
- *
- * @return true if the request is a multi-part content type POST request
- */
- public boolean isMultipartRequest() {
- return ClickUtils.isMultipartRequest(request);
- }
-
- /**
- * Return a rendered Velocity template and model for the given
- * class and model data.
- *
- * This method will merge the class .htm Velocity template and
- * model data using the applications Velocity Engine.
- *
- * An example of the class template resolution is provided below:
- *
- * // Full class name
- * com.mycorp.control.CustomTextField
- *
- * // Template path name
- * /com/mycorp/control/CustomTextField.htm
- *
- * Example method usage:
- *
- * public String toString() {
- * Map model = getModel();
- * return getContext().renderTemplate(getClass(), model);
- * }
- *
- * @param templateClass the class to resolve the template for
- * @param model the model data to merge with the template
- * @return rendered Velocity template merged with the model data
- * @throws RuntimeException if an error occurs
- */
- @SuppressWarnings("unchecked")
- public String renderTemplate(Class templateClass, Map model) {
-
- if (templateClass == null) {
- String msg = "Null templateClass parameter";
- throw new IllegalArgumentException(msg);
- }
-
- String templatePath = templateClass.getName();
- templatePath = '/' + templatePath.replace('.', '/') + ".htm";
-
- return renderTemplate(templatePath, model);
- }
-
- /**
- * Return a rendered Velocity template and model data.
- *
- * Example method usage:
- *
- * public String toString() {
- * Map model = getModel();
- * return getContext().renderTemplate("/custom-table.htm", model);
- * }
- *
- * @param templatePath the path of the Velocity template to render
- * @param model the model data to merge with the template
- * @return rendered Velocity template merged with the model data
- * @throws RuntimeException if an error occurs
- */
- public String renderTemplate(String templatePath, Map model) {
-
- if (templatePath == null) {
- String msg = "Null templatePath parameter";
- throw new IllegalArgumentException(msg);
- }
-
- if (model == null) {
- String msg = "Null model parameter";
- throw new IllegalArgumentException(msg);
- }
-
- StringWriter stringWriter = new StringWriter(1024);
-
- TemplateService templateService =
- clickServlet.getConfigService().getTemplateService();
-
- try {
- templateService.renderTemplate(templatePath, model, stringWriter);
-
- } catch (Exception e) {
- String msg = "Error occurred rendering template: "
- + templatePath + "\n";
- clickServlet.getConfigService().getLogService().error(msg, e);
-
- throw new RuntimeException(e);
- }
-
- return stringWriter.toString();
- }
-
- // ------------------------------------------------ Package Private Methods
-
- /**
- * Return the stack data structure where Context's are stored.
- *
- * @return stack data structure where Context's are stored
- */
- static Context.ContextStack getContextStack() {
- Context.ContextStack contextStack = THREAD_LOCAL_CONTEXT_STACK.get();
-
- if (contextStack == null) {
- contextStack = new Context.ContextStack(2);
- THREAD_LOCAL_CONTEXT_STACK.set(contextStack);
- }
-
- return contextStack;
- }
-
- /**
- * Adds the specified Context on top of the Context stack.
- *
- * @param context a context instance
- */
- static void pushThreadLocalContext(Context context) {
- getContextStack().push(context);
- }
-
- /**
- * Remove and return the context instance on top of the context stack.
- *
- * @return the context instance on top of the context stack
- */
- static Context popThreadLocalContext() {
- Context.ContextStack contextStack = getContextStack();
- Context context = contextStack.pop();
-
- if (contextStack.isEmpty()) {
- THREAD_LOCAL_CONTEXT_STACK.remove();
- }
-
- return context;
- }
-
- /**
- * Find and return the ClickRequestWrapper that is wrapped by the specified
- * request.
- *
- * @param request the servlet request that wraps the ClickRequestWrapper
- * @return the wrapped servlet request
- */
- static ClickRequestWrapper findClickRequestWrapper(ServletRequest request) {
-
- while (!(request instanceof ClickRequestWrapper)
- && request instanceof HttpServletRequestWrapper
- && request != null) {
- request = ((HttpServletRequestWrapper) request).getRequest();
- }
-
- if (request instanceof ClickRequestWrapper) {
- return (ClickRequestWrapper) request;
- } else {
- throw new IllegalStateException("Click request is not present");
- }
- }
-
- // -------------------------------------------------- Inner Classes
-
- /**
- * Provides an unsynchronized Context Stack.
- */
- static final class ContextStack extends ArrayList {
-
- /** Serialization version indicator. */
- private static final long serialVersionUID = 1L;
-
- /**
- * Create a new ContextStack with the given initial capacity.
- *
- * @param initialCapacity specify initial capacity of this stack
- */
- private ContextStack(int initialCapacity) {
- super(initialCapacity);
- }
-
- /**
- * Pushes the Context onto the top of this stack.
- *
- * @param context the Context to push onto this stack
- * @return the Context pushed on this stack
- */
- private Context push(Context context) {
- add(context);
-
- return context;
- }
-
- /**
- * Removes and return the Context at the top of this stack.
- *
- * @return the Context at the top of this stack
- */
- private Context pop() {
- Context context = peek();
-
- remove(size() - 1);
-
- return context;
- }
-
- /**
- * Looks at the Context at the top of this stack without removing it.
- *
- * @return the Context at the top of this stack
- */
- private Context peek() {
- int length = size();
-
- if (length == 0) {
- String msg = "No Context available on ThreadLocal Context Stack";
- throw new RuntimeException(msg);
- }
-
- return get(length - 1);
- }
- }
-
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/Control.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/Control.java
deleted file mode 100644
index 96d0c9d548..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/Control.java
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.openidentityplatform.openam.click;
-
-import java.io.Serializable;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import jakarta.servlet.ServletContext;
-
-import org.openidentityplatform.openam.click.element.Element;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-
-/**
- * Provides the interface for Page controls. Controls are also referred to
- * as components or widgets.
- *
- * When a Page request event is processed Controls may perform server side event
- * processing through their {@link #onProcess()} method. Controls are generally
- * rendered in a Page by calling their toString() method.
- *
- * The Control execution sequence is illustrated below:
- *
- *
- *
- *
HTML HEAD Elements
- *
- * Control HTML HEAD elements can be included in the Page by overriding the
- * {@link #getHeadElements()} method.
- *
- * Below is an example of a custom TextField control specifying that the
- * custom.js file should be included in the HTML HEADer:
- *
- *
- * public class CustomField extends TextField {
- *
- * public List getHeadElements() {
- * if(headElements == null) {
- * // If headElements is null, create default headElements
- * headElements = super.getHeadElements();
- *
- * // Add a new JavaScript Import Element for the "/custom.js" script
- * headElements.add(new JsImport("/click/custom.js"));
- * }
- * return headElements;
- * }
- *
- * ..
- * }
- *
- *
- *
Deploying Resources
- *
- * The Click framework uses the Velocity Tools WebappResourceLoader for loading templates.
- * This avoids issues associate with using the Velocity ClasspathResourceLoader and
- * FileResourceLoader on J2EE application servers.
- * To make preconfigured resources (templates, JavaScript, stylesheets, etc.)
- * available to web applications Click automatically deploys configured classpath
- * resources to the /click directory at startup
- * (existing files will not be overwritten).
- *
- * Click supports two ways of deploying pre-configured resources. The recommended
- * deployment strategy (which also the simplest) relies on packaging resources
- * into a special folder of the JAR, called 'META-INF/resources'. At
- * startup time Click will scan this folder for resources and deploy them to the
- * web application. This deployment strategy is the same approach taken by the
- * Servlet 3.0 specification. Please see the section
- * Deploying Custom Resources
- * for more details.
- *
- * An alternative approach to deploying static resources on startup is provided
- * by the Control interface through the {@link #onDeploy(ServletContext)} method.
- *
- * Continuing our example, the CustomField control deploys its
- * custom.js file to the /click directory:
- *
- *
- *
- * Controls using the onDeploy() method must be registered in the
- * application WEB-INF/click.xml for them to be invoked.
- * For example:
- *
- *
- *
- * When the Click application starts up it will deploy any control elements
- * defined in the following files in sequential order:
- *
- *
/click-controls.xml
- *
/extras-controls.xml
- *
WEB-INF/click.xml
- *
- *
- * Please note {@link org.apache.click.control.AbstractControl} provides
- * a default implementation of the Control interface to make it easier for
- * developers to create their own controls.
- *
- * @see org.apache.click.util.PageImports
- */
-public interface Control extends Serializable {
-
- /**
- * The global control messages bundle name: click-control.
- */
- public static final String CONTROL_MESSAGES = "click-control";
-
- /**
- * Return the Page request Context of the Control.
- *
- * @deprecated getContext() is now obsolete on the Control interface,
- * but will still be available on AbstractControl:
- * {@link org.apache.click.control.AbstractControl#getContext()}
- *
- * @return the Page request Context
- */
- public Context getContext();
-
- /**
- * Return the list of HEAD {@link org.apache.click.element.Element elements}
- * to be included in the page. Example HEAD elements include
- * {@link org.apache.click.element.JsImport JsImport},
- * {@link org.apache.click.element.JsScript JsScript},
- * {@link org.apache.click.element.CssImport CssImport} and
- * {@link org.apache.click.element.CssStyle CssStyle}.
- *
- * Controls can contribute their own list of HEAD elements by implementing
- * this method.
- *
- * The recommended approach when implementing this method is to use
- * lazy loading to ensure the HEAD elements are only added
- * once and when needed. For example:
- *
- *
- * public MyControl extends AbstractControl {
- *
- * public List getHeadElements() {
- * // Use lazy loading to ensure the JS is only added the
- * // first time this method is called.
- * if (headElements == null) {
- * // Get the head elements from the super implementation
- * headElements = super.getHeadElements();
- *
- * // Include the control's external JavaScript resource
- * JsImport jsImport = new JsImport("/mycorp/mycontrol/mycontrol.js");
- * headElements.add(jsImport);
- *
- * // Include the control's external Css resource
- * CssImport cssImport = new CssImport("/mycorp/mycontrol/mycontrol.css");
- * headElements.add(cssImport);
- * }
- * return headElements;
- * }
- * }
- *
- * Alternatively one can add the HEAD elements in the Control's constructor:
- *
- *
- *
- * One can also add HEAD elements from event handler methods such as
- * {@link #onInit()}, {@link #onProcess()}, {@link #onRender()}
- * etc.
- *
- * The order in which JS and CSS files are included will be preserved in the
- * page.
- *
- * Note: this method must never return null. If no HEAD elements
- * are available this method must return an empty {@link java.util.List}.
- *
- * Also note: a common problem when overriding getHeadElements in
- * subclasses is forgetting to call super.getHeadElements. Consider
- * carefully whether you should call super.getHeadElements or not.
- *
- * @return the list of HEAD elements to be included in the page
- */
- public List getHeadElements();
-
- /**
- * Return HTML element identifier attribute "id" value.
- *
- * {@link org.apache.click.control.AbstractControl#getId()}
- *
- * @return HTML element identifier attribute "id" value
- */
- public String getId();
-
- /**
- * Set the controls event listener.
- *
- * The method signature of the listener is:
- *
must have a valid Java method name
- *
takes no arguments
- *
returns a boolean value
- *
- *
- * An example event listener method would be:
- *
- *
- *
- * @param listener the listener object with the named method to invoke
- * @param method the name of the method to invoke
- *
- * @deprecated this method is now obsolete on the Control interface, but
- * will still be available on AbstractControl:
- * {@link org.apache.click.control.AbstractControl#setListener(java.lang.Object, java.lang.String)}
- */
- public void setListener(Object listener, String method);
-
- /**
- * Return the localized messages Map of the Control.
- *
- * @return the localized messages Map of the Control
- */
- public Map getMessages();
-
- /**
- * Return the name of the Control. Each control name must be unique in the
- * containing Page model or the containing Form.
- *
- * @return the name of the control
- */
- public String getName();
-
- /**
- * Set the name of the Control. Each control name must be unique in the
- * containing Page model or the parent container.
- *
- * Please note: changing the name of a Control after it has been
- * added to its parent container is undefined. Thus it is best not
- * to change the name of a Control once its been set.
- *
- * @param name of the control
- * @throws IllegalArgumentException if the name is null
- */
- public void setName(String name);
-
- /**
- * Return the parent of the Control.
- *
- * @return the parent of the Control
- */
- public Object getParent();
-
- /**
- * Set the parent of the Control.
- *
- * @param parent the parent of the Control
- */
- public void setParent(Object parent);
-
- /**
- * The on deploy event handler, which provides classes the
- * opportunity to deploy static resources when the Click application is
- * initialized.
- *
- * For example:
- *
- * Please note: a common problem when overriding onDeploy in
- * subclasses is forgetting to call super.onDeploy. Consider
- * carefully whether you should call super.onDeploy or not.
- *
- * Click also supports an alternative deployment strategy which relies on
- * packaging resource (stylesheets, JavaScript, images etc.) following a
- * specific convention. See the section
- * Deploying Custom Resources
- * for further details.
- *
- * @param servletContext the servlet context
- */
- public void onDeploy(ServletContext servletContext);
-
- /**
- * The on initialize event handler. Each control will be initialized
- * before its {@link #onProcess()} method is called.
- *
- * {@link org.apache.click.control.Container} implementations should recursively
- * invoke the onInit method on each of their child controls ensuring that
- * all controls receive this event.
- *
- * Please note: a common problem when overriding onInit in
- * subclasses is forgetting to call super.onInit(). Consider
- * carefully whether you should call super.onInit() or not,
- * especially for {@link org.apache.click.control.Container}s which by default
- * call onInit on all their child controls as well.
- */
- public void onInit();
-
- /**
- * The on process event handler. Each control will be processed when the
- * Page is requested.
- *
- * ClickServlet will process all Page controls in the order they were added
- * to the Page.
- *
- * {@link org.apache.click.control.Container} implementations should recursively
- * invoke the onProcess method on each of their child controls ensuring that
- * all controls receive this event. However when a control onProcess method
- * return false, no other controls onProcess method should be invoked.
- *
- * When a control is processed it should return true if the Page should
- * continue event processing, or false if no other controls should be
- * processed and the {@link org.apache.click.Page#onGet()} or {@link Page#onPost()} methods
- * should not be invoked.
- *
- * Please note: a common problem when overriding onProcess in
- * subclasses is forgetting to call super.onProcess(). Consider
- * carefully whether you should call super.onProcess() or not,
- * especially for {@link org.apache.click.control.Container}s which by default
- * call onProcess on all their child controls as well.
- *
- * @return true to continue Page event processing or false otherwise
- */
- public boolean onProcess();
-
- /**
- * The on render event handler. This event handler is invoked prior to the
- * control being rendered, and is useful for providing pre rendering logic.
- *
- * The on render method is typically used to populate tables performing some
- * database intensive operation. By putting the intensive operations in the
- * on render method they will not be performed if the user navigates away
- * to a different page.
- *
- * {@link org.apache.click.control.Container} implementations should recursively
- * invoke the onRender method on each of their child controls ensuring that
- * all controls receive this event.
- *
- * Please note: a common problem when overriding onRender in
- * subclasses is forgetting to call super.onRender(). Consider
- * carefully whether you should call super.onRender() or not,
- * especially for {@link org.apache.click.control.Container}s which by default
- * call onRender on all their child controls as well.
- */
- public void onRender();
-
- /**
- * The on destroy request event handler. Control classes should use this
- * method to add any resource clean up code.
- *
- * This method is guaranteed to be called before the Page object reference
- * goes out of scope and is available for garbage collection.
- *
- * {@link org.apache.click.control.Container} implementations should recursively
- * invoke the onDestroy method on each of their child controls ensuring that
- * all controls receive this event.
- *
- * Please note: a common problem when overriding onDestroy in
- * subclasses is forgetting to call super.onDestroy(). Consider
- * carefully whether you should call super.onDestroy() or not,
- * especially for {@link org.apache.click.control.Container}s which by default
- * call onDestroy on all their child controls as well.
- */
- public void onDestroy();
-
- /**
- * Render the control's HTML representation to the specified buffer. The
- * control's {@link java.lang.Object#toString()} method should delegate the
- * rendering to the render method for improved performance.
- *
- * An example implementation:
- *
- *
- * @param buffer the specified buffer to render the control's output to
- */
- public void render(HtmlStringBuffer buffer);
-
- /**
- * Returns true if this control has any
- * Behaviors registered, false otherwise.
- *
- * @return true if this control has any
- * Behaviors registered, false otherwise
- */
- public boolean hasBehaviors();
-
- /**
- * Returns the list of behaviors for this control.
- *
- * @return the list with this control behaviors.
- */
- public Set getBehaviors();
-
- /**
- * Returns true if this control is an Ajax target, false
- * otherwise.
- *
- * In order for a Control to be considered as an Ajax target it must be
- * registered through {@link org.apache.click.ControlRegistry#registerAjaxTarget(org.apache.click.Control) ControlRegistry.registerAjaxTarget}.
- *
- * When the Click handles an Ajax request it iterates the Controls
- * registered with the {@link org.apache.click.ControlRegistry ControlRegistry}
- * and checks if one of them is the Ajax target by calling
- * {@link #isAjaxTarget(org.openidentityplatform.openam.click.Context) isAjaxTarget}. If isAjaxTarget
- * returns true, Click will process that Control's {@link #getBehaviors() behaviors}.
- *
- * Please note: there can only be one target control, so the first
- * Control that is identified as the Ajax target will be processed, the other
- * controls will be skipped.
- *
- * The most common way to check whether a Control is the Ajax target is to
- * check if its {@link #getId ID} is available as a request parameter:
- *
- *
- *
- * Not every scenario can be covered through an ID attribute though. For example
- * if an ActionLink is rendered multiple times on the same page, it cannot have an
- * ID attribute, as that would lead to duplicate IDs, which isn't allowed by
- * the HTML specification. Control implementations has to cater for how the
- * control will be targeted. In the case of ActionLink it might check against
- * its id, and if that isn't available check against its name.
- *
- * @param context the request context
- * @return true if this control is an Ajax target, false
- * otherwise
- */
- public boolean isAjaxTarget(Context context);
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/ControlRegistry.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/ControlRegistry.java
deleted file mode 100644
index ecc271bb5e..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/ControlRegistry.java
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.openidentityplatform.openam.click;
-
-
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.openidentityplatform.openam.click.service.ConfigService;
-import org.openidentityplatform.openam.click.service.LogService;
-import org.apache.commons.lang.Validate;
-
-/**
- * Provides a centralized registry where Controls can be registered and interact
- * with the Click runtime.
- *
- * The primary use of the ControlRegistry is for Controls to register themselves
- * as potential targets of Ajax requests
- * (If a control is an Ajax request target, it's onProcess()
- * method is invoked while other controls are not processed).
- *
- * Registering controls as Ajax targets serves a dual purpose. In addition to
- * being potential Ajax targets, these controls will have all their Behaviors
- * processed by the Click runtime.
- *
- * Thus the ControlRegistry provides the Click runtime with easy access to Controls
- * that want to be processed for Ajax requests. It also provides quick access
- * to Controls that have Behaviors, and particularly AjaxBehaviors that want to
- * handle and respond to Ajax requests.
- *
- *
Register Control as an Ajax Target
- * Below is an example of a Control registering itself as an Ajax target:
- *
- *
- * public class AbstractControl implements Control {
- *
- * public void addBehavior(Behavior behavior) {
- * getBehaviors().add(behavior);
- * // Adding a behavior also registers the Control as an Ajax target
- * ControlRegistry.registerAjaxTarget(this);
- * }
- * }
- *
- *
Register Interceptor
- * Below is an example of a Container registering a Behavior in order to intercept
- * and decorate its child controls:
- *
- *
- * public class MyContainer extends AbstractContainer {
- *
- * public void onInit() {
- * Behavior controlInterceptor = getInterceptor();
- * ControlRegistry.registerInterceptor(this, controlInterceptor);
- * }
- *
- * private Behavior getInterceptor() {
- * Behavior controlInterceptor = new Behavior() {
- *
- * // This method is invoked before the controls are rendered to the client
- * public void preResponse(Control source) {
- * // Here we can add a CSS class attribute to each child control
- * addCssClassToChildControls();
- * }
- *
- * // This method is invoked before the HEAD elements are retrieved for each Control
- * public void preRenderHeadElements(Control source) {
- * }
- *
- * // This method is invoked before the Control onDestroy event
- * public void preDestroy(Control source) {
- * }
- * };
- * return controlInterceptor;
- * }
- * }
- */
-public class ControlRegistry {
-
- // Constants --------------------------------------------------------------
-
- /** The thread local registry holder. */
- private static final ThreadLocal THREAD_LOCAL_REGISTRY_STACK =
- new ThreadLocal<>();
-
- // Variables --------------------------------------------------------------
-
- /** The set of Ajax target controls. */
- Set ajaxTargetControls;
-
- /** The list of registered interceptors. */
- List interceptors;
-
- /** The application log service. */
- LogService logger;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * Construct the ControlRegistry with the given ConfigService.
- *
- * @param configService the click application configuration service
- */
- public ControlRegistry(ConfigService configService) {
- this.logger = configService.getLogService();
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * Return the thread local ControlRegistry instance.
- *
- * @return the thread local ControlRegistry instance.
- * @throws RuntimeException if a ControlRegistry is not available on the
- * thread
- */
- public static ControlRegistry getThreadLocalRegistry() {
- return getRegistryStack().peek();
- }
-
- /**
- * Returns true if a ControlRegistry instance is available on the current
- * thread, false otherwise.
- *
- * Unlike {@link #getThreadLocalRegistry()} this method can safely be used
- * and will not throw an exception if a ControlRegistry is not available on
- * the current thread.
- *
- * @return true if an ControlRegistry instance is available on the
- * current thread, false otherwise
- */
- public static boolean hasThreadLocalRegistry() {
- ControlRegistry.RegistryStack registryStack = THREAD_LOCAL_REGISTRY_STACK.get();
- if (registryStack == null) {
- return false;
- }
- return !registryStack.isEmpty();
- }
-
- /**
- * Register the control to be processed by the Click runtime if the control
- * is the Ajax target. A control is an Ajax target if the
- * {@link Control#isAjaxTarget(Context)} method returns true.
- * Once a target control is identified, Click invokes its
- * {@link Control#onProcess()} method.
- *
- * This method serves a dual purpose as all controls registered here
- * will also have their Behaviors (if any) processed. Processing
- * {@link Behavior Behaviors}
- * means their interceptor methods will be invoked during the request
- * life cycle, passing the control as the argument.
- *
- * @param control the control to register as an Ajax target
- */
- public static void registerAjaxTarget(Control control) {
- if (control == null) {
- throw new IllegalArgumentException("control cannot be null");
- }
-
- ControlRegistry instance = getThreadLocalRegistry();
- instance.internalRegisterAjaxTarget(control);
- }
-
- /**
- * Register a control event interceptor for the given Control and Behavior.
- * The control will be passed as the source control to the Behavior
- * interceptor methods:
- * {@link org.apache.click.Behavior#preRenderHeadElements(org.apache.click.Control) preRenderHeadElements(Control)},
- * {@link org.apache.click.Behavior#preResponse(org.apache.click.Control) preResponse(Control)} and
- * {@link org.apache.click.Behavior#preDestroy(org.apache.click.Control) preDestroy(Control)}.
- *
- * @param control the interceptor source control
- * @param controlInterceptor the control interceptor to register
- */
- public static void registerInterceptor(Control control, Behavior controlInterceptor) {
- if (control == null) {
- throw new IllegalArgumentException("control cannot be null");
- }
- if (controlInterceptor == null) {
- throw new IllegalArgumentException("control interceptor cannot be null");
- }
-
- ControlRegistry instance = getThreadLocalRegistry();
- instance.internalRegisterInterceptor(control, controlInterceptor);
- }
-
- // Protected Methods ------------------------------------------------------
-
- /**
- * Allow the registry to handle the error that occurred.
- *
- * @param throwable the error which occurred during processing
- */
- protected void errorOccurred(Throwable throwable) {
- clear();
- }
-
- // Package Private Methods ------------------------------------------------
-
- /**
- * Remove all interceptors and ajax target controls from this registry.
- */
- void clear() {
- if (hasInterceptors()) {
- getInterceptors().clear();
- }
-
- if (hasAjaxTargetControls()) {
- getAjaxTargetControls().clear();
- }
- }
-
- /**
- * Register the AJAX target control.
- *
- * @param control the AJAX target control
- */
- void internalRegisterAjaxTarget(Control control) {
- Validate.notNull(control, "Null control parameter");
- getAjaxTargetControls().add(control);
- }
-
- /**
- * Register the source control and associated interceptor.
- *
- * @param source the interceptor source control
- * @param controlInterceptor the control interceptor to register
- */
- void internalRegisterInterceptor(Control source, Behavior controlInterceptor) {
- Validate.notNull(source, "Null source parameter");
- Validate.notNull(controlInterceptor, "Null interceptor parameter");
-
- ControlRegistry.InterceptorHolder interceptorHolder = new ControlRegistry.InterceptorHolder(source, controlInterceptor);
-
- // Guard against adding duplicate interceptors
- List localInterceptors = getInterceptors();
- if (!localInterceptors.contains(interceptorHolder)) {
- localInterceptors.add(interceptorHolder);
- }
- }
-
- void processPreResponse(Context context) {
- if (hasAjaxTargetControls()) {
- for (Control control : getAjaxTargetControls()) {
- for (Behavior behavior : control.getBehaviors()) {
- behavior.preResponse(control);
- }
- }
- }
-
- if (hasInterceptors()) {
- for (ControlRegistry.InterceptorHolder interceptorHolder : getInterceptors()) {
- Behavior interceptor = interceptorHolder.getInterceptor();
- Control control = interceptorHolder.getControl();
- interceptor.preResponse(control);
- }
- }
- }
-
- void processPreRenderHeadElements(Context context) {
- if (hasAjaxTargetControls()) {
- for (Control control : getAjaxTargetControls()) {
- for (Behavior behavior : control.getBehaviors()) {
- behavior.preRenderHeadElements(control);
- }
- }
- }
-
- if (hasInterceptors()) {
- for (ControlRegistry.InterceptorHolder interceptorHolder : getInterceptors()) {
- Behavior interceptor = interceptorHolder.getInterceptor();
- Control control = interceptorHolder.getControl();
- interceptor.preRenderHeadElements(control);
- }
- }
- }
-
- void processPreDestroy(Context context) {
- if (hasAjaxTargetControls()) {
- for (Control control : getAjaxTargetControls()) {
- for (Behavior behavior : control.getBehaviors()) {
- behavior.preDestroy(control);
- }
- }
- }
-
- if (hasInterceptors()) {
- for (ControlRegistry.InterceptorHolder interceptorHolder : getInterceptors()) {
- Behavior interceptor = interceptorHolder.getInterceptor();
- Control control = interceptorHolder.getControl();
- interceptor.preDestroy(control);
- }
- }
- }
-
- /**
- * Checks if any AJAX target control have been registered.
- */
- boolean hasAjaxTargetControls() {
- if (ajaxTargetControls == null || ajaxTargetControls.isEmpty()) {
- return false;
- }
- return true;
- }
-
- /**
- * Return the set of potential Ajax target controls.
- *
- * @return the set of potential Ajax target controls
- */
- Set getAjaxTargetControls() {
- if (ajaxTargetControls == null) {
- ajaxTargetControls = new LinkedHashSet();
- }
- return ajaxTargetControls;
- }
-
- /**
- * Checks if any control interceptors have been registered.
- */
- boolean hasInterceptors() {
- if (interceptors == null || interceptors.isEmpty()) {
- return false;
- }
- return true;
- }
-
- /**
- * Return the set of registered control interceptors.
- *
- * @return set of registered interceptors
- */
- List getInterceptors() {
- if (interceptors == null) {
- interceptors = new ArrayList<>();
- }
- return interceptors;
- }
-
- /**
- * Adds the specified ControlRegistry on top of the registry stack.
- *
- * @param controlRegistry the ControlRegistry to add
- */
- static void pushThreadLocalRegistry(ControlRegistry controlRegistry) {
- getRegistryStack().push(controlRegistry);
- }
-
- /**
- * Remove and return the controlRegistry instance on top of the
- * registry stack.
- *
- * @return the controlRegistry instance on top of the registry stack
- */
- static ControlRegistry popThreadLocalRegistry() {
- ControlRegistry.RegistryStack registryStack = getRegistryStack();
- ControlRegistry controlRegistry = registryStack.pop();
-
- if (registryStack.isEmpty()) {
- THREAD_LOCAL_REGISTRY_STACK.set(null);
- }
-
- return controlRegistry;
- }
-
- static ControlRegistry.RegistryStack getRegistryStack() {
- ControlRegistry.RegistryStack registryStack = THREAD_LOCAL_REGISTRY_STACK.get();
-
- if (registryStack == null) {
- registryStack = new ControlRegistry.RegistryStack(2);
- THREAD_LOCAL_REGISTRY_STACK.set(registryStack);
- }
-
- return registryStack;
- }
-
- /**
- * Provides an unsynchronized Stack.
- */
- static class RegistryStack extends ArrayList {
-
- /** Serialization version indicator. */
- private static final long serialVersionUID = 1L;
-
- /**
- * Create a new RegistryStack with the given initial capacity.
- *
- * @param initialCapacity specify initial capacity of this stack
- */
- private RegistryStack(int initialCapacity) {
- super(initialCapacity);
- }
-
- /**
- * Pushes the ControlRegistry onto the top of this stack.
- *
- * @param controlRegistry the ControlRegistry to push onto this stack
- * @return the ControlRegistry pushed on this stack
- */
- private ControlRegistry push(ControlRegistry controlRegistry) {
- add(controlRegistry);
-
- return controlRegistry;
- }
-
- /**
- * Removes and return the ControlRegistry at the top of this stack.
- *
- * @return the ControlRegistry at the top of this stack
- */
- private ControlRegistry pop() {
- ControlRegistry controlRegistry = peek();
-
- remove(size() - 1);
-
- return controlRegistry;
- }
-
- /**
- * Looks at the ControlRegistry at the top of this stack without
- * removing it.
- *
- * @return the ControlRegistry at the top of this stack
- */
- private ControlRegistry peek() {
- int length = size();
-
- if (length == 0) {
- String msg = "No ControlRegistry available on ThreadLocal Registry Stack";
- throw new RuntimeException(msg);
- }
-
- return get(length - 1);
- }
- }
-
- static class InterceptorHolder {
-
- private Behavior interceptor;
-
- private Control control;
-
- public InterceptorHolder(Control control, Behavior interceptor) {
- this.control = control;
- this.interceptor = interceptor;
- }
-
- public Behavior getInterceptor() {
- return interceptor;
- }
-
- public void setInterceptor(Behavior interceptor) {
- this.interceptor = interceptor;
- }
-
- public Control getControl() {
- return control;
- }
-
- public void setControl(Control control) {
- this.control = control;
- }
-
- /**
- * @see Object#equals(java.lang.Object)
- *
- * @param o the reference object with which to compare
- * @return true if this object equals the given object
- */
- @Override
- public boolean equals(Object o) {
-
- //1. Use the == operator to check if the argument is a reference to this object.
- if (o == this) {
- return true;
- }
-
- //2. Use the instanceof operator to check if the argument is of the correct type.
- if (!(o instanceof ControlRegistry.InterceptorHolder)) {
- return false;
- }
-
- //3. Cast the argument to the correct type.
- ControlRegistry.InterceptorHolder that = (ControlRegistry.InterceptorHolder) o;
-
- boolean equals = this.control == null ? that.control == null : this.control.equals(that.control);
- if (!equals) {
- return false;
- }
-
- return this.interceptor == null ? that.interceptor == null : this.interceptor.equals(that.interceptor);
- }
-
- /**
- * @see java.lang.Object#hashCode()
- *
- * @return the InterceptorHolder hashCode
- */
- @Override
- public int hashCode() {
- int result = 17;
- result = 37 * result + (control == null ? 0 : control.hashCode());
- result = 37 * result + (interceptor == null ? 0 : interceptor.hashCode());
- return result;
- }
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/Page.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/Page.java
deleted file mode 100644
index aa65de9e1d..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/Page.java
+++ /dev/null
@@ -1,1362 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click;
-
-import java.io.Serializable;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.openidentityplatform.openam.click.element.Element;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-import org.apache.click.util.Format;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-import org.openidentityplatform.openam.click.util.PageImports;
-import org.apache.commons.lang.StringUtils;
-
-/**
- * Provides the Page request event handler class.
- *
- * The Page class plays a central role in Click applications defining how the
- * application's pages are processed and rendered. All application pages
- * must extend the base Page class, and provide a no arguments constructor.
- *
- *
Page Execution Sequence
- *
- * The default Page execution path for a GET request is:
- *
- *
- * no-args constructor invoked to create a new Page instance.
- * At this point no dependencies have been injected into the Page, and any
- * request information is not available. You should put any "static"
- * page initialization code, which doesn't depend upon request information,
- * in the constructor. This will enable subclasses to have this code
- * automatically initialized when they are created.
- *
- *
- * {@link #format} property is set
- *
- *
- * {@link #headers} property is set
- *
- *
- * {@link #path} property is set
- *
- *
- * {@link #onSecurityCheck()} method called to check whether the page should
- * be processed. This method should return true if the Page should continue
- * to be processed, or false otherwise.
- *
- *
- * {@link #onInit()} method called to complete the initialization of the page
- * after all the dependencies have been set. This is where you should put
- * any "dynamic" page initialization code which depends upon the request or any
- * other dependencies.
- *
- * Form and field controls must be fully initialized by the time this method
- * has completed.
- *
- *
- * ClickServlet processes all the page {@link #controls}
- * calling their {@link org.apache.click.Control#onProcess()} method. If any of these
- * controls return false, continued control and page processing will be aborted.
- *
- *
- * {@link #onGet()} method called for any additional GET related processing.
- *
- * Form and field controls should NOT be created or initialized at this
- * point as the control processing stage has already been completed.
- *
- *
- * {@link #onRender()} method called for any pre-render processing. This
- * method is often use to perform database queries to load information for
- * rendering tables.
- *
- * Form and field controls should NOT be created or initialized at this
- * point as the control processing stage has already been completed.
- *
- *
- * ClickServlet renders the page merging the {@link #model} with the
- * Velocity template defined by the {@link #getTemplate()} property.
- *
- *
- * {@link #onDestroy()} method called to clean up any resources. This method
- * is guaranteed to be called, even if an exception occurs. You can use
- * this method to close resources like database connections or Hibernate
- * sessions.
- *
- *
- *
- * For POST requests the default execution path is identical, except the
- * {@link #onPost()} method is called instead of {@link #onGet()}. The POST
- * request page execution sequence is illustrated below:
- *
- *
- *
- *
- * A good way to see the page event execution order is to view the log when
- * the application mode is set to trace:
- *
- *
- *
- * When a Velocity template is rendered the ClickServlet uses Pages:
- *
- *
{@link #getTemplate()} to find the Velocity template.
- *
{@link #model} to populate the Velocity Context
- *
{@link #format} to add to the Velocity Context
- *
{@link #getContentType()} to set as the HttpServletResponse content type
- *
{@link #headers} to set as the HttpServletResponse headers
- *
- *
- * These Page properties are also used when rendering JSP pages.
- */
-public class Page implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * The global page messages bundle name: click-page.
- */
- public static final String PAGE_MESSAGES = "click-page";
-
- /**
- * The Page action request parameter: "pageAction".
- */
- public final static String PAGE_ACTION = "pageAction";
-
- // Instance Variables -----------------------------------------------------
-
- /** The list of page controls. */
- protected List controls;
-
- /**
- * The list of page HTML HEAD elements including: Javascript imports,
- * Css imports, inline Javascript and inline Css.
- */
- protected List headElements;
-
- /** The Velocity template formatter object. */
- protected Format format;
-
- /** The forward path. */
- protected String forward;
-
- /** The HTTP response headers. */
- protected Map headers;
-
- /** The map of localized page resource messages. **/
- protected transient Map messages;
-
- /**
- * The page model. For Velocity templates the model is used to populate the
- * Velocity context. For JSP pages the model values are set as named
- * request attributes.
- */
- protected Map model = new HashMap();
-
- /** The Page header imports. */
- protected transient PageImports pageImports;
-
- /** The path of the page template to render. */
- protected String path;
-
- /** The redirect path. */
- protected String redirect;
-
- /**
- * The page is stateful and should be saved to the users HttpSession
- * between requests, default value is false.
- *
- * @deprecated stateful pages are not supported anymore, use stateful
- * Controls instead
- */
- protected boolean stateful;
-
- /** The path of the page border template to render.*/
- protected String template;
-
- /**
- * Indicates whether Control head elements should be included in the
- * page template, default value is true.
- */
- protected boolean includeControlHeadElements = true;
-
- // Event Handlers ---------------------------------------------------------
-
- /**
- * The on Security Check event handler. This event handler is invoked after
- * the pages constructor has been called and all the page properties have
- * been set.
- *
- * Security check provides the Page an opportunity to check the users
- * security credentials before processing the Page.
- *
- * If security check returns true the Page is processed as
- * normal. If the method returns then no other event handlers are invoked
- * (except onDestroy() and no page controls are processed.
- *
- * If the method returns false, the forward or redirect property should be
- * set to send the request to another Page.
- *
- * By default this method returns true, subclass may override this method
- * to provide their security authorization/authentication mechanism.
- *
- * @return true by default, subclasses may override this method
- */
- public boolean onSecurityCheck() {
- return true;
- }
-
- /**
- * The on Initialization event handler. This event handler is invoked after
- * the {@link #onInit()} method has been called.
- *
- * Subclasses should place any initialization code which has dependencies
- * on the context or other properties in this method. Generally light
- * weight initialization code should be placed in the Pages constructor.
- *
- * Time consuming operations such as fetching the results of a database
- * query should not be placed in this method. These operations should be
- * performed in the {@link #onRender()}, {@link #onGet()} or
- * {@link #onPost()} methods so that other event handlers may take
- * alternative execution paths without performing these expensive operations.
- *
- * Please Note however the qualifier for the previous statement is
- * that all form and field controls must be fully initialized before they
- * are processed, which is after the onInit() method has
- * completed. After this point their onProcess() methods will be
- * invoked by the ClickServlet.
- *
- * Select controls in particular must have their option list values populated
- * before the form is processed otherwise field validations cannot be performed.
- *
- * For initializing page controls the best practice is to place all the
- * control creation code in the pages constructor, and only place any
- * initialization code in the onInit() method which has an external
- * dependency to the context or some other object. By following this practice
- * it is easy to see what code is "design time" initialization code and what
- * is "runtime initialization code".
- *
- * When subclassing pages which also use the onInit() method is
- * is critical you call the super.onInit() method first, for
- * example:
- *
- */
- public void onInit() {
- }
-
- /**
- * The on Get request event handler. This event handler is invoked if the
- * HTTP request method is "GET".
- *
- * The event handler is invoked after {@link #onSecurityCheck()} has been
- * called and all the Page {@link #controls} have been processed. If either
- * the security check or one of the controls cancels continued event
- * processing the onGet() method will not be invoked.
- *
- *
Important Note
- *
- * Form and field controls should NOT be created
- * or initialized at this point as the control processing stage has already
- * been completed. Select option list values should also be populated
- * before the control processing stage is performed so that they can
- * validate the submitted values.
- */
- public void onGet() {
- }
-
- /**
- * The on Post request event handler. This event handler is invoked if the
- * HTTP request method is "POST".
- *
- * The event handler is invoked after {@link #onSecurityCheck()} has been
- * called and all the Page {@link #controls} have been processed. If either
- * the security check or one of the controls cancels continued event
- * processing the onPost() method will not be invoked.
- *
- *
Important Note
- *
- * Form and field controls should NOT be created
- * or initialized at this point as the control processing stage has already
- * been completed. Select option list values should also be populated
- * before the control processing stage is performed so that they can
- * validate the submitted values.
- */
- public void onPost() {
- }
-
- /**
- * The on render event handler. This event handler is invoked prior to the
- * page being rendered.
- *
- * This method will not be invoked if either the security check or one of
- * the controls cancels continued event processing.
- *
- * The on render method is typically used to populate tables performing some
- * database intensive operation. By putting the intensive operations in the
- * on render method they will not be performed if the user navigates away
- * to a different page.
- *
- * If you have code which you are using in both the onGet() and
- * onPost() methods, use the onRender() method instead.
- *
- *
Important Note
- *
- * Form and field controls should NOT be created
- * or initialized at this point as the control processing stage has already
- * been completed. Select option list values should also be populated
- * before the control processing stage is performed so that they can
- * validate the submitted values.
- */
- public void onRender() {
- }
-
- /**
- * The on Destroy request event handler. Subclasses may override this method
- * to add any resource clean up code.
- *
- * This method is guaranteed to be called before the Page object reference
- * goes out of scope and is available for garbage collection.
- */
- public void onDestroy() {
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * Add the control to the page. The control will be added to the page model
- * using the control name as the key. The Controls parent property will
- * also be set to the page instance.
- *
- * Please note: if the page contains a control with the same name as
- * the given control, that control will be replaced by the given control.
- * If a control has no name defined it cannot be replaced.
- *
- * @param control the control to add to the page
- * @throws IllegalArgumentException if the control is null or if the name
- * of the control is not defined
- */
- public void addControl(Control control) {
- if (control == null) {
- throw new IllegalArgumentException("Null control parameter");
- }
- if (StringUtils.isBlank(control.getName())) {
- throw new IllegalArgumentException("Control name not defined: "
- + control.getClass());
- }
-
- // Check if page already contains a named value
- Object currentValue = getModel().get(control.getName());
- if (currentValue != null && currentValue instanceof Control) {
- Control currentControl = (Control) currentValue;
- replaceControl(currentControl, control);
- return;
- }
-
- // Note: set parent first as setParent might veto further processing
- control.setParent(this);
-
- getControls().add(control);
- addModel(control.getName(), control);
- }
-
- /**
- * Remove the control from the page. The control will be removed from the
- * pages model and the control parent property will be set to null.
- *
- * @param control the control to remove
- * @throws IllegalArgumentException if the control is null, or if the name
- * of the control is not defined
- */
- public void removeControl(Control control) {
- if (control == null) {
- throw new IllegalArgumentException("Null control parameter");
- }
- if (StringUtils.isBlank(control.getName())) {
- throw new IllegalArgumentException("Control name not defined");
- }
-
- getControls().remove(control);
- getModel().remove(control.getName());
-
- control.setParent(null);
- }
-
- /**
- * Return the list of page Controls.
- *
- * @return the list of page Controls
- */
- public List getControls() {
- if (controls == null) {
- controls = new ArrayList();
- }
- return controls;
- }
-
- /**
- * Return true if the page has any controls defined.
- *
- * @return true if the page has any controls defined
- */
- public boolean hasControls() {
- return (controls != null) && !controls.isEmpty();
- }
-
- /**
- * Return the request context of the page.
- *
- * @return the request context of the page
- */
- public Context getContext() {
- return Context.getThreadLocalContext();
- }
-
- /**
- * Return the HTTP response content type. By default this method returns
- * "text/html".
- *
- * If the request specifies a character encoding via
- * If {@link jakarta.servlet.ServletRequest#getCharacterEncoding()}
- * then this method will return "text/html; charset=encoding".
- *
- * The ClickServlet uses the pages content type for setting the
- * HttpServletResponse content type.
- *
- * @return the HTTP response content type
- */
- public String getContentType() {
- String charset = getContext().getRequest().getCharacterEncoding();
-
- if (charset == null) {
- return "text/html";
-
- } else {
- return "text/html; charset=" + charset;
- }
- }
-
- /**
- * Return the Velocity template formatter object.
- *
- * The ClickServlet adds the format object to the Velocity context using
- * the key "format" so that it can be used in the page template.
- *
- * @return the Velocity template formatter object
- */
- public Format getFormat() {
- return format;
- }
-
- /**
- * Set the Velocity template formatter object.
- *
- * @param value the Velocity template formatter object.
- */
- public void setFormat(Format value) {
- format = value;
- }
-
- /**
- * Return the path to forward the request to.
- *
- * If the {@link #forward} property is not null it will be used to forward
- * the request to in preference to rendering the template defined by the
- * {@link #path} property. The request is forwarded using the
- * RequestDispatcher.
- *
- * See also {@link #getPath()}, {@link #getRedirect()}
- *
- * @return the path to forward the request to
- */
- public String getForward() {
- return forward;
- }
-
- /**
- * Set the path to forward the request to.
- *
- * If the {@link #forward} property is not null it will be used to forward
- * the request to in preference to rendering the template defined by the
- * {@link #path} property. The request is forwarded using the Servlet
- * RequestDispatcher.
- *
- * If forward paths start with a "/"
- * character the forward path is
- * relative to web applications root context, otherwise the path is
- * relative to the requests current location.
- *
- * For example given a web application deployed to context mycorp
- * with the pages:
- *
- *
- * To forward to the customer search.htm page from
- * the web app root you could set forward as
- * setForward("/customer/search.htm")
- * or setForward("customer/search.htm").
- *
- * If a user was currently viewing the add-customer.htm
- * to forward to customer details.htm you could
- * set forward as
- * setForward("/customer/details.htm")
- * or setForward("../details.htm").
- *
- * See also {@link #setPath(String)}, {@link #setRedirect(String)}
- *
- * @param value the path to forward the request to
- */
- public void setForward(String value) {
- forward = value;
- }
-
- /**
- * The Page instance to forward the request to. The given Page object
- * must have a valid {@link #path} defined, as the {@link #path} specifies
- * the location to forward to.
- *
- * @see #setForward(java.lang.String)
- *
- * @param page the Page object to forward the request to.
- */
- public void setForward(Page page) {
- if (page == null) {
- throw new IllegalArgumentException("Null page parameter");
- }
- if (page.getPath() == null) {
- throw new IllegalArgumentException("Page has no path defined");
- }
- setForward(page.getPath());
- getContext().setRequestAttribute(ClickServlet.FORWARD_PAGE, page);
- }
-
- /**
- * Set the request to forward to the given page class.
- *
- * @see #setForward(java.lang.String)
- *
- * @param pageClass the class of the Page to forward the request to
- * @throws IllegalArgumentException if the Page Class is not configured
- * with a unique path
- */
- public void setForward(Class extends Page> pageClass) {
- String target = getContext().getPagePath(pageClass);
-
- // If page class maps to a jsp, convert to htm which allows ClickServlet
- // to process the redirect
- if (target != null && target.endsWith(".jsp")) {
- target = StringUtils.replaceOnce(target, ".jsp", ".htm");
- }
- setForward(target);
- }
-
- /**
- * Return the map of HTTP header to be set in the HttpServletResponse.
- *
- * @return the map of HTTP header to be set in the HttpServletResponse
- */
- public Map getHeaders() {
- if (headers == null) {
- headers = new HashMap();
- }
- return headers;
- }
-
- /**
- * Return true if the page has headers, false otherwise.
- *
- * @return true if the page has headers, false otherwise
- */
- public boolean hasHeaders() {
- return headers != null && !headers.isEmpty();
- }
-
- /**
- * Set the named header with the given value. Value can be either a String,
- * Date or Integer.
- *
- * @param name the name of the header
- * @param value the value of the header, either a String, Date or Integer
- */
- public void setHeader(String name, Object value) {
- if (name == null) {
- throw new IllegalArgumentException("Null header name parameter");
- }
-
- getHeaders().put(name, value);
- }
-
- /**
- * Set the map of HTTP header to be set in the HttpServletResponse.
- *
- * @param value the map of HTTP header to be set in the HttpServletResponse
- */
- public void setHeaders(Map value) {
- headers = value;
- }
-
- /**
- * @deprecated use the new {@link #getHeadElements()} instead
- *
- * @return the HTML includes statements for the control stylesheet and
- * JavaScript files
- */
- public final String getHtmlImports() {
- throw new UnsupportedOperationException("Use getHeadElements instead");
- }
-
- /**
- * Return the list of HEAD {@link org.apache.click.element.Element elements}
- * to be included in the page. Example HEAD elements include
- * {@link org.apache.click.element.JsImport JsImport},
- * {@link org.apache.click.element.JsScript JsScript},
- * {@link org.apache.click.element.CssImport CssImport} and
- * {@link org.apache.click.element.CssStyle CssStyle}.
- *
- * Pages can contribute their own list of HEAD elements by overriding
- * this method.
- *
- * The recommended approach when overriding this method is to use
- * lazy loading to ensure the HEAD elements are only added
- * once and when needed. For example:
- *
- *
- * public MyPage extends Page {
- *
- * public List getHeadElements() {
- * // Use lazy loading to ensure the JS is only added the
- * // first time this method is called.
- * if (headElements == null) {
- * // Get the head elements from the super implementation
- * headElements = super.getHeadElements();
- *
- * // Include the page's external Javascript resource
- * JsImport jsImport = new JsImport("/mycorp/js/mypage.js");
- * headElements.add(jsImport);
- *
- * // Include the page's external Css resource
- * CssImport cssImport = new CssImport("/mycorp/js/mypage.css");
- * headElements.add(cssImport);
- * }
- * return headElements;
- * }
- * }
- *
- * Alternatively one can add the HEAD elements in the Page constructor:
- *
- *
- *
- * One can also add HEAD elements from event handler methods such as
- * {@link #onInit()}, {@link #onGet()}, {@link #onPost()}, {@link #onRender()}
- * etc.
- *
- * The order in which JS and CSS files are included will be preserved in the
- * page.
- *
- * Note: this method must never return null. If no HEAD elements
- * are available this method must return an empty {@link java.util.List}.
- *
- * Also note: a common problem when overriding getHeadElements in
- * subclasses is forgetting to call super.getHeadElements. Consider
- * carefully whether you should call super.getHeadElements or not.
- *
- * @return the list of HEAD elements to be included in the page
- */
- public List getHeadElements() {
- if (headElements == null) {
- headElements = new ArrayList(2);
- }
- return headElements;
- }
-
- /**
- * Return the localized Page resource message for the given resource
- * name or null if not found. The resource message returned will use the
- * Locale obtained from the Context.
- *
- * Pages can define text properties files to store localized messages. These
- * properties files must be stored on the Page class path with a name
- * matching the class name. For example:
- *
- * The page class:
- *
- * package com.mycorp.pages;
- *
- * public class Login extends Page {
- * ..
- *
- * The page class property filenames and their path:
- *
- *
- * Page messages can also be defined in the optional global messages
- * bundle:
- *
- *
- * /click-page.properties
- *
- * To define global page messages simply add click-page.properties
- * file to your application's class path. Message defined in this properties
- * file will be available to all of your application pages.
- *
- * Note messages in your page class properties file will override any
- * messages in the global click-page.properties file.
- *
- * Page messages can be accessed directly in the page template using
- * the $messages reference. For examples:
- *
- *
- * $messages.title
- *
- * Please see the {@link org.apache.click.util.MessagesMap} adaptor for more
- * details.
- *
- * @param name resource name of the message
- * @return the named localized message for the page or null if no message
- * was found
- */
- public String getMessage(String name) {
- if (name == null) {
- throw new IllegalArgumentException("Null name parameter");
- }
- return getMessages().get(name);
- }
-
- /**
- * Return the formatted page message for the given resource name and
- * message format arguments or null if no message was found. The resource
- * message returned will use the Locale obtained from the Context.
- *
- * {@link #getMessage(java.lang.String)} is invoked to retrieve the message
- * for the specified name.
- *
- * @param name resource name of the message
- * @param args the message arguments to format
- * @return the named localized message for the page or null if no message
- * was found
- */
- public String getMessage(String name, Object... args) {
- String value = getMessage(name);
-
- return MessageFormat.format(value, args);
- }
-
- /**
- * Return a Map of localized messages for the Page. The messages returned
- * will use the Locale obtained from the Context.
- *
- * @see #getMessage(String)
- *
- * @return a Map of localized messages for the Page
- * @throws IllegalStateException if the context for the Page has not be set
- */
- public Map getMessages() {
- if (messages == null) {
- if (getContext() != null) {
- messages = getContext().createMessagesMap(getClass(), PAGE_MESSAGES);
-
- } else {
- String msg = "Context not set cannot initialize messages";
- throw new IllegalStateException(msg);
- }
- }
- return messages;
- }
-
- /**
- * Add the named object value to the Pages model map.
- *
- * Please note: if the Page contains an object with a matching name,
- * that object will be replaced by the given value.
- *
- * @param name the key name of the object to add
- * @param value the object to add
- * @throws IllegalArgumentException if the name or value parameters are
- * null
- */
- public void addModel(String name, Object value) {
- if (name == null) {
- String msg = "Cannot add null parameter name to "
- + getClass().getName() + " model";
- throw new IllegalArgumentException(msg);
- }
- if (value == null) {
- String msg = "Cannot add null " + name + " parameter "
- + "to " + getClass().getName() + " model";
- throw new IllegalArgumentException(msg);
- }
-
- getModel().put(name, value);
- }
-
- /**
- * Return the Page's model map. The model is used populate the
- * Velocity Context with is merged with the page template before rendering.
- *
- * @return the Page's model map
- */
- public Map getModel() {
- return model;
- }
-
- /**
- * Return the Page header imports.
- *
- * PageImports are used define the CSS and JavaScript imports and blocks
- * to be included in the page template.
- *
- * The PageImports object will be included in the Page template when the
- * following methods are invoked:
- *
- *
{@link ClickServlet#createTemplateModel(Page)} - for template pages
- *
{@link ClickServlet#setRequestAttributes(Page)} - for JSP pages
- *
- *
- * If you need to tailor the page imports rendered, override this method
- * and modify the PageImports object returned.
- *
- * If you need to create a custom PageImports, override the method
- * {@link ClickServlet#createPageImports(org.openidentityplatform.openam.click.Page)}
- *
- * @deprecated use the new {@link #getHeadElements()} instead
- *
- * @return the Page header imports
- */
- public PageImports getPageImports() {
- return pageImports;
- }
-
- /**
- * Set the Page header imports.
- *
- * PageImports are used define the CSS and JavaScript imports and blocks
- * to be included in the page template.
- *
- * The PageImports references will be included in the Page model when the
- * following methods are invoked:
- *
- *
{@link ClickServlet#createTemplateModel(Page)} - for template pages
- *
{@link ClickServlet#setRequestAttributes(Page)} - for JSP pages
- *
- *
- * If you need to tailor the page imports rendered, override the
- * {@link #getPageImports()} method and modify the PageImports object
- * returned.
- *
- * If you need to create a custom PageImports, override the method
- * {@link ClickServlet#createPageImports(org.openidentityplatform.openam.click.Page)}
- *
- * @deprecated use the new {@link #getHeadElements()} instead
- *
- * @param pageImports the new pageImports instance to set
- */
- public void setPageImports(PageImports pageImports) {
- this.pageImports = pageImports;
- }
-
- /**
- * Return the path of the Template or JSP to render.
- *
- * If this method returns null, Click will not perform any rendering.
- * This is useful when you want to stream or write directly to the
- * HttpServletResponse.
- *
- * See also {@link #getForward()}, {@link #getRedirect()}
- *
- * @return the path of the Template or JSP to render
- */
- public String getPath() {
- return path;
- }
-
- /**
- * Set the path of the Template or JSP to render.
- *
- * By default Click will set the path to the requested page url. Meaning
- * if the url /edit-customer.htm is requested, path will be set
- * to /edit-customer.htm.
- *
- * Here is an example if you want to change the path to a different Template:
- *
- *
- *
- * If path is set to null, Click will not perform any rendering.
- * This is useful when you want to stream or write directly to the
- * HttpServletResponse.
- *
- * See also {@link #setForward(String)}, {@link #setRedirect(String)}
- *
- * @param value the path of the Template or JSP to render
- */
- public void setPath(String value) {
- path = value;
- }
-
- /**
- * Return the path to redirect the request to.
- *
- * If the {@link #redirect} property is not null it will be used to redirect
- * the request in preference to {@link #forward} or {@link #path} properties.
- * The request is redirected to using the HttpServletResponse.setRedirect()
- * method.
- *
- * See also {@link #getForward()}, {@link #getPath()}
- *
- * @return the path to redirect the request to
- */
- public String getRedirect() {
- return redirect;
- }
-
- /**
- * Return true if the page is stateful and should be saved in the users
- * HttpSession between requests, default value is false.
- *
- * @deprecated stateful pages are not supported anymore, use stateful
- * Controls instead
- *
- * @return true if the page is stateful and should be saved in the users
- * session
- */
- public boolean isStateful() {
- return stateful;
- }
-
- /**
- * Set whether the page is stateful and should be saved in the users
- * HttpSession between requests.
- *
- * Click will synchronize on the page instance. This ensures that if
- * multiple requests arrive from the same user for the page, only one
- * request can access the page at a time.
- *
- * Stateful pages are stored in the HttpSession using the key
- * page.getClass().getName().
- *
- * It is worth noting that Click checks a Page's stateful property after
- * each request. Thus it becomes possible to enable a stateful Page for a
- * number of requests and then setting it to false again at which
- * point Click will remove the Page from the HttpSession, freeing up memory
- * for the server.
- *
- * @deprecated stateful pages are not supported anymore, use stateful
- * Controls instead
- *
- * @param stateful the flag indicating whether the page should be saved
- * between user requests
- */
- public void setStateful(boolean stateful) {
- this.stateful = stateful;
- if (isStateful()) {
- getContext().getSession();
- }
- }
-
- /**
- * Return true if the Control head elements should be included in the page
- * template, false otherwise. Default value is true.
- *
- * @see #setIncludeControlHeadElements(boolean)
- *
- * @return true if the Control head elements should be included in the page
- * template, false otherwise
- */
- public boolean isIncludeControlHeadElements() {
- return includeControlHeadElements;
- }
-
- /**
- * Set whether the Control head elements should be included in the page
- * template.
- *
- * By setting this value to false, Click won't include Control's
- * {@link #getHeadElements() head elements}, however the Page head elements
- * will still be included.
- *
- * This allows one to create a single JavaScript and CSS resource file for
- * the entire Page which increases performance, since the browser only has
- * to load one resource, instead of multiple resources.
- *
- * Below is an example:
- *
- *
- * public class HomePage extends Page {
- *
- * private Form form = new Form("form");
- *
- * public HomePage() {
- * // Indicate that Controls should not import their head elements
- * setIncludeControlHeadElements(false);
- *
- * form.add(new EmailField("email");
- * addControl(form);
- * }
- *
- * // Include the Page JavaScript and CSS resources
- * public List getHeadElements() {
- * if (headElements == null) {
- * headElements = super.getHeadElements();
- *
- * // Include the Page CSS resource. This resource should combine
- * // all the CSS necessary for the page
- * headElements.add(new CssImport("/assets/css/home-page.css"));
- *
- * // Include the Page JavaScript resource. This resource should
- * // combine all the JavaScript necessary for the page
- * headElements.add(new JsImport("/assets/js/home-page.js"));
- * }
- * return headElements;
- * }
- * }
- *
- * @param includeControlHeadElements flag indicating whether Control
- * head elements should be included in the page
- */
- public void setIncludeControlHeadElements(boolean includeControlHeadElements) {
- this.includeControlHeadElements = includeControlHeadElements;
- }
-
- /**
- * Set the location to redirect the request to.
- *
- * If the {@link #redirect} property is not null it will be used to redirect
- * the request in preference to the {@link #forward} and {@link #path}
- * properties. The request is redirected using the HttpServletResponse.setRedirect()
- * method.
- *
- * If the redirect location begins with a "/"
- * character the redirect location will be prefixed with the web applications
- * context path. Note if the given location is already prefixed
- * with the context path, Click won't add it a second time.
- *
- * For example if an application is deployed to the context
- * "mycorp" calling
- * setRedirect("/customer/details.htm")
- * will redirect the request to:
- * "/mycorp/customer/details.htm"
- *
- * If the redirect location does not begin with a "/"
- * character the redirect location will be used as specified. Thus if the
- * location is http://somehost.com/myapp/customer.jsp,
- * Click will redirect to that location.
- *
- * JSP note: when redirecting to a JSP template keep in mind that the
- * JSP template won't be processed by Click, as ClickServlet is mapped to
- * *.htm. Instead JSP templates are processed by the Servlet
- * container JSP engine.
- *
- * So if you have a situation where a Page Class
- * (Customer.class) is mapped to the JSP
- * ("/customer.jsp") and you want to redirect to
- * Customer.class, you could either redirect to
- * ("/customer.htm") or
- * use the alternative redirect utility {@link #setRedirect(java.lang.Class)}.
- *
- * Please note that Click will url encode the location by invoking
- * response.encodeRedirectURL(location) before redirecting.
- *
- * See also {@link #setRedirect(java.lang.String, java.util.Map)},
- * {@link #setForward(String)}, {@link #setPath(String)}
- *
- * @param location the path to redirect the request to
- */
- public void setRedirect(String location) {
- setRedirect(location, null);
- }
-
- /**
- * Set the request to redirect to the give page class.
- *
- * @see #setRedirect(java.lang.String)
- *
- * @param pageClass the class of the Page to redirect the request to
- * @throws IllegalArgumentException if the Page Class is not configured
- * with a unique path
- */
- public void setRedirect(Class extends Page> pageClass) {
- setRedirect(pageClass, null);
- }
-
- /**
- * Set the request to redirect to the given location and append
- * the map of request parameters to the location URL.
- *
- * The map keys will be used as the request parameter names and the map
- * values will be used as the request parameter values. For example:
- *
- *
- *
- * @see #setRedirect(java.lang.String)
- *
- * @param location the path to redirect the request to
- * @param params the map of request parameter name and value pairs
- */
- public void setRedirect(String location, Map params) {
- Context context = getContext();
- if (StringUtils.isNotBlank(location)) {
- if (location.charAt(0) == '/') {
- String contextPath = context.getRequest().getContextPath();
-
- // Guard against adding duplicate context path
- if (!location.startsWith(contextPath + '/')) {
- location = contextPath + location;
- }
- }
- }
-
- if (params != null && !params.isEmpty()) {
- HtmlStringBuffer buffer = new HtmlStringBuffer();
-
- Iterator extends Map.Entry> i = params.entrySet().iterator();
- while (i.hasNext()) {
- Map.Entry entry = i.next();
- String paramName = entry.getKey();
- Object paramValue = entry.getValue();
-
- // Check for multivalued parameter
- if (paramValue instanceof String[]) {
- String[] paramValues = (String[]) paramValue;
- for (int j = 0; j < paramValues.length; j++) {
- buffer.append(paramName);
- buffer.append("=");
- buffer.append(ClickUtils.encodeUrl(paramValues[j], context));
- if (j < paramValues.length - 1) {
- buffer.append("&");
- }
- }
- } else {
- if (paramValue != null) {
- buffer.append(paramName);
- buffer.append("=");
- buffer.append(ClickUtils.encodeUrl(paramValue, context));
- }
- }
- if (i.hasNext()) {
- buffer.append("&");
- }
- }
-
- if (buffer.length() > 0) {
- if (location.contains("?")) {
- location += "&" + buffer.toString();
- } else {
- location += "?" + buffer.toString();
- }
- }
- }
-
- redirect = location;
- }
-
- /**
- * Set the request to redirect to the given page class and and append
- * the map of request parameters to the page URL.
- *
- * The map keys will be used as the request parameter names and the map
- * values will be used as the request parameter values.
- *
- * @see #setRedirect(java.lang.String, java.util.Map)
- * @see #setRedirect(java.lang.String)
- *
- * @param pageClass the class of the Page to redirect the request to
- * @param params the map of request parameter name and value pairs
- * @throws IllegalArgumentException if the Page Class is not configured
- * with a unique path
- */
- public void setRedirect(Class extends Page> pageClass,
- Map params) {
-
- String target = getContext().getPagePath(pageClass);
-
- // If page class maps to a jsp, convert to htm which allows ClickServlet
- // to process the redirect
- if (target != null && target.endsWith(".jsp")) {
- target = StringUtils.replaceOnce(target, ".jsp", ".htm");
- }
-
- setRedirect(target, params);
- }
-
- /**
- * Return the path of the page border template to render, by default this
- * method returns {@link #getPath()}.
- *
- * Pages can override this method to return an alternative border page
- * template. This is very useful when implementing an standardized look and
- * feel for a web site. The example below provides a BorderedPage base Page
- * which other site templated Pages should extend.
- *
- *
- * public class BorderedPage extends Page {
- * public String getTemplate() {
- * return"border.htm";
- * }
- * }
- *
- * The BorderedPage returns the page border template "border.htm":
- *
- *
- *
- * Other pages insert their content into this template, via their
- * {@link #path} property using the Velocity
- * #parse
- * directive. Note the $path value is automatically
- * added to the VelocityContext by the ClickServlet.
- *
- * @return the path of the page template to render, by default returns
- * {@link #getPath()}
- */
- public String getTemplate() {
- return template == null ? getPath() : template;
- }
-
- /**
- * Set the page border template path.
- *
- * Note: if this value is not set, {@link #getTemplate()} will default
- * to {@link #getPath()}.
- *
- * @param template the border template path
- */
- public void setTemplate(String template) {
- this.template = template;
- }
-
- // Private methods --------------------------------------------------------
-
- /**
- * Replace the current control with the new control.
- *
- * @param currentControl the control currently contained in the page
- * @param newControl the control to replace the current control container in
- * the page
- *
- * @throws IllegalStateException if the currentControl is not contained in
- * the page
- */
- private void replaceControl(Control currentControl, Control newControl) {
-
- // Current control and new control are referencing the same object
- // so we exit early
- if (currentControl == newControl) {
- return;
- }
-
- int controlIndex = getControls().indexOf(currentControl);
- if (controlIndex == -1) {
- throw new IllegalStateException("Cannot replace the given control"
- + " because it is not present in the page");
- }
-
- // Note: set parent first since setParent might veto further processing
- newControl.setParent(this);
- currentControl.setParent(null);
-
- // Set control to current control index
- getControls().set(controlIndex, newControl);
-
- addModel(newControl.getName(), newControl);
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/PageInterceptor.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/PageInterceptor.java
deleted file mode 100644
index 42b4829629..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/PageInterceptor.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click;
-
-/**
- * Provides a Page life cycle interceptor. Classes implementing this interface
- * can be used to listen for key page life cycle events and abort further page
- * processing if required.
- *
- * PageInterceptors can be used for many different purposes including:
- *
- *
enforcing application wide page security policies
- *
injecting dependencies into page objects
- *
logging and profiling page performance
- *
- *
- * A Click application can define multiple page interceptors that are invoked in
- * the order in which they are returned by the ConfigService.
- *
- *
Scope
- *
- * Page interceptors can be defined with a request level scope, whereby a new
- * page interceptor will be created with each page request providing a thread
- * safe programming model.
- *
- * Please note, as new interceptor instances are created with each request, care
- * should be taken to ensure that these objects are light weight and do not
- * introduce a performance bottleneck into your application.
- *
- * Alternatively, page interceptors can be defined with application level scope
- * whereby a single instance is created for the application and is used for
- * all requests.
- *
- * Note application scope interceptors are more efficient that request scope
- * interceptors, but you are responsible for ensuring that they are thread safe
- * and support reentrant method invocations as multiple page requests are
- * processed at the same time.
- *
- *
Configuration
- *
- * Application PageInterceptors are configured in the click.xml
- * configuration file. PageInterceptors must support construction using a
- * no-args public constructor.
- *
- * Page interceptors can have multiple properties configured with their XML
- * definition which are set after the constructor has been called. Properties
- * are set using OGNL via {@link org.apache.click.util.PropertyUtils}.
- *
- * An example configuration is provided below:
- *
- *
- *
- * The default scope for page interceptors is "request", but this can be configured
- * as "application" as is done in the example configuration above.
- *
- *
- */
-public interface PageInterceptor {
-
- /**
- * Provides a before page object creation interceptor method, which is passed
- * the class of the page to be instantiated and the page request context.
- * If this method returns true then the normal page processing is performed,
- * otherwise if this method returns false the page instance is never created
- * and the request is considered to have been handled.
- *
- * @param pageClass the class of the page to be instantiated
- * @param context the page request context
- * @return true to continue normal page processing or false whereby the
- * request is considered to be handled
- */
- public boolean preCreate(Class extends Page> pageClass, Context context);
-
- /**
- * Provides a post page object creation interceptor method, which is passed
- * the instance of the newly created page. This interceptor method is called
- * before the page {@link Page#onSecurityCheck()} method is invoked.
- *
- * If this method returns true then the normal page processing is performed,
- * otherwise if this method returns false the request is considered to have
- * been handled.
- *
- * Please note the page {@link Page#onDestroy()} method will still be invoked.
- *
- * @param page the newly instantiated page instance
- * @return true to continue normal page processing or false whereby the
- * request is considered to be handled
- */
- public boolean postCreate(Page page);
-
- /**
- * Provides a page interceptor before response method. This method is invoked
- * prior to the page redirect, forward or rendering phase.
- *
- * If this method returns true then the normal page processing is performed,
- * otherwise if this method returns false request is considered to have been
- * handled.
- *
- * Please note the page {@link Page#onDestroy()} method will still be invoked.
- *
- * @param page the newly instantiated page instance
- * @return true to continue normal page processing or false whereby the
- * request is considered to be handled
- */
- public boolean preResponse(Page page);
-
- /**
- * Provides a post page destroy interceptor method. This interceptor method
- * is called immediately after the page {@link Page#onDestroy()} method is
- * invoked.
- *
- * @param page the page object which has just been destroyed
- */
- public void postDestroy(Page page);
-
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/ajax/AjaxBehavior.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/ajax/AjaxBehavior.java
deleted file mode 100644
index fac9f69b4e..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/ajax/AjaxBehavior.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.openidentityplatform.openam.click.ajax;
-
-
-import org.openidentityplatform.openam.click.ActionResult;
-import org.openidentityplatform.openam.click.Behavior;
-import org.openidentityplatform.openam.click.Context;
-import org.openidentityplatform.openam.click.Control;
-
-/**
- * AjaxBehavior extends the basic Behavior functionality to allow Controls to
- * handle and process incoming Ajax requests.
- *
- * To handle an Ajax request, AjaxBehavior exposes the listener method:
- * {@link #onAction(org.openidentityplatform.openam.click.Control) onAction}.
- * The onAction method returns an ActionResult that is rendered back
- * to the browser.
- *
- * Before Click invokes the onAction method it checks whether the request
- * is targeted at the AjaxBehavior by invoking the method
- * {@link #isAjaxTarget(org.openidentityplatform.openam.click.Context) Behavior.isAjaxTarget()}.
- * Click will only invoke onAction if isAjaxTarget returns true.
- */
-public interface AjaxBehavior extends Behavior {
-
- /**
- * This method can be implemented to handle and respond to an Ajax request.
- * For example:
- *
- *
- * public void onInit() {
- * ActionLink link = new ActionLink("link");
- * link.addBehaior(new DefaultAjaxBehavior() {
- *
- * public ActionResult onAction(Control source) {
- * ActionResult result = new ActionResult("<h1>Hello world</h1>", ActionResult.HTML);
- * return result;
- * }
- * });
- * }
- *
- * @param source the control the behavior is attached to
- * @return the action result instance
- */
- public ActionResult onAction(Control source);
-
- /**
- * Return true if the behavior is the request target, false otherwise.
- *
- * This method is queried by Click to determine if the behavior's
- * {@link #onAction(org.openidentityplatform.openam.click.Control)} method should be called in
- * response to a request.
- *
- * By exposing this method through the Behavior interface it provides
- * implementers with fine grained control over whether the Behavior's
- * {@link #onAction(org.openidentityplatform.openam.click.Control)} method should be invoked or not.
- *
- * Below is an example implementation:
- *
- *
- * public CustomBehavior implements Behavior {
- *
- * private String eventType;
- *
- * public CustomBehavior(String eventType) {
- * // The event type of the behavior
- * super(eventType);
- * }
- *
- * public boolean isAjaxTarget(Context context) {
- * // Retrieve the eventType parameter from the incoming request
- * String eventType = context.getRequestParameter("type");
- *
- * // Check if this Behavior's eventType matches the request
- * // "type" parameter
- * return StringUtils.equalsIgnoreCase(this.eventType, eventType);
- * }
- *
- * public ActionResult onAction(Control source) {
- * // If isAjaxTarget returned true, the onAction method will be
- * // invoked
- * ...
- * }
- * }
- *
- * @param context the request context
- * @return true if the behavior is the request target, false otherwise
- */
- public boolean isAjaxTarget(Context context);
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractContainer.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractContainer.java
deleted file mode 100644
index fa553be154..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractContainer.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click.control;
-
-import org.openidentityplatform.openam.click.Control;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-import org.openidentityplatform.openam.click.util.ContainerUtils;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Provides a default implementation of the {@link org.openidentityplatform.openam.click.control.Container} interface
- * to make it easier for developers to create their own containers.
- *
- * Subclasses can override {@link #getTag()} to return a specific HTML element.
- *
- * The following example shows how to create an HTML div element:
- *
- *
- * public class Div extends AbstractContainer {
- *
- * public String getTag() {
- * // Return the HTML tag
- * return "div";
- * }
- * }
- */
-public abstract class AbstractContainer extends AbstractControl implements
- Container {
-
- // Constants --------------------------------------------------------------
-
- private static final long serialVersionUID = 1L;
-
- // Instance Variables -----------------------------------------------------
-
- /** The list of controls. */
- protected List controls;
-
- /** The map of controls keyed by field name. */
- protected Map controlMap;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * Create a container with no name defined.
- */
- public AbstractContainer() {
- }
-
- /**
- * Create a container with the given name.
- *
- * @param name the container name
- */
- public AbstractContainer(String name) {
- super(name);
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * @see org.openidentityplatform.openam.click.control.Container#add(Control).
- *
- * Please note: if the container contains a control with the same name
- * as the given control, that control will be
- * {@link #replace(Control, Control) replaced}
- * by the given control. If a control has no name defined it cannot be replaced.
- *
- * @param control the control to add to the container
- * @return the control that was added to the container
- * @throws IllegalArgumentException if the control is null
- */
- public Control add(Control control) {
- return insert(control, getControls().size());
- }
-
- /**
- * Add the control to the container at the specified index, and return the
- * added instance.
- *
- * Please note: if the container contains a control with the same name
- * as the given control, that control will be
- * {@link #replace(Control, Control) replaced}
- * by the given control. If a control has no name defined it cannot be replaced.
- *
- * Also note if the specified control already has a parent assigned,
- * it will automatically be removed from that parent and inserted into this
- * container.
- *
- * @see org.openidentityplatform.openam.click.control.Container#insert(Control, int)
- *
- * @param control the control to add to the container
- * @param index the index at which the control is to be inserted
- * @return the control that was added to the container
- *
- * @throws IllegalArgumentException if the control is null or if the control
- * and container is the same instance
- *
- * @throws IndexOutOfBoundsException if index is out of range
- * (index < 0 || index > getControls().size())
- */
- public Control insert(Control control, int index) {
- // Check if panel already contains the control
- String controlName = control.getName();
- if (controlName != null) {
- // Check if container already contains the control
- Control currentControl = getControlMap().get(control.getName());
-
- // If container already contains the control do a replace
- if (currentControl != null) {
-
- // Current control and new control are referencing the same object
- // so we exit early
- if (currentControl == control) {
- return control;
- }
-
- // If the two controls are different objects, we remove the current
- // control and add the given control
- return replace(currentControl, control);
- }
- }
-
- return ContainerUtils.insert(this, control, index, getControlMap());
- }
-
- /**
- * @seeorg.openidentityplatform.openam.click.control.Container#remove(Control).
- *
- * @param control the control to remove from the container
- * @return true if the control was removed from the container
- * @throws IllegalArgumentException if the control is null
- */
- public boolean remove(Control control) {
- return ContainerUtils.remove(this, control, getControlMap());
- }
-
- /**
- * Replace the control in the container at the specified index, and return
- * the newly added control.
- *
- * @see org.openidentityplatform.openam.click.control.Container#replace(Control, Control)
- *
- * @param currentControl the control currently contained in the container
- * @param newControl the control to replace the current control contained in
- * the container
- * @return the new control that replaced the current control
- *
- * @deprecated this method was used for stateful pages, which have been deprecated
- *
- * @throws IllegalArgumentException if the currentControl or newControl is
- * null
- * @throws IllegalStateException if the currentControl is not contained in
- * the container
- */
- public Control replace(Control currentControl, Control newControl) {
- int controlIndex = getControls().indexOf(currentControl);
- return ContainerUtils.replace(this, currentControl, newControl,
- controlIndex, getControlMap());
- }
-
- /**
- * @see org.apache.click.control.Container#getControls().
- *
- * @return the sequential list of controls held by the container
- */
- public List getControls() {
- if (controls == null) {
- controls = new ArrayList();
- }
- return controls;
- }
-
- /**
- * @see org.apache.click.control.Container#getControl(String)
- *
- * @param controlName the name of the control to get from the container
- * @return the named control from the container if found or null otherwise
- */
- public Control getControl(String controlName) {
- if (hasControls()) {
- return getControlMap().get(controlName);
- }
- return null;
- }
-
- /**
- * @see Container#contains(Control)
- *
- * @param control the control whose presence in this container is to be tested
- * @return true if the container contains the specified control
- */
- public boolean contains(Control control) {
- return getControls().contains(control);
- }
-
- /**
- * Returns true if this container has existing controls, false otherwise.
- *
- * @return true if the container has existing controls, false otherwise.
- */
- public boolean hasControls() {
- return (controls != null) && !controls.isEmpty();
- }
-
- /**
- * Return the map of controls where each map's key / value pair will consist
- * of the control name and instance.
- *
- * Controls added to the container that did not specify a {@link #name},
- * will not be included in the returned map.
- *
- * @return the map of controls
- */
- public Map getControlMap() {
- if (controlMap == null) {
- controlMap = new HashMap();
- }
- return controlMap;
- }
-
- /**
- * @see org.openidentityplatform.openam.click.control.AbstractControl#getControlSizeEst().
- *
- * @return the estimated rendered control size in characters
- */
- @Override
- public int getControlSizeEst() {
- int size = 20;
-
- if (getTag() != null && hasAttributes()) {
- size += 20 * getAttributes().size();
- }
-
- if (hasControls()) {
- size += getControls().size() * size;
- }
-
- return size;
- }
-
- /**
- * @see Control#onProcess().
- *
- * @return true to continue Page event processing or false otherwise
- */
- @Override
- public boolean onProcess() {
-
- boolean continueProcessing = true;
-
- for (Control control : getControls()) {
- if (!control.onProcess()) {
- continueProcessing = false;
- }
- }
-
- dispatchActionEvent();
-
- return continueProcessing;
- }
-
- /**
- * @see Control#onDestroy()
- */
- @Override
- public void onDestroy() {
- for (Control control : getControls()) {
- try {
- control.onDestroy();
- } catch (Throwable t) {
- ClickUtils.getLogService().error("onDestroy error", t);
- }
- }
- }
-
- /**
- * @see Control#onInit()
- */
- @Override
- public void onInit() {
- super.onInit();
- for (Control control : getControls()) {
- control.onInit();
- }
- }
-
- /**
- * @see Control#onRender()
- */
- @Override
- public void onRender() {
- for (Control control : getControls()) {
- control.onRender();
- }
- }
-
- /**
- * Render the HTML representation of the container and all its child
- * controls to the specified buffer.
- *
- * If {@link #getTag()} returns null, this method will render only its
- * child controls.
- *
- * @see org.openidentityplatform.openam.click.control.AbstractControl#render(HtmlStringBuffer)
- *
- * @param buffer the specified buffer to render the control's output to
- */
- @Override
- public void render(HtmlStringBuffer buffer) {
-
- //If tag is set, render it
- if (getTag() != null) {
- renderTagBegin(getTag(), buffer);
- buffer.closeTag();
- if (hasControls()) {
- buffer.append("\n");
- }
- renderContent(buffer);
- renderTagEnd(getTag(), buffer);
-
- } else {
-
- //render only content because no tag is specified
- renderContent(buffer);
- }
- }
-
- /**
- * Returns the HTML representation of this control.
- *
- * This method delegates the rendering to the method
- * {@link #render(HtmlStringBuffer)}. The size of buffer
- * is determined by {@link #getControlSizeEst()}.
- *
- * @see Object#toString()
- *
- * @return the HTML representation of this control
- */
- @Override
- public String toString() {
- HtmlStringBuffer buffer = new HtmlStringBuffer(getControlSizeEst());
- render(buffer);
- return buffer.toString();
- }
-
- // Protected Methods ------------------------------------------------------
-
- /**
- * @see AbstractControl#renderTagEnd(String, HtmlStringBuffer).
- *
- * @param tagName the name of the tag to close
- * @param buffer the buffer to append the output to
- */
- @Override
- protected void renderTagEnd(String tagName, HtmlStringBuffer buffer) {
- buffer.elementEnd(tagName);
- }
-
- /**
- * Render this container content to the specified buffer.
- *
- * @param buffer the buffer to append the output to
- */
- protected void renderContent(HtmlStringBuffer buffer) {
- renderChildren(buffer);
- }
-
- /**
- * Render this container children to the specified buffer.
- *
- * @see #getControls()
- *
- * @param buffer the buffer to append the output to
- */
- protected void renderChildren(HtmlStringBuffer buffer) {
- for (Control control : getControls()) {
-
- int before = buffer.length();
- control.render(buffer);
-
- int after = buffer.length();
- if (before != after) {
- buffer.append("\n");
- }
- }
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractControl.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractControl.java
deleted file mode 100644
index a651f26699..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractControl.java
+++ /dev/null
@@ -1,1121 +0,0 @@
-package org.openidentityplatform.openam.click.control;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.Map.Entry;
-
-import jakarta.servlet.ServletContext;
-
-import org.openidentityplatform.openam.click.ActionEventDispatcher;
-import org.openidentityplatform.openam.click.ActionListener;
-import org.openidentityplatform.openam.click.Behavior;
-import org.openidentityplatform.openam.click.ControlRegistry;
-import org.openidentityplatform.openam.click.Context;
-import org.openidentityplatform.openam.click.Control;
-import org.openidentityplatform.openam.click.Page;
-import org.openidentityplatform.openam.click.element.Element;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-
-/**
- * Provides a default implementation of the {@link Control} interface
- * to make it easier for developers to create their own controls.
- *
- * Subclasses are expected to at least override {@link #getTag()}
- * to differentiate the control. However some controls do not map cleanly
- * to an html tag, in which case you can override
- * {@link #render(org.openidentityplatform.openam.click.util.HtmlStringBuffer)} for complete control
- * over the output.
- *
- * Below is an example of creating a new control called MyField:
- *
- * public class MyField extends AbstractControl {
- *
- * private String value;
- *
- * public void setValue(String value) {
- * this.value = value;
- * }
- *
- * public String getValue() {
- * return value;
- * }
- *
- * public String getTag() {
- * // Return the HTML tag
- * return "input";
- * }
- *
- * public boolean onProcess() {
- * // Bind the request parameter to the field value
- * String requestValue = getContext().getRequestParameter(getName());
- * setValue(requestValue);
- *
- * // Invoke any listener of MyField
- * return dispatchActionEvent();
- * }
- * }
- *
- * By overriding {@link #getTag()} one can specify the html tag to render.
- *
- * Overriding {@link #onProcess()} allows one to bind the servlet request
- * parameter to MyField value. The {@link #dispatchActionEvent()} method
- * registers the listener for this control on the Context. Once the onProcess
- * event has finished, all registered listeners will be fired.
- *
- * To view the html rendered by MyField invoke the control's {@link #toString()}
- * method:
- *
- *
- * public class Test {
- * public static void main (String args[]) {
- * // Create mock context in which to test the control.
- * MockContext.initContext();
- *
- * String fieldName = "myfield";
- * MyField myfield = new MyField(fieldName);
- * String output = myfield.toString();
- * System.out.println(output);
- * }
- * }
- *
- * Executing the above test results in the following output:
- *
- *
- * <input name="myfield" id="myfield"/>
- *
- * Also see {@link org.apache.click.Control} javadoc for an explanation of the
- * Control execution sequence.
- *
- *
Message Resources and Internationalization (i18n)
- *
- * Controls support a hierarchy of resource bundles for displaying messages.
- * These localized messages can be accessed through the methods:
- *
- *
- *
{@link #getMessage(String)}
- *
{@link #getMessage(String, Object...)}
- *
{@link #getMessages()}
- *
- *
- * The order in which localized messages resolve are described in the
- * user guide.
- */
-public abstract class AbstractControl implements Control {
-
- // Constants --------------------------------------------------------------
-
- private static final long serialVersionUID = 1L;
-
- // Instance Variables -----------------------------------------------------
-
- /** The control's action listener. */
- protected ActionListener actionListener;
-
- /** The control's list of {@link org.apache.click.Behavior behaviors}. */
- protected Set behaviors;
-
- /**
- * The list of page HTML HEAD elements including: Javascript imports,
- * Css imports, inline Javascript and inline Css.
- */
- protected List headElements;
-
- /** The Control attributes Map. */
- protected Map attributes;
-
- /** The Control localized messages Map. */
- protected transient Map messages;
-
- /** The Control name. */
- protected String name;
-
- /** The control's parent. */
- protected Object parent;
-
- /**
- * The Map of CSS style attributes.
- *
- * @deprecated use {@link #addStyleClass(String)} and
- * {@link #removeStyleClass(String)} instead.
- */
- protected Map styles;
-
- /** The listener target object. */
- protected Object listener;
-
- /** The listener method name. */
- protected String listenerMethod;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * Create a control with no name defined.
- */
- public AbstractControl() {
- }
-
- /**
- * Create a control with the given name.
- *
- * @param name the control name
- */
- public AbstractControl(String name) {
- if (name != null) {
- setName(name);
- }
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * Returns the controls html tag.
- *
- * Subclasses should override this method and return the correct tag.
- *
- * This method returns null by default.
- *
- * Example tags include table, form, a and
- * input.
- *
- * @return this controls html tag
- */
- public String getTag() {
- return null;
- }
-
- /**
- * Return the control's action listener. If the control has a listener
- * target and listener method defined, this method will return an
- * {@link org.apache.click.ActionListener} instance.
- *
- * @return the control's action listener
- */
- public ActionListener getActionListener() {
- if (actionListener == null && listener != null && listenerMethod != null) {
- actionListener = new ActionListener() {
-
- private static final long serialVersionUID = 1L;
-
- public boolean onAction(Control source) {
- return ClickUtils.invokeListener(listener, listenerMethod);
- }
- };
- }
- return actionListener;
- }
-
- /**
- * Set the control's action listener.
- *
- * @param listener the control's action listener
- */
- public void setActionListener(ActionListener listener) {
- this.actionListener = listener;
- }
-
- /**
- * Returns true if this control has any
- * Behaviors registered, false otherwise.
- *
- * @return true if this control has any Behaviors registered,
- * false otherwise
- */
- public boolean hasBehaviors() {
- return (behaviors != null && !behaviors.isEmpty());
- }
-
- /**
- * Add the given Behavior to the control's Set of
- * {@link #getBehaviors() Behaviors}.
- *
- * In addition, the Control will be registered with the
- * {@link org.apache.click.ControlRegistry#registerAjaxTarget(org.apache.click.Control) ControlRegistry}
- * as a potential Ajax target control and to have it's
- * Behaviors processed by the Click runtime.
- *
- * @param behavior the Behavior to add
- */
- public void addBehavior(Behavior behavior) {
- if (behavior == null) {
- throw new IllegalArgumentException("Behavior cannot be null");
- }
-
- // Ensure we register the behavior only once
- if (!hasBehaviors()) {
- ControlRegistry.registerAjaxTarget(this);
- }
-
- getBehaviors().add(behavior);
- }
-
- /**
- * Remove the given Behavior from the Control's Set of
- * {@link #getBehaviors() Behaviors}.
- *
- * @param behavior the Behavior to remove
- */
- public void removeBehavior(Behavior behavior) {
- getBehaviors().remove(behavior);
- }
-
- /**
- * Returns the Set of Behaviors for this control.
- *
- * @return the Set of Behaviors for this control
- */
- public Set getBehaviors() {
- if (behaviors == null) {
- behaviors = new HashSet();
- }
- return behaviors;
- }
-
- /**
- * Returns true if this control is an AJAX target, false
- * otherwise.
- *
- * The control is defined as an Ajax target if the control {@link #getId() ID}
- * is send as a request parameter.
- *
- * @param context the request context
- * @return true if this control is an AJAX target, false
- * otherwise
- */
- public boolean isAjaxTarget(Context context) {
- // TODO each control could have an optimized version of isAjaxTarget
- // targeting specifically that control. For now we just check that the
- // control id is present. Not all controls can use an ID for example:
- // ActionLink
- String id = getId();
- if (id != null) {
- return context.hasRequestParameter(id);
- } else {
- return false;
- }
- }
-
- /**
- * Return the control HTML attribute with the given name, or null if the
- * attribute does not exist.
- *
- * @param name the name of link HTML attribute
- * @return the link HTML attribute
- */
- public String getAttribute(String name) {
- if (hasAttributes()) {
- return getAttributes().get(name);
- }
- return null;
- }
-
- /**
- * Set the control attribute with the given attribute name and value. You would
- * generally use attributes if you were creating the entire Control
- * programmatically and rendering it with the {@link #toString()} method.
- *
- * For example given the ActionLink:
- *
- *
- *
- * Note: for style and class attributes you can
- * also use the methods {@link #setStyle(String, String)} and
- * {@link #addStyleClass(String)}.
- *
- * @see #setStyle(String, String)
- * @see #addStyleClass(String)
- * @see #removeStyleClass(String)
- *
- * @param name the attribute name
- * @param value the attribute value
- * @throws IllegalArgumentException if name parameter is null
- */
- public void setAttribute(String name, String value) {
- if (name == null) {
- throw new IllegalArgumentException("Null name parameter");
- }
-
- if (value != null) {
- getAttributes().put(name, value);
- } else {
- getAttributes().remove(name);
- }
- }
-
- /**
- * Return the control's attributes Map.
- *
- * @return the control's attributes Map.
- */
- public Map getAttributes() {
- if (attributes == null) {
- attributes = new HashMap();
- }
- return attributes;
- }
-
- /**
- * Return true if the control has attributes or false otherwise.
- *
- * @return true if the control has attributes on false otherwise
- */
- public boolean hasAttributes() {
- return attributes != null && !attributes.isEmpty();
- }
-
- /**
- * Returns true if specified attribute is defined, false otherwise.
- *
- * @param name the specified attribute to check
- * @return true if name is a defined attribute
- */
- public boolean hasAttribute(String name) {
- return hasAttributes() && getAttributes().containsKey(name);
- }
-
- /**
- * @see org.apache.click.Control#getContext()
- *
- * @return the Page request Context
- */
- public Context getContext() {
- return Context.getThreadLocalContext();
- }
-
- /**
- * @see Control#getName()
- *
- * @return the name of the control
- */
- public String getName() {
- return name;
- }
-
- /**
- * @see Control#setName(String)
- *
- * @param name of the control
- * @throws IllegalArgumentException if the name is null
- */
- public void setName(String name) {
- if (name == null) {
- throw new IllegalArgumentException("Null name parameter");
- }
- this.name = name;
- }
-
- /**
- * Return the "id" attribute value if defined, or the control name otherwise.
- *
- * @see org.apache.click.Control#getId()
- *
- * @return HTML element identifier attribute "id" value
- */
- public String getId() {
- String id = getAttribute("id");
-
- return (id != null) ? id : getName();
- }
-
- /**
- * Set the HTML id attribute for the control with the given value.
- *
- * @param id the element HTML id attribute value to set
- */
- public void setId(String id) {
- if (id != null) {
- setAttribute("id", id);
- } else {
- getAttributes().remove("id");
- }
- }
-
- /**
- * Return the localized message for the given key or null if not found.
- * The resource message returned will use the Locale obtained from the
- * Context.
- *
- * This method will attempt to lookup the localized message in the
- * parent's messages, which resolves to the Page's resource bundle.
- *
- * If the message was not found, this method will attempt to look up the
- * value in the /click-control.properties message properties file,
- * through the method {@link #getMessages()}.
- *
- * If still not found, this method will return null.
- *
- * @param name the name of the message resource
- * @return the named localized message for the control, or null if not found
- */
- public String getMessage(String name) {
- if (name == null) {
- throw new IllegalArgumentException("Null name parameter");
- }
-
- String message = null;
-
- message = ClickUtils.getParentMessage(this, name);
-
- if (message == null && getMessages().containsKey(name)) {
- message = getMessages().get(name);
- }
-
- return message;
- }
-
- /**
- * Return the formatted message for the given resource name and message
- * format arguments or null if no message was found. The resource
- * message returned will use the Locale obtained from the Context.
- *
- * {@link #getMessage(java.lang.String)} is invoked to retrieve the message
- * for the specified name.
- *
- * @param name resource name of the message
- * @param args the message arguments to format
- * @return the named localized message for the control or null if no message
- * was found
- */
- public String getMessage(String name, Object... args) {
- String value = getMessage(name);
- if (value == null) {
- return null;
- }
- return MessageFormat.format(value, args);
- }
-
- /**
- * Return a Map of localized messages for the control. The messages returned
- * will use the Locale obtained from the Context.
- *
- * @return a Map of localized messages for the control
- * @throws IllegalStateException if the context for the control has not be set
- */
- public Map getMessages() {
- if (messages == null) {
- messages = getContext().createMessagesMap(getClass(), CONTROL_MESSAGES);
- }
- return messages;
- }
-
- /**
- * @see org.apache.click.Control#getParent()
- *
- * @return the Control's parent
- */
- public Object getParent() {
- return parent;
- }
-
- /**
- * @see org.apache.click.Control#setParent(Object)
- *
- * @param parent the parent of the Control
- * @throws IllegalArgumentException if the given parent instance is
- * referencing this object: if (parent == this)
- */
- public void setParent(Object parent) {
- if (parent == this) {
- throw new IllegalArgumentException("Cannot set parent to itself");
- }
- this.parent = parent;
- }
-
- /**
- * @see org.apache.click.Control#onProcess()
- *
- * @return true to continue Page event processing or false otherwise
- */
- public boolean onProcess() {
- dispatchActionEvent();
- return true;
- }
-
- /**
- * Set the controls event listener.
- *
- * The method signature of the listener is:
- *
must have a valid Java method name
- *
takes no arguments
- *
returns a boolean value
- *
- *
- * An example event listener method would be:
- *
- *
- *
- * @param listener the listener object with the named method to invoke
- * @param method the name of the method to invoke
- */
- public void setListener(Object listener, String method) {
- this.listener = listener;
- this.listenerMethod = method;
- }
-
- /**
- * This method does nothing. Subclasses may override this method to perform
- * initialization.
- *
- * @see org.apache.click.Control#onInit()
- */
- public void onInit() {
- }
-
- /**
- * This method does nothing. Subclasses may override this method to perform
- * clean up any resources.
- *
- * @see org.apache.click.Control#onDestroy()
- */
- public void onDestroy() {
- }
-
- /**
- * This method does nothing. Subclasses may override this method to deploy
- * static web resources.
- *
- * @see org.openidentityplatform.openam.click.Control#onDeploy(ServletContext)
- *
- * @param servletContext the servlet context
- */
- public void onDeploy(ServletContext servletContext) {
- }
-
- /**
- * This method does nothing. Subclasses may override this method to perform
- * pre rendering logic.
- *
- * @see org.apache.click.Control#onRender()
- */
- public void onRender() {
- }
-
- /**
- * @deprecated use the new {@link #getHeadElements()} instead
- *
- * @return the HTML includes statements for the control stylesheet and
- * JavaScript files
- */
- public final String getHtmlImports() {
- throw new UnsupportedOperationException("Use getHeadElements instead");
- }
-
- /**
- * @see org.apache.click.Control#getHeadElements()
- *
- * @return the list of HEAD elements to be included in the page
- */
- public List getHeadElements() {
- if (headElements == null) {
- // Most controls won't provide their own head elements, so save
- // memory by creating an empty array list
- headElements = new ArrayList(0);
- }
- return headElements;
- }
-
- /**
- * Return the parent page of this control, or null if not defined.
- *
- * @return the parent page of this control, or null if not defined
- */
- public Page getPage() {
- return ClickUtils.getParentPage(this);
- }
-
- /**
- * Return the control CSS style for the given name.
- *
- * @param name the CSS style name
- * @return the CSS style for the given name
- */
- public String getStyle(String name) {
- if (hasAttribute("style")) {
- String currentStyles = getAttribute("style");
- Map stylesMap = parseStyles(currentStyles);
- return stylesMap.get(name);
- } else {
- return null;
- }
- }
-
- /**
- * Set the control CSS style name and value pair.
- *
- * For example given the ActionLink:
- *
- *
- * To remove an existing style, set the value to null.
- *
- * @param name the CSS style name
- * @param value the CSS style value
- */
- public void setStyle(String name, String value) {
- if (name == null) {
- throw new IllegalArgumentException("Null name parameter");
- }
-
- String oldStyles = getAttribute("style");
-
- if (oldStyles == null) {
-
- if (value == null) {
- //Exit early
- return;
- } else {
- //If value is not null, append the new style and return
- getAttributes().put("style", name + ":" + value + ";");
- return;
- }
- }
-
- //Create buffer and estimate the new size
- HtmlStringBuffer buffer = new HtmlStringBuffer(
- oldStyles.length() + 10);
-
- //Parse the current styles into a map
- Map stylesMap = parseStyles(oldStyles);
-
- //Check if the new style is already present
- if (stylesMap.containsKey(name)) {
-
- //If the style is present and the value is null, remove the style,
- //otherwise replace it with the new value
- if (value == null) {
- stylesMap.remove(name);
- } else {
- stylesMap.put(name, value);
- }
- } else {
- stylesMap.put(name, value);
- }
-
- //The styles map might be empty if the last style was removed
- if (stylesMap.isEmpty()) {
- getAttributes().remove("style");
- return;
- }
-
- //Iterate over the stylesMap appending each entry to buffer
- for (Entry entry : stylesMap.entrySet()) {
- String styleName = entry.getKey();
- String styleValue = entry.getValue();
- buffer.append(styleName);
- buffer.append(":");
- buffer.append(styleValue);
- buffer.append(";");
- }
- getAttributes().put("style", buffer.toString());
- }
-
- /**
- * Return true if CSS styles are defined.
- *
- * @deprecated use {@link #hasAttribute(String)} instead
- *
- * @return true if CSS styles are defined
- */
- public boolean hasStyles() {
- return (styles != null && !styles.isEmpty());
- }
-
- /**
- * Return the Map of control CSS styles.
- *
- * @deprecated use {@link #getAttribute(String)} instead
- *
- * @return the Map of control CSS styles
- */
- public Map getStyles() {
- if (styles == null) {
- styles = new HashMap();
- }
- return styles;
- }
-
- /**
- * Add the CSS class attribute. Null values will be ignored.
- *
- * For example given the ActionLink:
- *
- *
- *
- * @param value the class attribute to add
- */
- public void addStyleClass(String value) {
- //If value is null, exit early
- if (value == null) {
- return;
- }
-
- //If any class attributes already exist, check if the specified class
- //exists in the current set of classes.
- if (hasAttribute("class")) {
- String oldStyleClasses = getAttribute("class").trim();
-
- //Check if the specified class exists in the class attribute set
- boolean classExists = classExists(value, oldStyleClasses);
-
- if (classExists) {
- //If the class already exist, exit early
- return;
- }
-
- //Specified class does not exist so add it with the other class
- //attributes
- getAttributes().put("class", oldStyleClasses + " " + value);
-
- } else {
- //Since no class attributes exist yet, only add the specified class
- setAttribute("class", value);
- }
- }
-
- /**
- * Removes the CSS class attribute.
- *
- * @param value the CSS class attribute
- */
- public void removeStyleClass(String value) {
- // If value is null, exit early
- if (value == null) {
- return;
- }
-
- // If any class attributes already exist, check if the specified class
- // exists in the current set of classes.
- if (hasAttribute("class")) {
- String oldStyleClasses = getAttribute("class").trim();
-
- //Check if the specified class exists in the class attribute set
- boolean classExists = classExists(value, oldStyleClasses);
-
- if (!classExists) {
- //If the class does not exist, exit early
- return;
- }
-
- // If the class does exist, parse the class attributes into a set
- // and remove the specified class
- Set styleClassSet = parseStyleClasses(oldStyleClasses);
- styleClassSet.remove(value);
-
- if (styleClassSet.isEmpty()) {
- // If there are no more styleClasses left, remove the class
- // attribute from the attributes list
- getAttributes().remove("class");
- } else {
- // Otherwise render the styleClasses.
- // Create buffer and estimate the new size
- HtmlStringBuffer buffer = new HtmlStringBuffer(
- oldStyleClasses.length() + value.length());
-
- // Iterate over the styleClassSet appending each entry to buffer
- Iterator it = styleClassSet.iterator();
- while (it.hasNext()) {
- String entry = it.next();
- buffer.append(entry);
- if (it.hasNext()) {
- buffer.append(" ");
- }
- }
- getAttributes().put("class", buffer.toString());
- }
- }
- }
-
- /**
- * Render the control's output to the specified buffer.
- *
- * If {@link #getTag()} returns null, this method will return an empty
- * string.
- *
- * @see org.apache.click.Control#render(org.apache.click.util.HtmlStringBuffer)
- *
- * @param buffer the specified buffer to render the control's output to
- */
- public void render(HtmlStringBuffer buffer) {
- if (getTag() == null) {
- return;
- }
- renderTagBegin(getTag(), buffer);
- renderTagEnd(getTag(), buffer);
- }
-
- /**
- * Returns the HTML representation of this control.
- *
- * This method delegates the rendering to the method
- * {@link #render(org.openidentityplatform.openam.click.util.HtmlStringBuffer)}. The size of buffer
- * is determined by {@link #getControlSizeEst()}.
- *
- * @see Object#toString()
- *
- * @return the HTML representation of this control
- */
- @Override
- public String toString() {
- if (getTag() == null) {
- return "";
- }
- HtmlStringBuffer buffer = new HtmlStringBuffer(getControlSizeEst());
- render(buffer);
- return buffer.toString();
- }
-
- // Protected Methods ------------------------------------------------------
-
- /**
- * Dispatch an action event to the {@link org.apache.click.ActionEventDispatcher}.
- *
- * @see org.apache.click.ActionEventDispatcher#dispatchActionEvent(org.apache.click.Control, org.apache.click.ActionListener)
- * @see org.apache.click.ActionEventDispatcher#dispatchAjaxBehaviors(org.apache.click.Control)
- */
- protected void dispatchActionEvent() {
- if (getActionListener() != null) {
- ActionEventDispatcher.dispatchActionEvent(this, getActionListener());
- }
-
- if (hasBehaviors()) {
- ActionEventDispatcher.dispatchAjaxBehaviors(this);
- }
- }
-
- /**
- * Append all the controls attributes to the specified buffer.
- *
- * @param buffer the specified buffer to append all the attributes
- */
- protected void appendAttributes(HtmlStringBuffer buffer) {
- if (hasAttributes()) {
- buffer.appendAttributes(attributes);
- }
- }
-
- /**
- * Render the tag and common attributes including {@link #getId() id},
- * class and style. The {@link #getName() name} attribute
- * is not rendered by this control. It is up to subclasses
- * whether to render the name attribute or not. Generally only
- * {@link org.apache.click.control.Field} controls render the name attribute.
- *
- * Please note: the tag will not be closed by this method. This
- * enables callers of this method to append extra attributes as needed.
- *
- * For example the following example:
- *
- *
- * Field field = new TextField("mytext");
- * HtmlStringBuffer buffer = new HtmlStringBuffer();
- * field.renderTagBegin("input", buffer);
- *
- * will render:
- *
- *
- * <input name="mytext" id="mytext"
- *
- * Note that the tag is not closed, so subclasses can render extra
- * attributes.
- *
- * @param tagName the name of the tag to render
- * @param buffer the buffer to append the output to
- */
- protected void renderTagBegin(String tagName,
- HtmlStringBuffer buffer) {
- if (tagName == null) {
- throw new IllegalStateException("Tag cannot be null");
- }
-
- buffer.elementStart(tagName);
-
- String id = getId();
- if (id != null) {
- buffer.appendAttribute("id", id);
- }
-
- appendAttributes(buffer);
- }
-
- /**
- * Closes the specified tag.
- *
- * @param tagName the name of the tag to close
- * @param buffer the buffer to append the output to
- */
- protected void renderTagEnd(String tagName, HtmlStringBuffer buffer) {
- buffer.elementEnd();
- }
-
- /**
- * Return the estimated rendered control size in characters.
- *
- * @return the estimated rendered control size in characters
- */
- protected int getControlSizeEst() {
- int size = 0;
- if (getTag() != null && hasAttributes()) {
- //length of the markup -> > == 3
- //1 * tag.length()
- size += 3 + getTag().length();
- //using 20 as an estimate
- size += 20 * getAttributes().size();
- }
- return size;
- }
-
- // Private Methods --------------------------------------------------------
-
- /**
- * Parse the specified string of style attributes and return a Map
- * of key/value pairs. Invalid key/value pairs will not be added to
- * the map.
- *
- * @param style the string containing the styles to parse
- * @return map containing key/value pairs of the specified style
- * @throws IllegalArgumentException if style is null
- */
- private Map parseStyles(String style) {
- if (style == null) {
- throw new IllegalArgumentException("style cannot be null");
- }
-
- //LinkHashMap is used to keep the order of the style names. Probably
- //makes no difference to browser but it makes testing easier since the
- //order that styles are added are kept when rendering the control.
- Map stylesMap = new LinkedHashMap();
- StringTokenizer tokens = new StringTokenizer(style, ";");
- while (tokens.hasMoreTokens()) {
- String token = tokens.nextToken();
- int keyEnd = token.indexOf(":");
-
- //If there is no key/value delimiter or value is empty, continue
- if (keyEnd == -1 || (keyEnd + 1) == token.length()) {
- continue;
- }
- //Check that the value part of the key/value pair not only
- //consists of a ';' char.
- if (token.charAt(keyEnd + 1) == ';') {
- continue;
- }
- String styleName = token.substring(0, keyEnd);
- String styleValue = token.substring(keyEnd + 1);
- stylesMap.put(styleName, styleValue);
- }
-
- return stylesMap;
- }
-
- /**
- * Parse the specified string of style attributes and return a Map
- * of key/value pairs. Invalid key/value pairs will not be added to
- * the map.
- *
- * @param styleClasses the string containing the styles to parse
- * @return map containing key/value pairs of the specified style
- * @throws IllegalArgumentException if styleClasses is null
- */
- private Set parseStyleClasses(String styleClasses) {
- if (styleClasses == null) {
- throw new IllegalArgumentException("styleClasses cannot be null");
- }
-
- //LinkHashMap is used to keep the order of the class names. Probably
- //makes no difference to browser but it makes testing easier since the
- //order that classes were added in, are kept when rendering the control.
- //Thus one can test whether the expected result and actual result match.
- Set styleClassesSet = new LinkedHashSet();
- StringTokenizer tokens = new StringTokenizer(styleClasses, " ");
- while (tokens.hasMoreTokens()) {
- String token = tokens.nextToken();
- styleClassesSet.add(token);
- }
-
- return styleClassesSet;
- }
-
- /**
- * Return true if the new value exists in the current value.
- *
- * @param newValue the value of the class attribute to check
- * @param currentValue the current value to test
- * @return true if the classToFind exists in the current value set
- */
- private boolean classExists(String newValue, String currentValue) {
- //To find if a class eg. "myclass" exists, check the following:
-
- //1. Check if "myclass" is the only value in the string
- // -> "myclass"
- if (newValue.length() == currentValue.length()
- && currentValue.indexOf(newValue) == 0) {
- return true;
- }
-
- //2. Check if "myclass" exists at beginning of string
- // -> "myclass otherclass"
- if (currentValue.startsWith(newValue + " ")) {
- return true;
- }
-
- //3. Check if "myclass" exists in middle of string
- // -> "anotherclass myclass otherclass"
- if (currentValue.indexOf(" " + newValue + " ") >= 0) {
- return true;
- }
-
- //4. Check if "myclass" exists at end of string
- // -> "anotherclass myclass"
- return (currentValue.endsWith(" " + newValue));
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractLink.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractLink.java
deleted file mode 100644
index e920367086..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/AbstractLink.java
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click.control;
-
-import org.openidentityplatform.openam.click.Context;
-import org.apache.click.Stateful;
-import org.apache.click.control.ActionLink;
-import org.apache.click.control.Submit;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-import org.apache.commons.lang.StringUtils;
-
-import jakarta.servlet.http.HttpServletRequest;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Provides a Abstract Link control: <a href=""></a>.
- *
- * See also the W3C HTML reference:
- * A Links
- *
- * @see ActionLink
- * @see Submit
- */
-public abstract class AbstractLink extends AbstractControl implements Stateful {
-
- private static final long serialVersionUID = 1L;
-
- // Instance Variables -----------------------------------------------------
-
- /** The Field disabled value. */
- protected boolean disabled;
-
- /**
- * The image src path attribute. If the image src is defined then a
- * <img/> element will rendered inside the anchor link when
- * using the AbstractLink {@link #toString()} method.
- *
- * If the image src value is prefixed with '/' then the request context path
- * will be prefixed to the src value when rendered by the control.
- */
- protected String imageSrc;
-
- /** The link display label. */
- protected String label;
-
- /** The link parameters map. */
- protected Map parameters;
-
- /** The link 'tabindex' attribute. */
- protected int tabindex;
-
- /** The link title attribute, which acts as a tooltip help message. */
- protected String title;
-
- /** Flag to set if both icon and text are rendered, default value is false. */
- protected boolean renderLabelAndImage = false;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * Create an AbstractLink for the given name.
- *
- * @param name the page link name
- * @throws IllegalArgumentException if the name is null
- */
- public AbstractLink(String name) {
- setName(name);
- }
-
- /**
- * Create an AbstractLink with no name defined.
- *
- * Please note the control's name must be defined before it is valid.
- */
- public AbstractLink() {
- }
-
- // Public Attributes ------------------------------------------------------
-
- /**
- * Return the link html tag: a.
- *
- * @see AbstractControl#getTag()
- *
- * @return this controls html tag
- */
- @Override
- public String getTag() {
- return "a";
- }
-
- /**
- * Return true if the AbstractLink is a disabled. If the link is disabled
- * it will be rendered as <span> element with a HTML class attribute
- * of "disabled".
- *
- * @return true if the AbstractLink is a disabled
- */
- public boolean isDisabled() {
- return disabled;
- }
-
- /**
- * Set the disabled flag. If the link is disabled it will be rendered as
- * <span> element with a HTML class attribute of "disabled".
- *
- * @param disabled the disabled flag
- */
- public void setDisabled(boolean disabled) {
- this.disabled = disabled;
- }
-
- /**
- * Return the AbstractLink anchor <a> tag href attribute.
- * This method will encode the URL with the session ID
- * if required using HttpServletResponse.encodeURL().
- *
- * @return the AbstractLink HTML href attribute
- */
- public abstract String getHref();
-
- /**
- * Return the image src path attribute. If the image src is defined then a
- * <img/> element will be rendered inside the anchor link
- * when using the AbstractLink {@link #toString()} method.
- *
- * Note: the label will not be rendered in this case (default behavior),
- * unless the {@link #setRenderLabelAndImage(boolean)} flag is set to true.
- *
- * If the src value is prefixed with '/' then the request context path will
- * be prefixed to the src value when rendered by the control.
- *
- * @return the image src path attribute
- */
- public String getImageSrc() {
- return imageSrc;
- }
-
- /**
- * Set the image src path attribute. If the src value is prefixed with
- * '/' then the request context path will be prefixed to the src value when
- * rendered by the control.
- *
- * If the image src is defined then an <img/> element will
- * be rendered inside the anchor link when using the AbstractLink
- * {@link #toString()} method.
- *
- * Note: the label will not be rendered in this case (default behavior),
- * unless the {@link #setRenderLabelAndImage(boolean)} flag is set to true.
- *
- * @param src the image src path attribute
- */
- public void setImageSrc(String src) {
- this.imageSrc = src;
- }
-
- /**
- * Return the "id" attribute value if defined, or null otherwise.
- *
- * @see org.apache.click.Control#getId()
- *
- * @return HTML element identifier attribute "id" value
- */
- @Override
- public String getId() {
- if (hasAttributes()) {
- return getAttribute("id");
- } else {
- return null;
- }
- }
-
- /**
- * Return the label for the AbstractLink.
- *
- * If the label value is null, this method will attempt to find a
- * localized label message in the parent messages using the key:
- *
- * getName() + ".label"
- *
- * If not found then the message will be looked up in the
- * /click-control.properties file using the same key.
- * If a value still cannot be found then the ActionLink name will be converted
- * into a label using the method: {@link ClickUtils#toLabel(String)}
- *
- * For example given a OrderPage with the properties file
- * OrderPage.properties:
- *
- *
- * checkout.label=Checkout
- * checkout.title=Proceed to Checkout
- *
- * The page ActionLink code:
- *
- * public class OrderPage extends Page {
- * ActionLink checkoutLink = new ActionLink("checkout");
- * ..
- * }
- *
- * Will render the AbstractLink label and title properties as:
- *
- * <a href=".." title="Proceed to Checkout">Checkout</a>
- *
- * When a label value is not set, or defined in any properties files, then
- * its value will be created from the Fields name.
- *
- * For example given the ActionLink code:
- *
- *
- * ActionLink nameField = new ActionLink("deleteItem");
- *
- * Will render the ActionLink label as:
- *
- * <a href="..">Delete Item</a>
- *
- * Note the ActionLink label can include raw HTML to render other elements.
- *
- * For example the configured label:
- *
- *
- *
- * @return the label for the ActionLink
- */
- public String getLabel() {
- if (label == null) {
- label = getMessage(getName() + ".label");
- }
- if (label == null) {
- label = ClickUtils.toLabel(getName());
- }
- return label;
- }
-
- /**
- * Set the label for the ActionLink.
- *
- * @see #getLabel()
- *
- * @param label the label for the ActionLink
- */
- public void setLabel(String label) {
- this.label = label;
- }
-
- /**
- * Return the link request parameter value for the given name, or null if
- * the parameter value does not exist.
- *
- * @param name the name of request parameter
- * @return the link request parameter value
- */
- public String getParameter(String name) {
- if (hasParameters()) {
- Object value = getParameters().get(name);
-
- if (value instanceof String) {
- return (String) value;
- }
-
- if (value instanceof String[]) {
- String[] array = (String[]) value;
- if (array.length >= 1) {
- return array[0];
- } else {
- return null;
- }
- }
-
- return (value == null ? null : value.toString());
- } else {
- return null;
- }
- }
-
- /**
- * Set the link parameter with the given parameter name and value. You would
- * generally use parameter if you were creating the entire AbstractLink
- * programmatically and rendering it with the {@link #toString()} method.
- *
- * For example given the ActionLink:
- *
- *
- *
- * @param name the attribute name
- * @param value the attribute value
- * @throws IllegalArgumentException if name parameter is null
- */
- public void setParameter(String name, Object value) {
- if (name == null) {
- throw new IllegalArgumentException("Null name parameter");
- }
-
- if (value != null) {
- getParameters().put(name, value);
- } else {
- getParameters().remove(name);
- }
- }
-
- /**
- * Return the link request parameter values for the given name, or null if
- * the parameter values does not exist.
- *
- * @param name the name of request parameter
- * @return the link request parameter values
- */
- public String[] getParameterValues(String name) {
- if (hasParameters()) {
- Object values = getParameters().get(name);
- if (values instanceof String) {
- return new String[] { values.toString() };
- }
- if (values instanceof String[]) {
- return (String[]) values;
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
-
- /**
- * Set the link parameter with the given parameter name and values. If the
- * values are null, the parameter will be removed from the {@link #parameters}.
- *
- * @see #setParameter(String, Object)
- *
- * @param name the attribute name
- * @param values the attribute values
- * @throws IllegalArgumentException if name parameter is null
- */
- public void setParameterValues(String name, Object[] values) {
- if (name == null) {
- throw new IllegalArgumentException("Null name parameter");
- }
-
- if (values != null) {
- getParameters().put(name, values);
- } else {
- getParameters().remove(name);
- }
- }
-
- /**
- * Return the AbstractLink parameters Map.
- *
- * @return the AbstractLink parameters Map
- */
- public Map getParameters() {
- if (parameters == null) {
- parameters = new HashMap(4);
- }
- return parameters;
- }
-
- /**
- * Set the AbstractLink parameter map.
- *
- * @param parameters the link parameter map
- */
- public void setParameters(Map parameters) {
- this.parameters = parameters;
- }
-
- /**
- * Defines a link parameter that will have its value bound to a matching
- * request parameter. {@link #setParameter(String, Object) setParameter}
- * implicitly defines a parameter as well.
- *
- * Please note: parameters need only be defined for Ajax requests.
- * For non-Ajax requests, all incoming request parameters
- * are bound, whether they are defined or not. This behavior may change in a
- * future release.
- *
- * Also note: link parameters are bound to request parameters
- * during the {@link #onProcess()} event, so link parameters must be defined
- * in the Page constructor or onInit() event.
- *
- * @param name the name of the parameter to define
- */
- public void defineParameter(String name) {
- if (name == null) {
- throw new IllegalArgumentException("Null name parameter");
- }
-
- Map localParameters = getParameters();
- if (!localParameters.containsKey(name)) {
- localParameters.put(name, null);
- }
- }
-
- /**
- * Return true if the AbstractLink has parameters, false otherwise.
- *
- * @return true if the AbstractLink has parameters, false otherwise
- */
- public boolean hasParameters() {
- return parameters != null && !parameters.isEmpty();
- }
-
- /**
- * Return the link "tabindex" attribute value.
- *
- * @return the link "tabindex" attribute value
- */
- public int getTabIndex() {
- return tabindex;
- }
-
- /**
- * Set the link "tabindex" attribute value.
- *
- * @param tabindex the link "tabindex" attribute value
- */
- public void setTabIndex(int tabindex) {
- this.tabindex = tabindex;
- }
-
- /**
- * Return the 'title' attribute, or null if not defined. The title
- * attribute acts like tooltip message over the link.
- *
- * If the title value is null, this method will attempt to find a
- * localized label message in the parent messages using the key:
- *
- * getName() + ".title"
- *
- * If not found then the message will be looked up in the
- * /click-control.properties file using the same key.
- *
- * For examle given a ItemsPage with the properties file
- * ItemPage.properties:
- *
- *
- * edit.label=Edit
- * edit.title=Edit Item
- *
- * The page ActionLink code:
- *
- * public class ItemsPage extends Page {
- * ActionLink editLink = new ActionLink("edit");
- * ..
- * }
- *
- * Will render the ActionLink label and title properties as:
- *
- * <a href=".." title="Edit Item">Edit</a>
- *
- * @return the 'title' attribute tooltip message
- */
- public String getTitle() {
- if (title == null) {
- title = getMessage(getName() + ".title");
- }
- return title;
- }
-
- /**
- * Set the 'title' attribute tooltip message.
- *
- * @see #getTitle()
- *
- * @param value the 'title' attribute tooltip message
- */
- public void setTitle(String value) {
- title = value;
- }
-
- /**
- * Returns true if both {@link #setImageSrc(String) icon}
- * and {@link #setLabel(String) label} are rendered,
- * false otherwise.
- *
- * @return true if both icon and text are rendered,
- * false otherwise
- */
- public boolean isRenderLabelAndImage() {
- return renderLabelAndImage;
- }
-
- /**
- * Sets whether both {@link #setLabel(String) label} and
- * {@link #setImageSrc(String) icon} are rendered for this
- * link.
- *
- * @param renderLabelAndImage sets the rendering type of the link.
- */
- public void setRenderLabelAndImage(boolean renderLabelAndImage) {
- this.renderLabelAndImage = renderLabelAndImage;
- }
-
- @Override
- public boolean isAjaxTarget(Context context) {
- String id = getId();
- if (id != null) {
- return context.getRequestParameter(id) != null;
- } else {
- String localName = getName();
- if (localName != null) {
- return localName.equals(context.getRequestParameter(ActionLink.ACTION_LINK));
- }
- }
- return false;
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * This method does nothing by default since AbstractLink does not bind to
- * request values.
- */
- public void bindRequestValue() {
- }
-
- /**
- * Return the link state. The following state is returned:
- *
- *
{@link #getParameters() link parameters}
- *
- *
- * @return the link state
- */
- public Object getState() {
- if (hasParameters()) {
- return getParameters();
- }
- return null;
- }
-
- /**
- * Set the link state.
- *
- * @param state the link state to set
- */
- public void setState(Object state) {
- if (state == null) {
- return;
- }
-
- Map linkState = (Map) state;
- setParameters(linkState);
- }
-
- /**
- * Render the HTML representation of the anchor link. This method
- * will render the entire anchor link including the tags, the label and
- * any attributes, see {@link #setAttribute(String, String)} for an
- * example.
- *
- * If the image src is defined then a <img/> element will
- * rendered inside the anchor link instead of the label property.
- *
- * This method invokes the abstract {@link #getHref()} method.
- *
- * @see #toString()
- *
- * @param buffer the specified buffer to render the control's output to
- */
- @Override
- public void render(HtmlStringBuffer buffer) {
-
- if (isDisabled()) {
-
- buffer.elementStart("span");
- buffer.appendAttribute("id", getId());
- addStyleClass("disabled");
- buffer.appendAttribute("class", getAttribute("class"));
-
- if (hasAttribute("style")) {
- buffer.appendAttribute("style", getAttribute("style"));
- }
-
- buffer.closeTag();
-
- if (StringUtils.isBlank(getImageSrc())) {
- buffer.append(getLabel());
-
- } else {
- renderImgTag(buffer);
- if (isRenderLabelAndImage()) {
- buffer.elementStart("span").closeTag();
- buffer.append(getLabel());
- buffer.elementEnd("span");
- }
- }
-
- buffer.elementEnd("span");
-
- } else {
- buffer.elementStart(getTag());
- removeStyleClass("disabled");
- buffer.appendAttribute("href", getHref());
- buffer.appendAttribute("id", getId());
- buffer.appendAttributeEscaped("title", getTitle());
- if (getTabIndex() > 0) {
- buffer.appendAttribute("tabindex", getTabIndex());
- }
-
- appendAttributes(buffer);
-
- buffer.closeTag();
-
- if (StringUtils.isBlank(getImageSrc())) {
- buffer.append(getLabel());
-
- } else {
- renderImgTag(buffer);
- if (isRenderLabelAndImage()) {
- buffer.elementStart("span").closeTag();
- buffer.append(getLabel());
- buffer.elementEnd("span");
- }
- }
-
- buffer.elementEnd(getTag());
- }
- }
-
- /**
- * Remove the link state from the session for the given request context.
- *
- * @see #saveState(Context)
- * @see #restoreState(Context)
- *
- * @param context the request context
- */
- public void removeState(Context context) {
- ClickUtils.removeState(this, getName(), context);
- }
-
- /**
- * Restore the link state from the session for the given request context.
- *
- * This method delegates to {@link #setState(Object)} to set the
- * link restored state.
- *
- * @see #saveState(Context)
- * @see #removeState(Context)
- *
- * @param context the request context
- */
- public void restoreState(Context context) {
- ClickUtils.restoreState(this, getName(), context);
- }
-
- /**
- * Save the link state to the session for the given request context.
- *
- * * This method delegates to {@link #getState()} to retrieve the link state
- * to save.
- *
- * @see #restoreState(Context)
- * @see #removeState(Context)
- *
- * @param context the request context
- */
- public void saveState(Context context) {
- ClickUtils.saveState(this, getName(), context);
- }
-
- // Protected Methods ------------------------------------------------------
-
- /**
- * Render the Image tag to the buffer.
- *
- * @param buffer the buffer to render the image tag to
- */
- protected void renderImgTag(HtmlStringBuffer buffer) {
- buffer.elementStart("img");
- buffer.appendAttribute("border", 0);
- buffer.appendAttribute("hspace", 2);
- buffer.appendAttribute("class", "link");
-
- if (getTitle() != null) {
- buffer.appendAttributeEscaped("alt", getTitle());
- } else {
- buffer.appendAttributeEscaped("alt", getLabel());
- }
-
- String src = getImageSrc();
- if (StringUtils.isNotBlank(src)) {
- if (src.charAt(0) == '/') {
- src = getContext().getRequest().getContextPath() + src;
- }
- buffer.appendAttribute("src", src);
- }
-
- buffer.elementEnd();
- }
-
- /**
- * Render the given link parameters to the buffer.
- *
- * The parameters will be rendered as URL key/value pairs e.g:
- * "firstname=john&lastname=smith".
- *
- * Multivalued parameters will be rendered with each value sharing the same
- * key e.g: "name=john&name=susan&name=mary".
- *
- * The parameter value will be encoded through
- * {@link ClickUtils#encodeUrl(Object, Context)}.
- *
- * @param buffer the buffer to render the parameters to
- * @param parameters the parameters to render
- * @param context the request context
- */
- protected void renderParameters(HtmlStringBuffer buffer, Map parameters,
- Context context) {
-
- Iterator i = parameters.keySet().iterator();
- while (i.hasNext()) {
- String paramName = i.next();
- Object paramValue = getParameters().get(paramName);
-
- // Check for multivalued parameter
- if (paramValue instanceof String[]) {
- String[] paramValues = (String[]) paramValue;
- for (int j = 0; j < paramValues.length; j++) {
- buffer.append(paramName);
- buffer.append("=");
- buffer.append(ClickUtils.encodeUrl(paramValues[j], context));
- if (j < paramValues.length - 1) {
- buffer.append("&");
- }
- }
- } else {
- if (paramValue != null) {
- buffer.append(paramName);
- buffer.append("=");
- buffer.append(ClickUtils.encodeUrl(paramValue, context));
- }
- }
- if (i.hasNext()) {
- buffer.append("&");
- }
- }
- }
-
- /**
- * This method binds the submitted request parameters to the link
- * parameters.
- *
- * For non-Ajax requests this method will bind all incoming request
- * parameters to the link. For Ajax requests this method will only bind
- * the parameters already defined on the link.
- *
- * @param context the request context
- */
- @SuppressWarnings("unchecked")
- protected void bindRequestParameters(Context context) {
- HttpServletRequest request = context.getRequest();
-
- Set parameterNames = null;
-
- if (context.isAjaxRequest()) {
- parameterNames = getParameters().keySet();
- } else {
- parameterNames = request.getParameterMap().keySet();
- }
-
- for (String param : parameterNames) {
- String[] values = request.getParameterValues(param);
- // Do not process request parameters that return null values. Null
- // values are only returned if the request parameter is not present.
- // A null value can only occur for Ajax requests which processes
- // parameters defined on the link, not the incoming parameters.
- // The reason for not processing the null value is because it would
- // nullify parametesr that was set during onInit
- if (values == null) {
- continue;
- }
-
- if (values.length == 1) {
- getParameters().put(param, values[0]);
- } else {
- getParameters().put(param, values);
- }
- }
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/ActionLink.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/control/ActionLink.java
deleted file mode 100644
index c90f5f0423..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/ActionLink.java
+++ /dev/null
@@ -1,517 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click.control;
-
-import org.openidentityplatform.openam.click.Context;
-import org.apache.click.control.Submit;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-import org.apache.commons.lang.StringUtils;
-
-/**
- * Provides a Action Link control: <a href=""></a>.
- *
- *
- *
- * This control can render the "href" URL attribute using
- * {@link #getHref()}, or the entire ActionLink anchor tag using
- * {@link #toString()}.
- *
- * ActionLink support invoking control listeners.
- *
- *
ActionLink Example
- *
- * An example of using ActionLink to call a logout method is illustrated below:
- *
- *
- * public class MyPage extends Page {
- *
- * public MyPage() {
- * ActionLink link = new ActionLink("logoutLink");
- * link.setListener(this, "onLogoutClick");
- * addControl(link);
- * }
- *
- * public boolean onLogoutClick() {
- * if (getContext().hasSession()) {
- * getContext().getSession().invalidate();
- * }
- * setRedirect(LogoutPage.class);
- *
- * return false;
- * }
- * }
- *
- * The corresponding template code is below. Note href is evaluated by Velocity
- * to {@link #getHref()}:
- *
- *
- * <a href="$logoutLink.href" title="Click to Logout">Logout</a>
- *
- * ActionLink can also support a value parameter which is accessible
- * using {@link #getValue()}.
- *
- * For example a products table could include rows
- * of products, each with a get product details ActionLink and add product
- * ActionLink. The ActionLinks include the product's id as a parameter to
- * the {@link #getHref(Object)} method, which is then available when the
- * control is processed:
- *
- *
- *
- * The corresponding Page class for this template is:
- *
- *
- * public class ProductsPage extends Page {
- *
- * public ActionLink addLink = new ActionLink("addLink", this, "onAddClick");
- * public ActionLink detailsLink = new ActionLink("detailsLink", this, "onDetailsClick");
- * public List productList;
- *
- * public boolean onAddClick() {
- * // Get the product clicked on by the user
- * Integer productId = addLink.getValueInteger();
- * Product product = getProductService().getProduct(productId);
- *
- * // Add product to basket
- * List basket = (List) getContext().getSessionAttribute("basket");
- * basket.add(product);
- * getContext().setSessionAttribute("basket", basket);
- *
- * return true;
- * }
- *
- * public boolean onDetailsClick() {
- * // Get the product clicked on by the user
- * Integer productId = detailsLink.getValueInteger();
- * Product product = getProductService().getProduct(productId);
- *
- * // Store the product in the request and display in the details page
- * getContext().setRequestAttribute("product", product);
- * setForward(ProductDetailsPage.class);
- *
- * return false;
- * }
- *
- * public void onRender() {
- * // Display the list of available products
- * productList = getProductService().getProducts();
- * }
- * }
- *
- * See also the W3C HTML reference:
- * A Links
- *
- * @see org.apache.click.control.AbstractLink
- * @see Submit
- */
-public class ActionLink extends AbstractLink {
-
- // Constants --------------------------------------------------------------
-
- private static final long serialVersionUID = 1L;
-
- /** The action link parameter name: actionLink. */
- public static final String ACTION_LINK = "actionLink";
-
- /** The value parameter name: value. */
- public static final String VALUE = "value";
-
- // Instance Variables -----------------------------------------------------
-
- /** The link is clicked. */
- protected boolean clicked;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * Create an ActionLink for the given name.
- *
- * Please note the name 'actionLink' is reserved as a control request
- * parameter name and cannot be used as the name of the control.
- *
- * @param name the action link name
- * @throws IllegalArgumentException if the name is null
- */
- public ActionLink(String name) {
- setName(name);
- }
-
- /**
- * Create an ActionLink for the given name and label.
- *
- * Please note the name 'actionLink' is reserved as a control request
- * parameter name and cannot be used as the name of the control.
- *
- * @param name the action link name
- * @param label the action link label
- * @throws IllegalArgumentException if the name is null
- */
- public ActionLink(String name, String label) {
- setName(name);
- setLabel(label);
- }
-
- /**
- * Create an ActionLink for the given listener object and listener
- * method.
- *
- * @param listener the listener target object
- * @param method the listener method to call
- * @throws IllegalArgumentException if the name, listener or method is null
- * or if the method is blank
- */
- public ActionLink(Object listener, String method) {
- if (listener == null) {
- throw new IllegalArgumentException("Null listener parameter");
- }
- if (StringUtils.isBlank(method)) {
- throw new IllegalArgumentException("Blank listener method");
- }
- setListener(listener, method);
- }
-
- /**
- * Create an ActionLink for the given name, listener object and listener
- * method.
- *
- * Please note the name 'actionLink' is reserved as a control request
- * parameter name and cannot be used as the name of the control.
- *
- * @param name the action link name
- * @param listener the listener target object
- * @param method the listener method to call
- * @throws IllegalArgumentException if the name, listener or method is null
- * or if the method is blank
- */
- public ActionLink(String name, Object listener, String method) {
- setName(name);
- if (listener == null) {
- throw new IllegalArgumentException("Null listener parameter");
- }
- if (StringUtils.isBlank(method)) {
- throw new IllegalArgumentException("Blank listener method");
- }
- setListener(listener, method);
- }
-
- /**
- * Create an ActionLink for the given name, label, listener object and
- * listener method.
- *
- * Please note the name 'actionLink' is reserved as a control request
- * parameter name and cannot be used as the name of the control.
- *
- * @param name the action link name
- * @param label the action link label
- * @param listener the listener target object
- * @param method the listener method to call
- * @throws IllegalArgumentException if the name, listener or method is null
- * or if the method is blank
- */
- public ActionLink(String name, String label, Object listener,
- String method) {
-
- setName(name);
- setLabel(label);
- if (listener == null) {
- throw new IllegalArgumentException("Null listener parameter");
- }
- if (StringUtils.isBlank(method)) {
- throw new IllegalArgumentException("Blank listener method");
- }
- setListener(listener, method);
- }
-
- /**
- * Create an ActionLink with no name defined. Please note the
- * control's name must be defined before it is valid.
- */
- public ActionLink() {
- }
-
- // Public Attributes ------------------------------------------------------
-
- /**
- * Returns true if the ActionLink was clicked, otherwise returns false.
- *
- * @return true if the ActionLink was clicked, otherwise returns false.
- */
- public boolean isClicked() {
- return clicked;
- }
-
- /**
- * Return the ActionLink anchor <a> tag href attribute for the
- * given value. This method will encode the URL with the session ID
- * if required using HttpServletResponse.encodeURL().
- *
- * @param value the ActionLink value parameter
- * @return the ActionLink HTML href attribute
- */
- public String getHref(Object value) {
- Context context = getContext();
- String uri = ClickUtils.getRequestURI(context.getRequest());
-
- HtmlStringBuffer buffer =
- new HtmlStringBuffer(uri.length() + getName().length() + 40);
-
- buffer.append(uri);
- buffer.append("?");
- buffer.append(ACTION_LINK);
- buffer.append("=");
- buffer.append(getName());
- if (value != null) {
- buffer.append("&");
- buffer.append(VALUE);
- buffer.append("=");
- buffer.append(ClickUtils.encodeUrl(value, context));
- }
-
- if (hasParameters()) {
- for (String paramName : getParameters().keySet()) {
- if (!paramName.equals(ACTION_LINK) && !paramName.equals(VALUE)) {
- Object paramValue = getParameters().get(paramName);
-
- // Check for multivalued parameter
- if (paramValue instanceof String[]) {
- String[] paramValues = (String[]) paramValue;
- for (int j = 0; j < paramValues.length; j++) {
- buffer.append("&");
- buffer.append(paramName);
- buffer.append("=");
- buffer.append(ClickUtils.encodeUrl(paramValues[j],
- context));
- }
- } else {
- if (paramValue != null) {
- buffer.append("&");
- buffer.append(paramName);
- buffer.append("=");
- buffer.append(ClickUtils.encodeUrl(paramValue,
- context));
- }
- }
- }
- }
- }
-
- return context.getResponse().encodeURL(buffer.toString());
- }
-
- /**
- * Return the ActionLink anchor <a> tag href attribute value.
- *
- * @return the ActionLink anchor <a> tag HTML href attribute value
- */
- @Override
- public String getHref() {
- return getHref(getValue());
- }
-
- /**
- * Set the name of the Control. Each control name must be unique in the
- * containing Page model or the containing Form.
- *
- * Please note the name 'actionLink' is reserved as a control request
- * parameter name and cannot be used as the name of the control.
- *
- * @see org.apache.click.Control#setName(String)
- *
- * @param name of the control
- * @throws IllegalArgumentException if the name is null
- */
- @Override
- public void setName(String name) {
- if (ACTION_LINK.equals(name)) {
- String msg = "Invalid name '" + ACTION_LINK + "'. This name is "
- + "reserved for use as a control request parameter name";
- throw new IllegalArgumentException(msg);
- }
- super.setName(name);
- }
-
- /**
- * Set the parent of the ActionLink.
- *
- * @see org.apache.click.Control#setParent(Object)
- *
- * @param parent the parent of the Control
- * @throws IllegalStateException if {@link #name} is not defined
- * @throws IllegalArgumentException if the given parent instance is
- * referencing this object: if (parent == this)
- */
- @Override
- public void setParent(Object parent) {
- if (parent == this) {
- throw new IllegalArgumentException("Cannot set parent to itself");
- }
- if (getName() == null) {
- String msg = "ActionLink name not defined.";
- throw new IllegalArgumentException(msg);
- }
- this.parent = parent;
- }
-
- /**
- * Returns the ActionLink value if the action link was processed and has
- * a value, or null otherwise.
- *
- * @return the ActionLink value if the ActionLink was processed
- */
- public String getValue() {
- return getParameter(VALUE);
- }
-
- /**
- * Returns the action link Double value if the action link was
- * processed and has a value, or null otherwise.
- *
- * @return the action link Double value if the action link was processed
- *
- * @throws NumberFormatException if the value cannot be parsed into a Double
- */
- public Double getValueDouble() {
- String value = getValue();
- if (value != null) {
- return Double.valueOf(value);
- } else {
- return null;
- }
- }
-
- /**
- * Returns the ActionLink Integer value if the ActionLink was
- * processed and has a value, or null otherwise.
- *
- * @return the ActionLink Integer value if the action link was processed
- *
- * @throws NumberFormatException if the value cannot be parsed into an Integer
- */
- public Integer getValueInteger() {
- String value = getValue();
- if (value != null) {
- return Integer.valueOf(value);
- } else {
- return null;
- }
- }
-
- /**
- * Returns the ActionLink Long value if the ActionLink was
- * processed and has a value, or null otherwise.
- *
- * @return the ActionLink Long value if the action link was processed
- *
- * @throws NumberFormatException if the value cannot be parsed into a Long
- */
- public Long getValueLong() {
- String value = getValue();
- if (value != null) {
- return Long.valueOf(value);
- } else {
- return null;
- }
- }
-
- /**
- * Set the ActionLink value.
- *
- * @param value the ActionLink value
- */
- public void setValue(String value) {
- setParameter(VALUE, value);
- }
-
- /**
- * Set the value of the ActionLink using the given object.
- *
- * @param object the object value to set
- */
- public void setValueObject(Object object) {
- if (object != null) {
- setValue(object.toString());
- } else {
- setValue(null);
- }
- }
-
- /**
- * This method binds the submitted request value to the ActionLink's
- * value.
- */
- @Override
- public void bindRequestValue() {
- Context context = getContext();
- if (context.isMultipartRequest()) {
- return;
- }
-
- clicked = getName().equals(context.getRequestParameter(ACTION_LINK));
-
- if (clicked) {
- String value = context.getRequestParameter(VALUE);
- if (value != null) {
- setValue(value);
- }
- bindRequestParameters(context);
- }
- }
-
- // Public Methods ---------------------------------------------------------
-
- /**
- * This method will set the {@link #isClicked()} property to true if the
- * ActionLink was clicked, and if an action callback listener was set
- * this will be invoked.
- *
- * @see org.apache.click.Control#onProcess()
- *
- * @return true to continue Page event processing or false otherwise
- */
- @Override
- public boolean onProcess() {
- bindRequestValue();
-
- if (isClicked()) {
- dispatchActionEvent();
- }
- return true;
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/Button.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/control/Button.java
deleted file mode 100644
index bdf74b6e4b..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/Button.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click.control;
-
-import org.openidentityplatform.openam.click.Context;
-import org.apache.click.control.Reset;
-import org.apache.click.control.Submit;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-
-/**
- * Provides a Button control: <input type='button'/>.
- *
- *
- *
- *
- *
- *
- *
- * The Button control is used to render a JavaScript enabled button which can
- * perform client side logic. The Button control provides no server side
- * processing. If server side processing is required use {@link Submit} instead.
- *
- *
Button Example
- *
- * The example below adds a back button to a form, which when clicked returns
- * to the previous page.
- *
- *
- * Button backButton = new Button("back", " < Back ");
- * backButton.setOnClick("history.back();");
- * backButton.setTitle("Return to previous page");
- * form.add(backButton);
- *
- * HTML output:
- *
- * <input type='button' name='back' value=' < Back ' onclick='history.back();'
- * title='Return to previous page'/>
- *
- * See also W3C HTML reference
- * INPUT
- *
- * @see Reset
- * @see Submit
- */
-public class Button extends Field {
-
- // Constants --------------------------------------------------------------
-
- private static final long serialVersionUID = 1L;
-
- // Constructors -----------------------------------------------------------
-
- /**
- * Create a button with the given name.
- *
- * @param name the button name
- */
- public Button(String name) {
- super(name);
- }
-
- /**
- * Create a button with the given name and label. The button label is
- * rendered as the HTML "value" attribute.
- *
- * @param name the button name
- * @param label the button label
- */
- public Button(String name, String label) {
- setName(name);
- setLabel(label);
- }
-
- /**
- * Create a button with no name defined.
- *
- * Please note the control's name must be defined before it is valid.
- */
- public Button() {
- }
-
- // Public Attributes ------------------------------------------------------
-
- /**
- * Return the button's html tag: input.
- *
- * @see org.apache.click.control.AbstractControl#getTag()
- *
- * @return this controls html tag
- */
- @Override
- public String getTag() {
- return "input";
- }
-
- /**
- * Returns the button onclick attribute value, or null if not defined.
- *
- * @return the button onclick attribute value, or null if not defined.
- */
- public String getOnClick() {
- if (attributes != null) {
- return attributes.get("onclick");
- } else {
- return null;
- }
- }
-
- /**
- * Sets the button onclick attribute value.
- *
- * @param value the onclick attribute value.
- */
- public void setOnClick(String value) {
- setAttribute("onclick", value);
- }
-
- /**
- * Return the input type: 'button'.
- *
- * @return the input type: 'button'
- */
- public String getType() {
- return "button";
- }
-
- // Public Methods --------------------------------------------------------
-
- /**
- * For non Ajax requests this method returns true, as buttons by default
- * perform no server side logic. If the button is an Ajax target and a
- * behavior is defined, the behavior action will be invoked.
- *
- * @see Field#onProcess()
- *
- * @return true to continue Page event processing or false otherwise
- */
- @Override
- public boolean onProcess() {
- Context context = getContext();
-
- if (context.isAjaxRequest()) {
-
- if (isDisabled()) {
- // Switch off disabled property if control has incoming request
- // parameter. Normally this means the field was enabled via JS
- if (context.hasRequestParameter(getName())) {
- setDisabled(false);
- } else {
- // If field is disabled skip process event
- return true;
- }
- }
-
- if (context.hasRequestParameter(getName())) {
- dispatchActionEvent();
- }
- }
-
- return true;
- }
-
- /**
- * @see AbstractControl#getControlSizeEst()
- *
- * @return the estimated rendered control size in characters
- */
- @Override
- public int getControlSizeEst() {
- return 40;
- }
-
- /**
- * Render the HTML representation of the Button. Note the button label is
- * rendered as the HTML "value" attribute.
- *
- * @see #toString()
- *
- * @param buffer the specified buffer to render the control's output to
- */
- @Override
- public void render(HtmlStringBuffer buffer) {
- buffer.elementStart(getTag());
-
- buffer.appendAttribute("type", getType());
- buffer.appendAttribute("name", getName());
- buffer.appendAttribute("id", getId());
- buffer.appendAttribute("value", getLabel());
- buffer.appendAttribute("title", getTitle());
- if (getTabIndex() > 0) {
- buffer.appendAttribute("tabindex", getTabIndex());
- }
-
- appendAttributes(buffer);
-
- if (isDisabled()) {
- buffer.appendAttributeDisabled();
- }
-
- buffer.elementEnd();
- }
-}
diff --git a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/Column.java b/openam-core/src/main/java/org/openidentityplatform/openam/click/control/Column.java
deleted file mode 100644
index a9adbcf431..0000000000
--- a/openam-core/src/main/java/org/openidentityplatform/openam/click/control/Column.java
+++ /dev/null
@@ -1,1596 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.openidentityplatform.openam.click.control;
-
-import org.openidentityplatform.openam.click.Context;
-import org.openidentityplatform.openam.click.util.ClickUtils;
-import org.openidentityplatform.openam.click.util.HtmlStringBuffer;
-import org.apache.click.util.PropertyUtils;
-import org.apache.commons.lang.math.NumberUtils;
-
-import java.io.Serializable;
-import java.text.MessageFormat;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-/**
- * Provides the Column table data <td> and table header <th>
- * renderer.
- *
- *
- *
- *
- *
- * The Column object provide column definitions for the {@link org.apache.click.control.Table} object.
- *
- *
Column Options
- *
- * The Column class supports a number of rendering options which include:
- *
- *
- *
{@link #autolink} - the option to automatically render href links
- * for email and URL column values
- *
{@link #attributes} - the CSS style attributes for the table data cell
- *
{@link #dataClass} - the CSS class for the table data cell
- *
{@link #dataStyles} - the CSS styles for the table data cell
- *
{@link #decorator} - the custom column value renderer
- *
{@link #format} - the MessageFormat pattern rendering
- * the column value
- *
{@link #headerClass} - the CSS class for the table header cell
- *
{@link #headerStyles} - the CSS styles for the table header cell
- *
{@link #headerTitle} - the table header cell value to render
- *
{@link #sortable} - the table column sortable property
- *
{@link #width} - the table cell width property
- *
- *
- *
Format Pattern
- *
- * The {@link #format} property which specifies {@link MessageFormat} pattern
- * a is very useful for formatting column values. For example to render
- * formatted number and date values you simply specify:
- *
- *
- *
- * The support custom column value rendering you can specify a {@link Decorator}
- * class on columns. The decorator render method is passed the table
- * row object and the page request Context. Using the table row you can access
- * all the column values enabling you to render a compound value composed of
- * multiple column values. For example:
- *
- *
-
- * The Context parameter of the decorator render() method enables you to
- * render controls to provide additional functionality. For example:
- *
- *
- *
- * Column header titles can be localized using the controls parent messages.
- * If the column header title value is null, the column will attempt to find a
- * localized message in the parent messages using the key:
- *
- * getName() + ".headerTitle"
- *
- * If not found then the message will be looked up in the
- * /click-control.properties file using the same key.
- * If a value still cannot be found then the Column name will be converted
- * into a header title using the method: {@link ClickUtils#toLabel(String)}.
- *
- *
- * @see Decorator
- * @see org.apache.click.control.Table
- */
-public class Column implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- // ----------------------------------------------------- Instance Variables
-
- /** The Column attributes Map. */
- protected Map attributes;
-
- /**
- * The automatically hyperlink column URL and email address values flag,
- * default value is false.
- */
- protected boolean autolink;
-
- /** The column table data <td> CSS class attribute. */
- protected String dataClass;
-
- /** The Map of column table data <td> CSS style attributes. */
- protected Map dataStyles;
-
- /** The column row decorator. */
- protected Decorator decorator;
-
- /** The escape HTML characters flag. The default value is true. */
- protected boolean escapeHtml = true;
-
- /** The column message format pattern. */
- protected String format;
-
- /** The CSS class attribute of the column header. */
- protected String headerClass;
-
- /** The Map of column table header <th> CSS style attributes. */
- protected Map headerStyles;
-
- /** The title of the column header. */
- protected String headerTitle;
-
- /**
- * The maximum column length. If maxLength is greater than 0 and the column
- * data string length is greater than maxLength, the rendered value will be
- * truncated with an eclipse(...).
- *
- * Autolinked email or URL values will not be constrained.
- *
- * The default value is 0.
- */
- protected int maxLength;
-
- /**
- * The optional MessageFormat used to render the column table cell value.
- */
- protected MessageFormat messageFormat;
-
- /** The property name of the row object to render. */
- protected String name;
-
- /** The column render id attribute status. The default value is false. */
- protected Boolean renderId;
-
- /** The method cached for rendering column values. */
- protected transient Map