/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-2011 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.admin.rest;
import com.sun.enterprise.config.serverbeans.AdminService;
import com.sun.enterprise.config.serverbeans.SecureAdmin;
import com.sun.enterprise.v3.admin.AdminAdapter;
import com.sun.enterprise.v3.common.ActionReporter;
import java.io.IOException;
import java.security.Principal;
import java.util.Locale;
import com.sun.grizzly.tcp.http11.GrizzlyRequest;
import org.glassfish.admin.rest.generator.CommandResourceMetaData;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.glassfish.api.ActionReport;
import org.glassfish.api.Param;
import org.glassfish.api.admin.ServerEnvironment;
import org.glassfish.internal.api.AdminAccessController;
import org.jvnet.hk2.component.Habitat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import javax.security.auth.login.LoginException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.glassfish.api.admin.CommandModel;
import org.glassfish.api.admin.CommandRunner;
import org.glassfish.api.admin.ParameterMap;
import org.glassfish.api.admin.RestRedirect;
import org.glassfish.api.admin.RestRedirects;
import org.jvnet.hk2.config.Attribute;
import org.jvnet.hk2.config.ConfigBeanProxy;
import org.jvnet.hk2.config.ConfigModel;
import org.jvnet.hk2.config.Dom;
import org.jvnet.hk2.config.DomDocument;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.config.serverbeans.Domain;
import org.glassfish.admin.rest.provider.MethodMetaData;
import org.glassfish.admin.rest.provider.ParameterMetaData;
import org.glassfish.admin.rest.provider.ProviderUtil;
import org.glassfish.admin.rest.results.ActionReportResult;
import org.glassfish.admin.rest.utils.ConfigModelComparator;
import org.glassfish.admin.rest.utils.DomConfigurator;
import org.glassfish.admin.rest.utils.xml.RestActionReporter;
import static org.glassfish.admin.rest.Util.*;
import static org.glassfish.admin.rest.provider.ProviderUtil.getElementLink;
/**
* Resource utilities class. Used by resource templates,
* <code>TemplateListOfResource</code> and <code>TemplateRestResource</code>
*
* @author Rajeshwar Patil
*/
public class ResourceUtil {
private final static String QUERY_PARAMETERS = "queryParameters";
private final static String MESSAGE_PARAMETERS = "messageParameters";
//TODO this is copied from org.jvnet.hk2.config.Dom. If we are not able to encapsulate the conversion in Dom,
//need to make sure that the method convertName is refactored into smaller methods such that trimming of prefixes stops.
//We will need a promotion of HK2 for this.
static final Pattern TOKENIZER;
static {
String pattern = or(
split("x", "X"), // AbcDef -> Abc|Def
split("X", "Xx"), // USArmy -> US|Army
//split("\\D","\\d"), // SSL2 -> SSL|2
split("\\d", "\\D") // SSL2Connector -> SSL|2|Connector
);
pattern = pattern.replace("x", "\\p{Lower}").replace("X", "\\p{Upper}");
TOKENIZER = Pattern.compile(pattern);
}
private ResourceUtil() {
}
/**
* Adjust the input parameters. In case of POST and DELETE methods, user
* can provide name, id or DEFAULT parameter for primary parameter(i.e the
* object to create or delete). This method is used to rename primary
* parameter name to DEFAULT irrespective of what user provides.
*/
public static void adjustParameters(Map<String, String> data) {
if (data != null) {
if (!(data.containsKey("DEFAULT"))) {
boolean isRenamed = renameParameter(data, "id", "DEFAULT");
if (!isRenamed) {
renameParameter(data, "name", "DEFAULT");
}
}
}
}
/**
* Adjust the input parameters. In case of POST and DELETE methods, user
* can provide id or DEFAULT parameter for primary parameter(i.e the
* object to create or delete). This method is used to rename primary
* parameter name to DEFAULT irrespective of what user provides.
*/
public static void defineDefaultParameters(Map<String, String> data) {
if (data != null) {
if (!(data.containsKey("DEFAULT"))) {
renameParameter(data, "id", "DEFAULT");
}
}
}
/**
* Returns the name of the command associated with
* this resource,if any, for the given operation.
*
* @param type the given resource operation
* @return String the associated command name for the given operation.
*/
public static String getCommand(RestRedirect.OpType type, ConfigModel model) {
Class<? extends ConfigBeanProxy> cbp = null;
try {
cbp = (Class<? extends ConfigBeanProxy>) model.classLoaderHolder.get().loadClass(model.targetTypeName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
RestRedirects restRedirects = cbp.getAnnotation(RestRedirects.class);
if (restRedirects != null) {
RestRedirect[] values = restRedirects.value();
for (RestRedirect r : values) {
if (r.opType().equals(type)) {
return r.commandName();
}
}
}
return null;
}
/**
* Executes the specified __asadmin command.
*
* @param commandName the command to execute
* @param parameters the command parameters
* @param habitat the habitat
* @return ActionReport object with command execute status details.
*/
public static RestActionReporter runCommand(String commandName, Map<String, String> parameters, Habitat habitat, String resultType) {
ParameterMap p = new ParameterMap();
for (Map.Entry<String, String> entry : parameters.entrySet()) {
p.set(entry.getKey(), entry.getValue());
}
return runCommand(commandName, p, habitat, resultType);
}
public static RestActionReporter runCommand(String commandName, ParameterMap parameters, Habitat habitat, String resultType) {
CommandRunner cr = habitat.getComponent(CommandRunner.class);
RestActionReporter ar = new RestActionReporter();
cr.getCommandInvocation(commandName, ar).parameters(parameters).execute();
return ar;
}
/**
* Constructs and returns the resource method meta-data.
*
* @param command the command associated with the resource method
* @param habitat the habitat
* @param logger the logger to use
* @return MethodMetaData the meta-data store for the resource method.
*/
public static MethodMetaData getMethodMetaData(String command, Habitat habitat, Logger logger) {
return getMethodMetaData(command, null, habitat, logger);
}
/**
* Constructs and returns the resource method meta-data.
*
* @param command the command assocaited with the resource method
* @param commandParamsToSkip the command parameters for which not to
* include the meta-data.
* @param habitat the habitat
* @param logger the logger to use
* @return MethodMetaData the meta-data store for the resource method.
*/
public static MethodMetaData getMethodMetaData(String command, HashMap<String, String> commandParamsToSkip,
Habitat habitat, Logger logger) {
MethodMetaData methodMetaData = new MethodMetaData();
if (command != null) {
Collection<CommandModel.ParamModel> params;
if (commandParamsToSkip == null) {
params = getParamMetaData(command, habitat, logger);
} else {
params = getParamMetaData(command, commandParamsToSkip.keySet(), habitat, logger);
}
Iterator<CommandModel.ParamModel> iterator = params.iterator();
CommandModel.ParamModel paramModel;
while (iterator.hasNext()) {
paramModel = iterator.next();
Param param = paramModel.getParam();
ParameterMetaData parameterMetaData = getParameterMetaData(paramModel);
String parameterName = (param.primary()) ? "id" : paramModel.getName();
// If the Param has an alias, use it instead of the name
String alias = param.alias();
if (alias != null && (!alias.isEmpty())) {
parameterName = alias;
}
methodMetaData.putParameterMetaData(parameterName, parameterMetaData);
}
}
return methodMetaData;
}
/**
* Resolve command parameter value of $parent for the parameter
* in the given map.
*
* @param uriInfo the uri context to extract parent name value.
*/
public static void resolveParentParamValue(HashMap<String, String> commandParams, UriInfo uriInfo) {
String parent = getParentName(uriInfo);
if (parent != null) {
for (String key : commandParams.keySet()) {
if (commandParams.get(key).equals(Constants.VAR_PARENT)) {
commandParams.put(key, parent);
break;
}
}
/*
Set<String> keys = commandParams.keySet();
Iterator<String> iterator = keys.iterator();
String key;
while (iterator.hasNext()) {
key = iterator.next();
if (commandParams.get(key).equals(Constants.VAR_PARENT)) {
commandParams.put(key, parent);
break;
}
}
*/
}
}
/**
* Constructs and returns the resource method meta-data. This method is
* called to get meta-data in case of update method (POST).
*
* @param configBeanModel the config bean associated with the resource.
* @return MethodMetaData the meta-data store for the resource method.
*/
public static MethodMetaData getMethodMetaData(ConfigModel configBeanModel) {
MethodMetaData methodMetaData = new MethodMetaData();
Class<? extends ConfigBeanProxy> configBeanProxy = null;
try {
configBeanProxy = (Class<? extends ConfigBeanProxy>) configBeanModel.classLoaderHolder.get().loadClass(configBeanModel.targetTypeName);
Set<String> attributeNames = configBeanModel.getAttributeNames();
for (String attributeName : attributeNames) {
String methodName = getAttributeMethodName(attributeName);
Method method = null;
try {
method = configBeanProxy.getMethod(methodName);
} catch (NoSuchMethodException e) {
// Method not found, so let's try a brute force method if the method
// can't be found via the method above. For example: for
// Ssl.getSSLInactivityTimeout(), we calculate getSslInactivityTimeout,
// which doesn't match due to case.
for (Method m : configBeanProxy.getMethods()) {
if (m.getName().equalsIgnoreCase(methodName)) {
method = m;
}
}
}
Attribute attribute = method.getAnnotation(Attribute.class);
if (attribute != null) {
ParameterMetaData parameterMetaData = getParameterMetaData(attribute);
if (method.getAnnotation(Deprecated.class) != null) {
parameterMetaData.putAttribute(Constants.DEPRECATED, "true");
}
//camelCase the attributeName before passing out
attributeName = eleminateHypen(attributeName);
methodMetaData.putParameterMetaData(attributeName, parameterMetaData);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return methodMetaData;
}
public static MethodMetaData getMethodMetaData2(Dom parent, ConfigModel childModel, int parameterType) {
MethodMetaData methodMetaData = new MethodMetaData();
List<Class<?>> interfaces = new ArrayList<Class<?>>();
Map<String, ParameterMetaData> params = new HashMap<String, ParameterMetaData>();
try {
Class<? extends ConfigBeanProxy> configBeanProxy =
(Class<? extends ConfigBeanProxy>) childModel.classLoaderHolder.get().loadClass(childModel.targetTypeName);
getInterfaces(configBeanProxy, interfaces);
Set<String> attributeNames = childModel.getAttributeNames();
for (String attributeName : attributeNames) {
String methodName = ResourceUtil.getAttributeMethodName(attributeName);
//camelCase the attributeName before passing out
attributeName = Util.eleminateHypen(attributeName);
ParameterMetaData parameterMetaData = params.get(attributeName);
if (parameterMetaData == null) {
parameterMetaData = new ParameterMetaData();
params.put(attributeName, parameterMetaData);
}
// Check parent interfaces
for (int i = interfaces.size() - 1; i >= 0; i--) {
Class<?> intf = interfaces.get(i);
try {
Method method = intf.getMethod(methodName);
Attribute attribute = method.getAnnotation(Attribute.class);
if (attribute != null) {
ParameterMetaData localParam = ResourceUtil.getParameterMetaData(attribute);
copyParameterMetaDataAttribute(localParam, parameterMetaData, Constants.DEFAULT_VALUE);
copyParameterMetaDataAttribute(localParam, parameterMetaData, Constants.KEY);
copyParameterMetaDataAttribute(localParam, parameterMetaData, Constants.TYPE);
copyParameterMetaDataAttribute(localParam, parameterMetaData, Constants.OPTIONAL);
}
} catch (NoSuchMethodException e) {
}
}
// Check ConfigBean
try {
Method method = configBeanProxy.getMethod(methodName);
Attribute attribute = method.getAnnotation(Attribute.class);
if (attribute != null) {
ParameterMetaData localParam = ResourceUtil.getParameterMetaData(attribute);
copyParameterMetaDataAttribute(localParam, parameterMetaData, Constants.DEFAULT_VALUE);
copyParameterMetaDataAttribute(localParam, parameterMetaData, Constants.KEY);
copyParameterMetaDataAttribute(localParam, parameterMetaData, Constants.TYPE);
copyParameterMetaDataAttribute(localParam, parameterMetaData, Constants.OPTIONAL);
}
} catch (NoSuchMethodException e) {
}
methodMetaData.putParameterMetaData(attributeName, parameterMetaData);
}
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException(cnfe);
}
return methodMetaData;
}
protected static void copyParameterMetaDataAttribute(ParameterMetaData from, ParameterMetaData to, String key) {
if (from.getAttributeValue(key) != null) {
to.putAttribute(key, from.getAttributeValue(key));
}
}
protected static void getInterfaces(Class<?> clazz, List<Class<?>> interfaces) {
for (Class<?> intf : clazz.getInterfaces()) {
interfaces.add(intf);
getInterfaces(intf, interfaces);
}
}
/* test if a command really exists in the current runningVM
*
*/
public static boolean commandIsPresent(Habitat habitat , String commandName){
CommandRunner cr = habitat.getComponent(CommandRunner.class);
CommandModel cm = cr.getModel(commandName, RestService.logger);
return (cm!=null);
}
/**
* Constructs and returns the parameter meta-data.
*
* @param commandName the command associated with the resource method
* @param habitat the habitat
* @param logger the logger to use
* @return Collection the meta-data for the parameter of the resource method.
*/
public static Collection<CommandModel.ParamModel> getParamMetaData(
String commandName, Habitat habitat, Logger logger) {
CommandRunner cr = habitat.getComponent(CommandRunner.class);
CommandModel cm = cr.getModel(commandName, logger);
Collection<CommandModel.ParamModel> params = cm.getParameters();
//print(params);
return params;
}
/**
* Constructs and returns the parameter meta-data.
*
* @param commandName the command associated with the resource method
* @param commandParamsToSkip the command parameters for which not to
* include the meta-data.
* @param habitat the habitat
* @param logger the logger to use
* @return Collection the meta-data for the parameter of the resource method.
*/
public static Collection<CommandModel.ParamModel> getParamMetaData(
String commandName, Collection<String> commandParamsToSkip,
Habitat habitat, Logger logger) {
CommandRunner cr = habitat.getComponent(CommandRunner.class);
CommandModel cm = cr.getModel(commandName, logger);
Collection<String> parameterNames = cm.getParametersNames();
ArrayList<CommandModel.ParamModel> metaData = new ArrayList<CommandModel.ParamModel>();
CommandModel.ParamModel paramModel;
for (String name : parameterNames) {
paramModel = cm.getModelFor(name);
String parameterName = (paramModel.getParam().primary()) ? "id" : paramModel.getName();
boolean skipParameter = false;
try {
skipParameter = commandParamsToSkip.contains(parameterName);
} catch (Exception e) {
String errorMessage = localStrings.getLocalString("rest.metadata.skip.error",
"Parameter \"{0}\" may be redundant and not required.",
new Object[]{parameterName});
// TODO: Why are we logging twice?
Logger.getLogger(ResourceUtil.class.getName()).log(Level.INFO, null, errorMessage);
Logger.getLogger(ResourceUtil.class.getName()).log(Level.INFO, null, e);
}
if (!skipParameter) {
metaData.add(paramModel);
}
}
//print(metaData);
return metaData;
}
//removes entries with empty value from the given Map
public static void purgeEmptyEntries(Map<String, String> data) {
//hack-2 : remove empty entries if the form has a hidden param for __remove_empty_entries__
if ("true".equals(data.get("__remove_empty_entries__"))) {
data.remove("__remove_empty_entries__");
//findbug
for (Iterator<Map.Entry<String, String>> i = data.entrySet().iterator(); i.hasNext();) {
Map.Entry<String, String> entry = i.next();
String value = entry.getValue();
if ((value == null) || (value.length() < 1)) {
i.remove();
}
}
}
}
/**
* Constructs and returns the appropriate response object based on the client.
*
* @param status the http status code for the response
* @param message message for the response
* @param requestHeaders request headers of the request
* @return Response the response object to be returned to the client
*/
public static Response getResponse(int status, String message, HttpHeaders requestHeaders, UriInfo uriInfo) {
if (isBrowser(requestHeaders)) {
message = getHtml(message, uriInfo, false);
}
return Response.status(status).entity(message).build();
}
public static ActionReportResult getActionReportResult(ActionReport parentActionReport, String message, HttpHeaders requestHeaders, UriInfo uriInfo) {
ActionReportResult result = getActionReportResult(parentActionReport.getActionExitCode(), message, requestHeaders, uriInfo);
result.getActionReport().getSubActionsReport().addAll(((ActionReporter)parentActionReport).getSubActionsReport());
return result;
}
public static ActionReportResult getActionReportResult(ActionReport.ExitCode status, String message, HttpHeaders requestHeaders, UriInfo uriInfo) {
if (isBrowser(requestHeaders)) {
message = getHtml(message, uriInfo, false);
}
RestActionReporter ar = new RestActionReporter();
ActionReportResult result = new ActionReportResult(ar);
if (status != ActionReport.ExitCode.SUCCESS && status != ActionReport.ExitCode.WARNING) {
result.setErrorMessage(message);
result.setIsError(true);
}
ar.setActionExitCode(status);
ar.setMessage(message);
return result;
}
/**
* special case for the delete operation: we need to give back the URI of the parent
* since the resource we are on is deleted
*
* @param status
* @param message
* @param requestHeaders
* @param uriInfo
* @return
*/
// FIXME: This doesn't do what the javadoc says it should
public static Response getDeleteResponse(int status, String message,
HttpHeaders requestHeaders, UriInfo uriInfo,
Habitat habitat) {
if (isBrowser(requestHeaders)) {
message = getHtml(message, uriInfo, true);
}
return Response.status(status).entity(message).build();
}
/**
* <p>This method takes any query string parameters and adds them to the specified map. This
* is used, for example, with the delete operation when cascading deletes are required:</p>
* <code style="margin-left: 3em">DELETE http://localhost:4848/.../foo?cascade=true</code>
* <p>The reason we need to use query parameters versus "body" variables is the limitation
* that HttpURLConnection has in this regard.
*
* @param data
*/
public static void addQueryString(MultivaluedMap<String, String> qs, Map<String, String> data) {
for (Map.Entry<String, List<String>> entry : qs.entrySet()) {
String key = entry.getKey();
for (String value : entry.getValue()) {
data.put(key, value); // TODO: Last one wins? Can't imagine we'll see List.size() > 1, but...
}
}
}
public static void addQueryString(MultivaluedMap<String, String> qs, Properties data) {
for (Map.Entry<String, List<String>> entry : qs.entrySet()) {
String key = entry.getKey();
for (String value : entry.getValue()) {
data.put(key, value); // TODO: Last one wins? Can't imagine we'll see List.size() > 1, but...
}
}
}
//Construct parameter meta-data from the model
static ParameterMetaData getParameterMetaData(CommandModel.ParamModel paramModel) {
Param param = paramModel.getParam();
ParameterMetaData parameterMetaData = new ParameterMetaData();
parameterMetaData.putAttribute(Constants.TYPE, getXsdType(paramModel.getType().toString()));
parameterMetaData.putAttribute(Constants.OPTIONAL, Boolean.toString(param.optional()));
String val = param.defaultValue();
if ((val != null) && (!val.equals("\u0000"))) {
parameterMetaData.putAttribute(Constants.DEFAULT_VALUE, param.defaultValue());
}
parameterMetaData.putAttribute(Constants.ACCEPTABLE_VALUES, param.acceptableValues());
return parameterMetaData;
}
//Construct parameter meta-data from the attribute annotation
static ParameterMetaData getParameterMetaData(Attribute attribute) {
ParameterMetaData parameterMetaData = new ParameterMetaData();
parameterMetaData.putAttribute(Constants.TYPE, getXsdType(attribute.dataType().toString()));
parameterMetaData.putAttribute(Constants.OPTIONAL, Boolean.toString(!attribute.required()));
if (!(attribute.defaultValue().equals("\u0000"))) {
parameterMetaData.putAttribute(Constants.DEFAULT_VALUE, attribute.defaultValue());
}
parameterMetaData.putAttribute(Constants.KEY, Boolean.toString(attribute.key()));
//FIXME - Currently, Attribute class does not provide acceptable values.
//parameterMetaData.putAttribute(Contants.ACCEPTABLE_VALUES,
// getXsdType(attribute.acceptableValues()));
return parameterMetaData;
}
//rename the given input parameter
private static boolean renameParameter(Map<String, String> data,
String parameterToRename, String newName) {
if ((data.containsKey(parameterToRename))) {
String value = data.get(parameterToRename);
data.remove(parameterToRename);
data.put(newName, value);
return true;
}
return false;
}
//print given parameter meta-data.
private static void print(Collection<CommandModel.ParamModel> params) {
for (CommandModel.ParamModel pm : params) {
System.out.println("Command Param: " + pm.getName());
System.out.println("Command Param Type: " + pm.getType());
System.out.println("Command Param Name: " + pm.getParam().name());
System.out.println("Command Param Shortname: " + pm.getParam().shortName());
}
}
//returns true only if the request is from browser
private static boolean isBrowser(HttpHeaders requestHeaders) {
boolean isClientAcceptsHtml = false;
MediaType media = requestHeaders.getMediaType();
java.util.List<String> acceptHeaders =
requestHeaders.getRequestHeader(HttpHeaders.ACCEPT);
for (String header : acceptHeaders) {
if (header.contains(MediaType.TEXT_HTML)) {
isClientAcceptsHtml = true;
break;
}
}
if (media != null) {
if ((media.equals(MediaType.APPLICATION_FORM_URLENCODED_TYPE)) &&
(isClientAcceptsHtml)) {
return true;
}
}
return false;
}
private static String getXsdType(String javaType) {
if (javaType.indexOf(Constants.JAVA_STRING_TYPE) != -1)
return Constants.XSD_STRING_TYPE;
if (javaType.indexOf(Constants.JAVA_BOOLEAN_TYPE) != -1)
return Constants.XSD_BOOLEAN_TYPE;
if (javaType.indexOf(Constants.JAVA_INT_TYPE) != -1)
return Constants.XSD_INT_TYPE;
if (javaType.indexOf(Constants.JAVA_PROPERTIES_TYPE) != -1)
return Constants.XSD_PROPERTIES_TYPE;
return javaType;
}
static String getAttributeMethodName(String attributeName) {
return methodNameFromDtdName(attributeName, "get");
}
private static String split(String lookback, String lookahead) {
return "((?<=" + lookback + ")(?=" + lookahead + "))";
}
private static String or(String... tokens) {
StringBuilder buf = new StringBuilder();
for (String t : tokens) {
if (buf.length() > 0) buf.append('|');
buf.append(t);
}
return buf.toString();
}
public static String convertToXMLName(String name) {
// tokenize by finding 'x|X' and 'X|Xx' then insert '-'.
StringBuilder buf = new StringBuilder(name.length() + 5);
for (String t : TOKENIZER.split(name)) {
if (buf.length() > 0) {
buf.append('-');
}
buf.append(t.toLowerCase(Locale.US));
}
return buf.toString();
}
/**
* @return A copy of given <code>sourceData</code> where key of each entry from it is converted to xml name
*/
public static HashMap<String, String> translateCamelCasedNamesToXMLNames(Map<String, String> sourceData) {
HashMap<String, String> convertedData = new HashMap<String, String>(sourceData.size());
for (Map.Entry<String, String> entry : sourceData.entrySet()) {
String camelCasedKeyName = entry.getKey();
String xmlKeyName = convertToXMLName(camelCasedKeyName);
convertedData.put(xmlKeyName, entry.getValue());
}
return convertedData;
}
/* we try to prefer html by default for all browsers (safari, chrome, firefox).
* Same if the request is asking for "*"
* among all the possible AcceptableMediaTypes
*/
public static String getResultType(HttpHeaders requestHeaders) {
String result = "html";
String firstOne = null;
List<MediaType> lmt = requestHeaders.getAcceptableMediaTypes();
for (MediaType mt : lmt) {
if (mt.getSubtype().equals("html")) {
return result;
}
if (mt.getSubtype().equals("*")) {
return result;
}
if (firstOne == null) { //default to the first one if many are there.
firstOne = mt.getSubtype();
}
}
if (firstOne != null) {
return firstOne;
} else {
return result;
}
}
public static Map buildMethodMetadataMap(MethodMetaData mmd) { // yuck
Map<String, Map> map = new TreeMap<String, Map>();
Set<String> params = mmd.parameters();
Iterator<String> iterator = params.iterator();
String param;
while (iterator.hasNext()) {
param = iterator.next();
ParameterMetaData parameterMetaData = mmd.getParameterMetaData(param);
map.put(param, processAttributes(parameterMetaData.attributes(), parameterMetaData));
}
return map;
}
private static Map<String, String> processAttributes(Set<String> attributes, ParameterMetaData parameterMetaData) {
Map <String, String> pmdm = new HashMap<String, String>();
Iterator<String> attriter = attributes.iterator();
String attributeName;
while (attriter.hasNext()) {
attributeName = attriter.next();
String attributeValue = parameterMetaData.getAttributeValue(attributeName);
pmdm.put(attributeName, attributeValue);
}
return pmdm;
}
/* REST can now be configured via RestConfig to show or hide the deprecated elements and attributes
* @return true if this model is deprecated
*/
static public boolean isDeprecated(ConfigModel model) {
Class<? extends ConfigBeanProxy> cbp = null;
try {
cbp = (Class<? extends ConfigBeanProxy>) model.classLoaderHolder.get().loadClass(model.targetTypeName);
Deprecated dep = cbp.getAnnotation(Deprecated.class);
return dep != null;
} catch (ClassNotFoundException e) {
//e.printStackTrace();
}
return false;
}
public static Map<String, String> getResourceLinks(Dom dom, UriInfo uriInfo, boolean canShowDeprecated) {
Map<String, String> links = new TreeMap<String, String>();
Set<String> elementNames = dom.model.getElementNames();
for (String elementName : elementNames) { //for each element
if (elementName.equals("*")) {
ConfigModel.Node node = (ConfigModel.Node) dom.model.getElement(elementName);
ConfigModel childModel = node.getModel();
List<ConfigModel> lcm = getRealChildConfigModels(childModel, dom.document);
Collections.sort(lcm, new ConfigModelComparator());
if (lcm != null) {
for (ConfigModel cmodel : lcm) {
if ((!isDeprecated(cmodel) || canShowDeprecated)) {
links.put(cmodel.getTagName(), ProviderUtil.getElementLink(uriInfo, cmodel.getTagName()));
}
}
}
} else {
ConfigModel.Property childElement = dom.model.getElement(elementName);
boolean deprec = false;
if (childElement instanceof ConfigModel.Node) {
ConfigModel.Node node = (ConfigModel.Node) childElement;
deprec = isDeprecated(node.getModel());
}
for (String annotation : childElement.getAnnotations()) {
if (annotation.equals(Deprecated.class.getName())) {
deprec = true;
}
}
if ((!deprec || canShowDeprecated)) {
links.put(elementName, ProviderUtil.getElementLink(uriInfo, elementName));
}
}
}
String beanName = getUnqualifiedTypeName(dom.model.targetTypeName);
for (CommandResourceMetaData cmd : CommandResourceMetaData.getCustomResourceMapping(beanName)) {
links.put(cmd.resourcePath, ProviderUtil.getElementLink(uriInfo, cmd.resourcePath));
}
return links;
}
/**
* @param qualifiedTypeName
* @return unqualified type name for given qualified type name. This is a substring of qualifiedTypeName after last "."
*/
public static String getUnqualifiedTypeName(String qualifiedTypeName) {
return qualifiedTypeName.substring(qualifiedTypeName.lastIndexOf(".") + 1, qualifiedTypeName.length());
}
public static boolean isOnlyATag (ConfigModel model){
return (model.getAttributeNames().isEmpty()) && (model.getElementNames().isEmpty());
}
public static List<ConfigModel> getRealChildConfigModels(ConfigModel childModel, DomDocument domDocument) {
List<ConfigModel> retlist = new ArrayList<ConfigModel>();
try {
Class<?> subType = childModel.classLoaderHolder.get().loadClass(childModel.targetTypeName);
List<ConfigModel> list = domDocument.getAllModelsImplementing(subType);
if (list != null) {
for (ConfigModel el : list) {
if (isOnlyATag(el)) { //this is just a tag element
retlist.addAll(getRealChildConfigModels(el, domDocument));
} else {
retlist.add(el);
}
}
} else {//https://glassfish.dev.java.net/issues/show_bug.cgi?id=12654
if (!isOnlyATag(childModel)) { //this is just a tag element
retlist.add(childModel);
}
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return retlist;
}
/**
* @param model
* @return name of the key attribute for the given model.
*/
private static String getKey(Dom model) {
String key = null;
if (model.getKey() == null) {
for (String s : model.getAttributeNames()) {//no key, by default use the name attr
if (s.equals("name")) {
key = model.attribute(s) ;
}
}
if (key == null)//nothing, so pick the first one
{
Set<String> attributeNames = model.getAttributeNames();
if(!attributeNames.isEmpty()) {
key = model.attribute(attributeNames.iterator().next());
} else {
//TODO carried forward from old generator. Should never reach here. But we do for ConfigExtension and WebModuleConfig
key = "ThisIsAModelBug:NoKeyAttr"; //no attr choice fo a key!!! Error!!!
}
}
} else {
key = model.getKey();
}
return key;
}
public static Map<String, String> getResourceLinks(List<Dom> proxyList, UriInfo uriInfo) {
Map<String, String> links = new TreeMap<String, String>();
Collections.sort(proxyList, new DomConfigurator());
for (Dom proxy : proxyList) { //for each element
try {
links.put(
getKey(proxy),
getElementLink(uriInfo, getKey(proxy)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return links;
}
public static List<Map<String, String>> getCommandLinks(String[][] commandResourcesPaths) {
List<Map<String, String>> commands = new ArrayList<Map<String, String>>();
for (String[] array : commandResourcesPaths) {
Map<String, String> command = new HashMap<String, String>();
command.put("command", array[0]);
command.put("method", array[1]);
command.put("path", array[2]);
commands.add(command);
}
return commands;
}
public static void addMethodMetaData(ActionReport ar, Map<String, MethodMetaData> mmd) {
List<Map> methodMetaData = new ArrayList<Map>();
MethodMetaData getMetaData = mmd.get("GET");
methodMetaData.add(new HashMap() {{ put("name", "GET"); }});
if (getMetaData != null) { //are they extra params for a GET command?
Map<String, Object> getMetaDataMap = new HashMap<String, Object>();
if (getMetaData.sizeParameterMetaData() > 0) {
getMetaDataMap.put(MESSAGE_PARAMETERS, buildMethodMetadataMap(getMetaData));
}
methodMetaData.add(getMetaDataMap);
}
MethodMetaData postMetaData = mmd.get("POST");
Map<String, Object> postMetaDataMap = new HashMap<String, Object>();
if (postMetaData != null) {
postMetaDataMap.put("name", "POST");
// if (postMetaData.sizeQueryParamMetaData() > 0) {
// postMetaDataMap.put(QUERY_PARAMETERS, buildMethodMetadataMap(postMetaData, true));
// }
if (postMetaData.sizeParameterMetaData() > 0) {
postMetaDataMap.put(MESSAGE_PARAMETERS, buildMethodMetadataMap(postMetaData));
}
methodMetaData.add(postMetaDataMap);
}
MethodMetaData deleteMetaData = mmd.get("DELETE");
if (deleteMetaData != null) {
Map<String, Object> deleteMetaDataMap = new HashMap<String, Object>();
deleteMetaDataMap.put("name", "DELETE");
deleteMetaDataMap.put(MESSAGE_PARAMETERS, buildMethodMetadataMap(deleteMetaData));
methodMetaData.add(deleteMetaDataMap);
}
ar.getExtraProperties().put("methods", methodMetaData);
}
public static RestConfig getRestConfig(Habitat habitat) {
if (habitat == null) {
return null;
}
Domain domain = habitat.getComponent(Domain.class);
if (domain != null) {
Config config = domain.getConfigNamed("server-config");
if (config != null) {
return config.getExtensionByType(RestConfig.class);
}
}
return null;
}
/*
* returns true if the HTML viewer displays the deprecated elements or attributes
* of a config bean
*/
public static boolean canShowDeprecatedItems(Habitat habitat) {
RestConfig rg = getRestConfig(habitat);
if ((rg != null) && (rg.getShowDeprecatedItems().equalsIgnoreCase("true"))) {
return true;
}
return false;
}
/**
* Authenticate the given req against admin realm.
* @return Access as granted by authenticator
*/
public static AdminAccessController.Access authenticateViaAdminRealm(Habitat habitat, GrizzlyRequest req) throws LoginException, IOException {
return authenticateViaAdminRealm(habitat, req, req.getRemoteHost());
}
private static final String DAS_LOOK_FOR_CERT_PROPERTY_NAME = "org.glassfish.admin.DASCheckAdminCert";
/**
* Authenticate the given req as originated from given remoteHost against admin realm.
* @return Access as granted by authenticator
*/
public static AdminAccessController.Access authenticateViaAdminRealm(Habitat habitat, GrizzlyRequest req, String remoteHost) throws IOException, LoginException {
String[] up = AdminAdapter.getUserPassword(req.getRequest());
String user = up[0];
String password = up.length > 1 ? up[1] : "";
AdminAccessController authenticator = habitat.getByContract(AdminAccessController.class);
AdminAccessController.Access access = AdminAccessController.Access.FULL; //if the authenticator is not available, allow all access - per Jerome
if (authenticator != null) {
// This is temporary workaround for a Grizzly issue that prohibits uploading (deploy) files larger than 2MB when secure admin is enabled.
// The workaround will not be required in trunk when the corresponding Grizzly issue is fixed.
// Please see http://java.net/jira/browse/GLASSFISH-16665 for details
// The workaround duplicates code from AdminAdapter.
ServerEnvironment serverEnvironment = habitat.getByContract(ServerEnvironment.class);
final Principal sslPrincipal = !serverEnvironment.isDas() || Boolean.getBoolean(DAS_LOOK_FOR_CERT_PROPERTY_NAME) ? req.getUserPrincipal() : null;
AdminService as = habitat.getByType(AdminService.class);
access = authenticator.loginAsAdmin(user, password, as.getAuthRealmName(), remoteHost, getAuthRelatedHeaders(req), sslPrincipal);
}
return access;
}
/**
* Extract authentication related headers from Grizzly request.
* This headers enables us to authenticate a request coming from DAS without a password.
* The headers will be present if secured admin is not turned on and a request is sent from DAS to an instance.
* @param req
* @return Authentication related headers
*/
private static Map<String, String> getAuthRelatedHeaders(GrizzlyRequest req) {
Map<String, String> authRelatedHeaders = Collections.EMPTY_MAP;
String adminIndicatorHeader = req.getHeader(SecureAdmin.Util.ADMIN_INDICATOR_HEADER_NAME);
if(adminIndicatorHeader != null) {
authRelatedHeaders = new HashMap<String, String>(1);
authRelatedHeaders.put(SecureAdmin.Util.ADMIN_INDICATOR_HEADER_NAME, adminIndicatorHeader);
}
return authRelatedHeaders;
}
}