| /* |
| * 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.apache.felix.webconsole; |
| |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.Array; |
| import java.net.URLDecoder; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import javax.servlet.ServletRequest; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.fileupload.FileItem; |
| import org.apache.commons.fileupload.FileUploadBase; |
| import org.apache.commons.fileupload.FileUploadException; |
| import org.apache.commons.fileupload.disk.DiskFileItemFactory; |
| import org.apache.commons.fileupload.servlet.ServletFileUpload; |
| import org.apache.commons.fileupload.servlet.ServletRequestContext; |
| import org.json.JSONException; |
| import org.json.JSONWriter; |
| |
| |
| /** |
| * The <code>WebConsoleUtil</code> provides various utility methods for use |
| * by Web Console plugins. |
| */ |
| public final class WebConsoleUtil |
| { |
| |
| private WebConsoleUtil() |
| { |
| /* no instantiation */ |
| } |
| |
| /** |
| * Returns the {@link VariableResolver} for the given request. |
| * <p> |
| * If no resolver has yet be created for the requests, an instance of the |
| * {@link DefaultVariableResolver} is created with preset properties, |
| * placed into the request and returned. The preset properties are |
| * <code>appRoot</code> set to the value of the |
| * {@link WebConsoleConstants#ATTR_APP_ROOT} request attribute and |
| * <code>pluginRoot</code> set to the value of the |
| * {@link WebConsoleConstants#ATTR_PLUGIN_ROOT} request attribute. |
| * <p> |
| * <b>Note</b>: An object not implementing the {@link VariableResolver} |
| * interface already stored as the |
| * {@link WebConsoleConstants#ATTR_CONSOLE_VARIABLE_RESOLVER} attribute |
| * will silently be replaced by the {@link DefaultVariableResolver} |
| * instance. |
| * |
| * @param request The request whose attribute is returned (or set) |
| * |
| * @return The {@link VariableResolver} for the given request. |
| */ |
| public static VariableResolver getVariableResolver( final ServletRequest request ) |
| { |
| final Object resolverObj = request.getAttribute( WebConsoleConstants.ATTR_CONSOLE_VARIABLE_RESOLVER ); |
| if ( resolverObj instanceof VariableResolver ) |
| { |
| return ( VariableResolver ) resolverObj; |
| } |
| |
| final DefaultVariableResolver resolver = new DefaultVariableResolver(); |
| // FIXME: don't we need a constant for the values below? |
| resolver.put( "appRoot", request.getAttribute( WebConsoleConstants.ATTR_APP_ROOT ) ); //$NON-NLS-1$ |
| resolver.put( "pluginRoot", request.getAttribute( WebConsoleConstants.ATTR_PLUGIN_ROOT ) ); //$NON-NLS-1$ |
| setVariableResolver( request, resolver ); |
| return resolver; |
| } |
| |
| |
| /** |
| * Sets the {@link VariableResolver} as the |
| * {@link WebConsoleConstants#ATTR_CONSOLE_VARIABLE_RESOLVER} |
| * attribute in the given request. An attribute of that name already |
| * existing is silently replaced. |
| * |
| * @param request The request whose attribute is set |
| * @param resolver The {@link VariableResolver} to place into the request |
| */ |
| public static void setVariableResolver( final ServletRequest request, final VariableResolver resolver ) |
| { |
| request.setAttribute( WebConsoleConstants.ATTR_CONSOLE_VARIABLE_RESOLVER, resolver ); |
| } |
| |
| |
| /** |
| * An utility method, that is used to filter out simple parameter from file |
| * parameter when multipart transfer encoding is used. |
| * |
| * This method processes the request and sets a request attribute |
| * {@link AbstractWebConsolePlugin#ATTR_FILEUPLOAD}. The attribute value is a {@link Map} |
| * where the key is a String specifying the field name and the value |
| * is a {@link org.apache.commons.fileupload.FileItem}. |
| * |
| * @param request the HTTP request coming from the user |
| * @param name the name of the parameter |
| * @return if not multipart transfer encoding is used - the value is the |
| * parameter value or <code>null</code> if not set. If multipart is used, |
| * and the specified parameter is field - then the value of the parameter |
| * is returned. |
| */ |
| public static final String getParameter( HttpServletRequest request, String name ) |
| { |
| // just get the parameter if not a multipart/form-data POST |
| if ( !FileUploadBase.isMultipartContent( new ServletRequestContext( request ) ) ) |
| { |
| return request.getParameter( name ); |
| } |
| |
| // check, whether we already have the parameters |
| Map params = ( Map ) request.getAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD ); |
| if ( params == null ) |
| { |
| // parameters not read yet, read now |
| // Create a factory for disk-based file items |
| DiskFileItemFactory factory = new DiskFileItemFactory(); |
| factory.setSizeThreshold( 256000 ); |
| // See https://issues.apache.org/jira/browse/FELIX-4660 |
| final Object repo = request.getAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD_REPO ); |
| if ( repo instanceof File ) |
| { |
| factory.setRepository( (File) repo ); |
| } |
| |
| // Create a new file upload handler |
| ServletFileUpload upload = new ServletFileUpload( factory ); |
| upload.setSizeMax( -1 ); |
| |
| // Parse the request |
| params = new HashMap(); |
| try |
| { |
| List items = upload.parseRequest( request ); |
| for ( Iterator fiter = items.iterator(); fiter.hasNext(); ) |
| { |
| FileItem fi = ( FileItem ) fiter.next(); |
| FileItem[] current = ( FileItem[] ) params.get( fi.getFieldName() ); |
| if ( current == null ) |
| { |
| current = new FileItem[] |
| { fi }; |
| } |
| else |
| { |
| FileItem[] newCurrent = new FileItem[current.length + 1]; |
| System.arraycopy( current, 0, newCurrent, 0, current.length ); |
| newCurrent[current.length] = fi; |
| current = newCurrent; |
| } |
| params.put( fi.getFieldName(), current ); |
| } |
| } |
| catch ( FileUploadException fue ) |
| { |
| // TODO: log |
| } |
| request.setAttribute( AbstractWebConsolePlugin.ATTR_FILEUPLOAD, params ); |
| } |
| |
| FileItem[] param = ( FileItem[] ) params.get( name ); |
| if ( param != null ) |
| { |
| for ( int i = 0; i < param.length; i++ ) |
| { |
| if ( param[i].isFormField() ) |
| { |
| return param[i].getString(); |
| } |
| } |
| } |
| |
| // no valid string parameter, fail |
| return null; |
| } |
| |
| /** |
| * Utility method to handle relative redirects. |
| * Some application servers like Web Sphere handle relative redirects differently |
| * therefore we should make an absolute URL before invoking send redirect. |
| * |
| * @param request the HTTP request coming from the user |
| * @param response the HTTP response, where data is rendered |
| * @param redirectUrl the redirect URI. |
| * @throws IOException If an input or output exception occurs |
| * @throws IllegalStateException If the response was committed or if a partial |
| * URL is given and cannot be converted into a valid URL |
| */ |
| public static final void sendRedirect(final HttpServletRequest request, |
| final HttpServletResponse response, |
| String redirectUrl) throws IOException { |
| // check for relative URL |
| if ( !redirectUrl.startsWith("/") ) { //$NON-NLS-1$ |
| String base = request.getContextPath() + request.getServletPath() + request.getPathInfo(); |
| int i = base.lastIndexOf('/'); |
| if (i > -1) { |
| base = base.substring(0, i); |
| } else { |
| i = base.indexOf(':'); |
| base = (i > -1) ? base.substring(i + 1, base.length()) : ""; //$NON-NLS-1$ |
| } |
| if (!base.startsWith("/")) { //$NON-NLS-1$ |
| base = '/' + base; |
| } |
| redirectUrl = base + '/' + redirectUrl; |
| |
| } |
| response.sendRedirect(redirectUrl); |
| } |
| |
| /** |
| * Sets response headers to force the client to not cache the response |
| * sent back. This method must be called before the response is committed |
| * otherwise it will have no effect. |
| * <p> |
| * This method sets the <code>Cache-Control</code>, <code>Expires</code>, |
| * and <code>Pragma</code> headers. |
| * |
| * @param response The response for which to set the cache prevention |
| */ |
| public static final void setNoCache(final HttpServletResponse response) { |
| response.setHeader("Cache-Control", "no-cache"); //$NON-NLS-1$ //$NON-NLS-2$ |
| response.addHeader("Cache-Control", "no-store"); //$NON-NLS-1$ //$NON-NLS-2$ |
| response.addHeader("Cache-Control", "must-revalidate"); //$NON-NLS-1$ //$NON-NLS-2$ |
| response.addHeader("Cache-Control", "max-age=0"); //$NON-NLS-1$ //$NON-NLS-2$ |
| response.setHeader("Expires", "Thu, 01 Jan 1970 01:00:00 GMT"); //$NON-NLS-1$ //$NON-NLS-2$ |
| response.setHeader("Pragma", "no-cache"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| /** |
| * Escapes HTML special chars like: <>&\r\n and space |
| * |
| * |
| * @param text the text to escape |
| * @return the escaped text |
| */ |
| public static final String escapeHtml(String text) |
| { |
| StringBuffer sb = new StringBuffer(text.length() * 4 / 3); |
| synchronized (sb) // faster buffer operations |
| { |
| char ch, oldch = '_'; |
| for (int i = 0; i < text.length(); i++) |
| { |
| switch (ch = text.charAt(i)) |
| { |
| case '<': |
| sb.append("<"); //$NON-NLS-1$ |
| break; |
| case '>': |
| sb.append(">"); //$NON-NLS-1$ |
| break; |
| case '&': |
| sb.append("&"); //$NON-NLS-1$ |
| break; |
| case ' ': |
| sb.append(" "); //$NON-NLS-1$ |
| break; |
| case '\r': |
| case '\n': |
| if (oldch != '\r' && oldch != '\n') // don't add twice <br> |
| sb.append("<br/>\n"); //$NON-NLS-1$ |
| break; |
| default: |
| sb.append(ch); |
| } |
| oldch = ch; |
| } |
| |
| return sb.toString(); |
| } |
| } |
| |
| /** |
| * Retrieves a request parameter and converts it to int. |
| * |
| * @param request the HTTP request |
| * @param name the name of the request parameter |
| * @param _default the default value returned if the parameter is not set or is not a valid integer. |
| * @return the request parameter if set and is valid integer, or the default value |
| */ |
| public static final int getParameterInt(HttpServletRequest request, String name, |
| int _default) |
| { |
| int ret = _default; |
| String param = request.getParameter(name); |
| try |
| { |
| if (param != null) |
| ret = Integer.parseInt(param); |
| } |
| catch (NumberFormatException nfe) |
| { |
| // don't care, will return default |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Writes a key-value pair in a JSON writer. Write is performed only if both key and |
| * value are not null. |
| * |
| * @param jw the writer, where to write the data |
| * @param key the key value, stored under 'key' |
| * @param value the value stored under 'value' |
| * @throws JSONException if the value cannot be serialized. |
| * |
| * @deprecated Plugins should use their own json code/library |
| */ |
| public static final void keyVal(JSONWriter jw, String key, Object value) |
| throws JSONException |
| { |
| if (key != null && value != null) |
| { |
| jw.object(); |
| jw.key("key"); //$NON-NLS-1$ |
| jw.value(key); |
| jw.key("value"); //$NON-NLS-1$ |
| jw.value(value); |
| jw.endObject(); |
| } |
| } |
| |
| |
| /** |
| * Decode the given value expected to be URL encoded. |
| * <p> |
| * This method first tries to use the Java 1.4 method |
| * <code>URLDecoder.decode(String, String)</code> method and falls back to |
| * the now deprecated <code>URLDecoder.decode(String, String)</code> |
| * which uses the platform character set to decode the string. This is |
| * because the platforms before 1.4 and most notably some OSGi Execution |
| * Environments (such as Minimum EE) do not provide the newer method. |
| * |
| * @param value the value to decode |
| * @return the decoded string |
| */ |
| public static String urlDecode( final String value ) |
| { |
| // shortcut for empty or missing values |
| if ( value == null || value.length() == 0 ) |
| { |
| return value; |
| } |
| |
| try |
| { |
| return URLDecoder.decode( value, "UTF-8" ); //$NON-NLS-1$ |
| } |
| catch ( Throwable t ) |
| { |
| // expected NoSuchMethodError: if platform does not support it |
| // expected UnsupportedEncoding (not really: UTF-8 is required) |
| return URLDecoder.decode( value ); |
| } |
| } |
| |
| /** |
| * This method will stringify a Java object. It is mostly used to print the values |
| * of unknown properties. This method will correctly handle if the passed object |
| * is array and will property display it. |
| * |
| * If the value is byte[] the elements are shown as Hex |
| * |
| * @param value the value to convert |
| * @return the string representation of the value |
| */ |
| public static final String toString(Object value) |
| { |
| if (value == null) |
| { |
| return "n/a"; //$NON-NLS-1$ |
| } |
| else if (value.getClass().isArray()) |
| { |
| final StringBuffer sb = new StringBuffer(); |
| final int len = Array.getLength(value); |
| synchronized (sb) |
| { // it's faster to synchronize ALL loop calls |
| sb.append('['); |
| for (int i = 0; i < len; i++) |
| { |
| final Object element = Array.get(value, i); |
| if (element instanceof Byte) |
| { |
| // convert byte[] to hex string |
| sb.append("0x"); //$NON-NLS-1$ |
| final String x = Integer.toHexString(((Byte) element).intValue() & 0xff); |
| if (1 == x.length()) |
| { |
| sb.append('0'); |
| } |
| sb.append(x); |
| } |
| else |
| { |
| sb.append(toString(element)); |
| } |
| |
| if (i < len - 1) |
| { |
| sb.append(", "); //$NON-NLS-1$ |
| } |
| } |
| return sb.append(']').toString(); |
| } |
| } |
| else |
| { |
| return value.toString(); |
| } |
| |
| } |
| } |