org.apache.felix.webconsole.internal.Util Maven / Gradle / Ivy
Show all versions of org.apache.felix.webconsole Show documentation
/*
* 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.internal;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Locale;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* The Util
class contains various utility methods used internally
* by the web console implementation and the build-in plugins.
*/
public class Util {
/**
* Return a display name for the given bundle
:
*
* - If the bundle has a non-empty
Bundle-Name
manifest
* header that value is returned.
* - Otherwise the symbolic name is returned if set
* - Otherwise the bundle's location is returned if defined
* - Finally, as a last resort, the bundles id is returned
*
*
* @param bundle the bundle which name to retrieve
* @param locale the locale, in which the bundle name is requested
* @return the bundle name - see the description of the method for more details.
*/
public static String getName( Bundle bundle, Locale locale )
{
final String loc = locale == null ? null : locale.toString();
String name = ( String ) bundle.getHeaders( loc ).get( Constants.BUNDLE_NAME );
if ( name == null || name.length() == 0 )
{
name = bundle.getSymbolicName();
if ( name == null )
{
name = bundle.getLocation();
if ( name == null )
{
name = String.valueOf( bundle.getBundleId() );
}
}
}
return name;
}
/**
* Returns the value of the header or the empty string if the header
* is not available.
*
* @param bundle the bundle which header to retrieve
* @param headerName the name of the header to retrieve
* @return the header or empty string if it is not set
*/
public static String getHeaderValue( Bundle bundle, String headerName )
{
Object value = bundle.getHeaders().get(headerName);
if ( value != null )
{
return value.toString();
}
return "";
}
/**
* Orders the bundles according to their name as returned by
* {@link #getName(Bundle, Locale)}, with the exception that the system bundle is
* always place as the first entry. If two bundles have the same name, they
* are ordered according to their version. If they have the same version,
* the bundle with the lower bundle id comes before the other.
*
* @param bundles the bundles to sort
* @param locale the locale, used to obtain the localized bundle name
*/
public static void sort( Bundle[] bundles, Locale locale )
{
Arrays.sort( bundles, new BundleNameComparator( locale ) );
}
/**
* This method expects a locale string in format language_COUNTRY, or
* language. The method will determine which is the correct form of locale
* string and construct a Locale
object.
*
* @param locale the locale string, if null
- default locale is
* returned
* @return a locale object
* @see Locale
*/
public static final Locale parseLocaleString(String locale)
{
if (locale == null)
{
return Locale.getDefault();
}
int idx = locale.indexOf('_');
String language;
String country = "";
if (idx < 0)
{ // "en"
language = locale;
}
else
{ // "en_US"
language = locale.substring(0, idx); // "en"
idx++; // "_"
int last = locale.indexOf('_', idx); // "US"
if (last < 0)
{
last = locale.length();
}
country = locale.substring(idx, last);
}
return new Locale(language, country);
}
private static final class BundleNameComparator implements Comparator
{
private final Locale locale;
BundleNameComparator( final Locale locale ) {
this.locale = locale;
}
public int compare( Bundle b1, Bundle b2 )
{
// the same bundles
if ( b1 == b2 || b1.getBundleId() == b2.getBundleId() )
{
return 0;
}
// special case for system bundle, which always is first
if ( b1.getBundleId() == 0 )
{
return -1;
}
else if ( b2.getBundleId() == 0 )
{
return 1;
}
// compare the symbolic names
int snComp = Util.getName( b1, locale ).compareToIgnoreCase( Util.getName( b2, locale ) );
if ( snComp != 0 )
{
return snComp;
}
// same names, compare versions
Version v1 = Version.parseVersion( ( String ) b1.getHeaders().get( Constants.BUNDLE_VERSION ) );
Version v2 = Version.parseVersion( ( String ) b2.getHeaders().get( Constants.BUNDLE_VERSION ) );
int vComp = v1.compareTo( v2 );
if ( vComp != 0 )
{
return vComp;
}
// same version ? Not really, but then, we compare by bundle id
if ( b1.getBundleId() < b2.getBundleId() )
{
return -1;
}
// b1 id must be > b2 id because equality is already checked
return 1;
}
}
public static void sendJsonOk(final HttpServletResponse response) throws IOException {
response.setContentType( "application/json" );
response.setCharacterEncoding( "UTF-8" );
response.getWriter().print( "{ \"status\": true }" );
}
/**
* 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.
*
* This method sets the Cache-Control
, Expires
,
* and Pragma
headers.
*
* @param response The response for which to set the cache prevention
*/
public static void setNoCache(final HttpServletResponse response) {
response.setHeader("Cache-Control", "no-cache");
response.addHeader("Cache-Control", "no-store");
response.addHeader("Cache-Control", "must-revalidate");
response.addHeader("Cache-Control", "max-age=0");
response.setHeader("Expires", "Thu, 01 Jan 1970 01:00:00 GMT");
response.setHeader("Pragma", "no-cache");
}
public static String getStringProperty( final ServiceReference> service, final String propertyName ) {
final Object property = service.getProperty( propertyName );
if ( property instanceof String ) {
return ( String ) property;
}
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 void sendRedirect(final HttpServletRequest request, final HttpServletResponse response, String redirectUrl)
throws IOException {
// check for relative URL
if ( !redirectUrl.startsWith("/") ) {
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()) : "";
}
if (!base.startsWith("/")) {
base = '/' + base;
}
redirectUrl = base + '/' + redirectUrl;
}
response.sendRedirect(redirectUrl);
}
@SuppressWarnings("rawtypes")
public static String[] toStringArray( final Object value ) {
if ( value instanceof String ) {
return new String[] { ( String ) value };
} else if ( value != null ) {
final Collection col;
if ( value.getClass().isArray() ) {
col = Arrays.asList( ( Object[] ) value );
} else if ( value instanceof Collection ) {
col = ( Collection ) value;
} else {
col = null;
}
if ( col != null && !col.isEmpty() ) {
final String[] entries = new String[col.size()];
int i = 0;
for (final Iterator cli = col.iterator(); cli.hasNext(); i++ ) {
entries[i] = String.valueOf( cli.next() );
}
return entries;
}
}
return null;
}
/**
* 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";
} else if (value.getClass().isArray()) {
final StringBuilder sb = new StringBuilder();
int len = Array.getLength(value);
sb.append('[');
for(int i = 0; i < len; ++i) {
final Object element = Array.get(value, i);
if (element instanceof Byte) {
sb.append("0x");
final String x = Integer.toHexString(((Byte)element).intValue() & 255);
if (1 == x.length()) {
sb.append('0');
}
sb.append(x);
} else {
sb.append(toString(element));
}
if (i < len - 1) {
sb.append(", ");
}
}
return sb.append(']').toString();
} else {
return value.toString();
}
}
public static String toString(final ServiceReference> ref) {
return "Service " + ref.getProperty(Constants.SERVICE_ID) + "(" + ref + ") from bundle " +
ref.getBundle().getSymbolicName() + ":" + ref.getBundle().getVersion() + "(" + ref.getBundle().getBundleId() + ")";
}
public static final String readTemplateFile( final Class> clazz, final String templateFile) throws IOException {
try(final InputStream templateStream = clazz.getResourceAsStream( templateFile )) {
if ( templateStream != null ) {
try ( final StringWriter w = new StringWriter()) {
final byte[] buf = new byte[2048];
int l;
while ( ( l = templateStream.read(buf)) > 0 ) {
w.write(new String(buf, 0, l, StandardCharsets.UTF_8));
}
String str = w.toString();
switch ( str.charAt(0) ) { // skip BOM
case 0xFEFF: // UTF-16/UTF-32, big-endian
case 0xFFFE: // UTF-16, little-endian
case 0xEFBB: // UTF-8
return str.substring(1);
}
return str;
}
}
}
throw new FileNotFoundException("Template " + templateFile + " not found");
}
/** Logger for the webconsole */
public static final Logger LOGGER = LoggerFactory.getLogger("org.apache.felix.webconsole");
}