http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/location/LocationAttributes.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/location/LocationAttributes.java b/core/src/main/java/com/opensymphony/xwork2/util/location/LocationAttributes.java new file mode 100644 index 0000000..5ca0934 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/location/LocationAttributes.java @@ -0,0 +1,348 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.location; + +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +/** + * A class to handle location information stored in attributes. + * These attributes are typically setup using {@link com.opensymphony.xwork2.util.location.LocationAttributes.Pipe} + * which augments the SAX stream with additional attributes, e.g.: + * <pre> + * <root xmlns:loc="http://struts.apache.org/xwork/location" + * loc:src="file://path/to/file.xml" + * loc:line="1" loc:column="1"> + * <foo loc:src="file://path/to/file.xml" loc:line="2" loc:column="3"/> + * </root> + * </pre> + * + * @see com.opensymphony.xwork2.util.location.LocationAttributes.Pipe + * @since 2.1.8 + * @version $Id$ + */ +public class LocationAttributes { + /** Prefix for the location namespace */ + public static final String PREFIX = "loc"; + /** Namespace URI for location attributes */ + public static final String URI = "http://struts.apache.org/xwork/location"; + + /** Attribute name for the location URI */ + public static final String SRC_ATTR = "src"; + /** Attribute name for the line number */ + public static final String LINE_ATTR = "line"; + /** Attribute name for the column number */ + public static final String COL_ATTR = "column"; + + /** Attribute qualified name for the location URI */ + public static final String Q_SRC_ATTR = "loc:src"; + /** Attribute qualified name for the line number */ + public static final String Q_LINE_ATTR = "loc:line"; + /** Attribute qualified name for the column number */ + public static final String Q_COL_ATTR = "loc:column"; + + // Private constructor, we only have static methods + private LocationAttributes() { + // Nothing + } + + /** + * Add location attributes to a set of SAX attributes. + * + * @param locator the <code>Locator</code> (can be null) + * @param attrs the <code>Attributes</code> where locator information should be added + * @return Location enabled Attributes. + */ + public static Attributes addLocationAttributes(Locator locator, Attributes attrs) { + if (locator == null || attrs.getIndex(URI, SRC_ATTR) != -1) { + // No location information known, or already has it + return attrs; + } + + // Get an AttributeImpl so that we can add new attributes. + AttributesImpl newAttrs = attrs instanceof AttributesImpl ? + (AttributesImpl)attrs : new AttributesImpl(attrs); + + newAttrs.addAttribute(URI, SRC_ATTR, Q_SRC_ATTR, "CDATA", locator.getSystemId()); + newAttrs.addAttribute(URI, LINE_ATTR, Q_LINE_ATTR, "CDATA", Integer.toString(locator.getLineNumber())); + newAttrs.addAttribute(URI, COL_ATTR, Q_COL_ATTR, "CDATA", Integer.toString(locator.getColumnNumber())); + + return newAttrs; + } + + /** + * Returns the {@link Location} of an element (SAX flavor). + * + * @param attrs the element's attributes that hold the location information + * @param description a description for the location (can be null) + * @return a {@link Location} object + */ + public static Location getLocation(Attributes attrs, String description) { + String src = attrs.getValue(URI, SRC_ATTR); + if (src == null) { + return Location.UNKNOWN; + } + + return new LocationImpl(description, src, getLine(attrs), getColumn(attrs)); + } + + /** + * Returns the location of an element (SAX flavor). If the location is to be kept + * into an object built from this element, consider using {@link #getLocation(Attributes, String)} + * and the {@link Locatable} interface. + * + * @param attrs the element's attributes that hold the location information + * @return a location string as defined by {@link Location}. + */ + public static String getLocationString(Attributes attrs) { + String src = attrs.getValue(URI, SRC_ATTR); + if (src == null) { + return LocationUtils.UNKNOWN_STRING; + } + + return src + ":" + attrs.getValue(URI, LINE_ATTR) + ":" + attrs.getValue(URI, COL_ATTR); + } + + /** + * Returns the URI of an element (SAX flavor) + * + * @param attrs the element's attributes that hold the location information + * @return the element's URI or "<code>[unknown location]</code>" if <code>attrs</code> + * has no location information. + */ + public static String getURI(Attributes attrs) { + String src = attrs.getValue(URI, SRC_ATTR); + return src != null ? src : LocationUtils.UNKNOWN_STRING; + } + + /** + * Returns the line number of an element (SAX flavor) + * + * @param attrs the element's attributes that hold the location information + * @return the element's line number or <code>-1</code> if <code>attrs</code> + * has no location information. + */ + public static int getLine(Attributes attrs) { + String line = attrs.getValue(URI, LINE_ATTR); + return line != null ? Integer.parseInt(line) : -1; + } + + /** + * Returns the column number of an element (SAX flavor) + * + * @param attrs the element's attributes that hold the location information + * @return the element's column number or <code>-1</code> if <code>attrs</code> + * has no location information. + */ + public static int getColumn(Attributes attrs) { + String col = attrs.getValue(URI, COL_ATTR); + return col != null ? Integer.parseInt(col) : -1; + } + + /** + * Returns the {@link Location} of an element (DOM flavor). + * + * @param elem the element that holds the location information + * @param description a description for the location (if <code>null</code>, the element's name is used) + * @return a {@link Location} object + */ + public static Location getLocation(Element elem, String description) { + Attr srcAttr = elem.getAttributeNodeNS(URI, SRC_ATTR); + if (srcAttr == null) { + return Location.UNKNOWN; + } + + return new LocationImpl(description == null ? elem.getNodeName() : description, + srcAttr.getValue(), getLine(elem), getColumn(elem)); + } + + /** + * Same as <code>getLocation(elem, null)</code>. + */ + public static Location getLocation(Element elem) { + return getLocation(elem, null); + } + + + /** + * Returns the location of an element that has been processed by this pipe (DOM flavor). + * If the location is to be kept into an object built from this element, consider using + * {@link #getLocation(Element)} and the {@link Locatable} interface. + * + * @param elem the element that holds the location information + * @return a location string as defined by {@link Location}. + */ + public static String getLocationString(Element elem) { + Attr srcAttr = elem.getAttributeNodeNS(URI, SRC_ATTR); + if (srcAttr == null) { + return LocationUtils.UNKNOWN_STRING; + } + + return srcAttr.getValue() + ":" + elem.getAttributeNS(URI, LINE_ATTR) + ":" + elem.getAttributeNS(URI, COL_ATTR); + } + + /** + * Returns the URI of an element (DOM flavor) + * + * @param elem the element that holds the location information + * @return the element's URI or "<code>[unknown location]</code>" if <code>elem</code> + * has no location information. + */ + public static String getURI(Element elem) { + Attr attr = elem.getAttributeNodeNS(URI, SRC_ATTR); + return attr != null ? attr.getValue() : LocationUtils.UNKNOWN_STRING; + } + + /** + * Returns the line number of an element (DOM flavor) + * + * @param elem the element that holds the location information + * @return the element's line number or <code>-1</code> if <code>elem</code> + * has no location information. + */ + public static int getLine(Element elem) { + Attr attr = elem.getAttributeNodeNS(URI, LINE_ATTR); + return attr != null ? Integer.parseInt(attr.getValue()) : -1; + } + + /** + * Returns the column number of an element (DOM flavor) + * + * @param elem the element that holds the location information + * @return the element's column number or <code>-1</code> if <code>elem</code> + * has no location information. + */ + public static int getColumn(Element elem) { + Attr attr = elem.getAttributeNodeNS(URI, COL_ATTR); + return attr != null ? Integer.parseInt(attr.getValue()) : -1; + } + + /** + * Remove the location attributes from a DOM element. + * + * @param elem the element to remove the location attributes from. + * @param recurse if <code>true</code>, also remove location attributes on descendant elements. + */ + public static void remove(Element elem, boolean recurse) { + elem.removeAttributeNS(URI, SRC_ATTR); + elem.removeAttributeNS(URI, LINE_ATTR); + elem.removeAttributeNS(URI, COL_ATTR); + if (recurse) { + NodeList children = elem.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + remove((Element)child, recurse); + } + } + } + } + + /** + * A SAX filter that adds the information available from the <code>Locator</code> as attributes. + * The purpose of having location as attributes is to allow this information to survive transformations + * of the document (an XSL could copy these attributes over) or conversion of SAX events to a DOM. + * <p> + * The location is added as 3 attributes in a specific namespace to each element. + * <pre> + * <root xmlns:loc="http://opensymphony.com/xwork/location" + * loc:src="file://path/to/file.xml" + * loc:line="1" loc:column="1"> + * <foo loc:src="file://path/to/file.xml" loc:line="2" loc:column="3"/> + * </root> + * </pre> + * <strong>Note:</strong> Although this adds a lot of information to the serialized form of the document, + * the overhead in SAX events is not that big, as attribute names are interned, and all <code>src</code> + * attributes point to the same string. + * + * @see com.opensymphony.xwork2.util.location.LocationAttributes + */ + public static class Pipe implements ContentHandler { + + private Locator locator; + + private ContentHandler nextHandler; + + /** + * Create a filter. It has to be chained to another handler to be really useful. + */ + public Pipe() { + } + + /** + * Create a filter that is chained to another handler. + * @param next the next handler in the chain. + */ + public Pipe(ContentHandler next) { + nextHandler = next; + } + + public void setDocumentLocator(Locator locator) { + this.locator = locator; + nextHandler.setDocumentLocator(locator); + } + + public void startDocument() throws SAXException { + nextHandler.startDocument(); + nextHandler.startPrefixMapping(LocationAttributes.PREFIX, LocationAttributes.URI); + } + + public void endDocument() throws SAXException { + endPrefixMapping(LocationAttributes.PREFIX); + nextHandler.endDocument(); + } + + public void startElement(String uri, String loc, String raw, Attributes attrs) throws SAXException { + // Add location attributes to the element + nextHandler.startElement(uri, loc, raw, LocationAttributes.addLocationAttributes(locator, attrs)); + } + + public void endElement(String arg0, String arg1, String arg2) throws SAXException { + nextHandler.endElement(arg0, arg1, arg2); + } + + public void startPrefixMapping(String arg0, String arg1) throws SAXException { + nextHandler.startPrefixMapping(arg0, arg1); + } + + public void endPrefixMapping(String arg0) throws SAXException { + nextHandler.endPrefixMapping(arg0); + } + + public void characters(char[] arg0, int arg1, int arg2) throws SAXException { + nextHandler.characters(arg0, arg1, arg2); + } + + public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException { + nextHandler.ignorableWhitespace(arg0, arg1, arg2); + } + + public void processingInstruction(String arg0, String arg1) throws SAXException { + nextHandler.processingInstruction(arg0, arg1); + } + + public void skippedEntity(String arg0) throws SAXException { + nextHandler.skippedEntity(arg0); + } + } +}
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/location/LocationImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/location/LocationImpl.java b/core/src/main/java/com/opensymphony/xwork2/util/location/LocationImpl.java new file mode 100644 index 0000000..ca101ca --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/location/LocationImpl.java @@ -0,0 +1,216 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.location; + +import org.apache.commons.lang3.StringUtils; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Serializable; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * A simple immutable and serializable implementation of {@link Location}. + */ +public class LocationImpl implements Location, Serializable { + + private final String uri; + private final int line; + private final int column; + private final String description; + + // Package private: outside this package, use Location.UNKNOWN. + static final LocationImpl UNKNOWN = new LocationImpl(null, null, -1, -1); + + /** + * Build a location for a given URI, with unknown line and column numbers. + * + * @param uri the resource URI + */ + public LocationImpl(String description, String uri) { + this(description, uri, -1, -1); + } + + /** + * Build a location for a given URI and line and column numbers. + * + * @param uri the resource URI + * @param line the line number (starts at 1) + * @param column the column number (starts at 1) + */ + public LocationImpl(String description, String uri, int line, int column) { + if (StringUtils.isEmpty(uri)) { + this.uri = null; + this.line = -1; + this.column = -1; + } else { + this.uri = uri; + this.line = line; + this.column = column; + } + this.description = StringUtils.trimToNull(description); + } + + /** + * Copy constructor. + * + * @param location the location to be copied + */ + public LocationImpl(Location location) { + this(location.getDescription(), location.getURI(), location.getLineNumber(), location.getColumnNumber()); + } + + /** + * Create a location from an existing one, but with a different description + */ + public LocationImpl(String description, Location location) { + this(description, location.getURI(), location.getLineNumber(), location.getColumnNumber()); + } + + /** + * Obtain a <code>LocationImpl</code> from a {@link Location}. If <code>location</code> is + * already a <code>LocationImpl</code>, it is returned, otherwise it is copied. + * <p> + * This method is useful when an immutable and serializable location is needed, such as in locatable + * exceptions. + * + * @param location the location + * @return an immutable and serializable version of <code>location</code> + */ + public static LocationImpl get(Location location) { + if (location instanceof LocationImpl) { + return (LocationImpl)location; + } else if (location == null) { + return UNKNOWN; + } else { + return new LocationImpl(location); + } + } + + /** + * Get the description of this location + * + * @return the description (can be <code>null</code>) + */ + public String getDescription() { + return this.description; + } + + /** + * Get the URI of this location + * + * @return the URI (<code>null</code> if unknown). + */ + public String getURI() { + return this.uri; + } + + /** + * Get the line number of this location + * + * @return the line number (<code>-1</code> if unknown) + */ + public int getLineNumber() { + return this.line; + } + + /** + * Get the column number of this location + * + * @return the column number (<code>-1</code> if unknown) + */ + public int getColumnNumber() { + return this.column; + } + + /** + * Gets a source code snippet with the default padding + * + * @param padding The amount of lines before and after the error to include + */ + public List<String> getSnippet(int padding) { + List<String> snippet = new ArrayList<>(); + if (getLineNumber() > 0) { + try { + InputStream in = new URL(getURI()).openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + + int lineno = 0; + int errno = getLineNumber(); + String line; + while ((line = reader.readLine()) != null) { + lineno++; + if (lineno >= errno - padding && lineno <= errno + padding) { + snippet.add(line); + } + } + } catch (Exception ex) { + // ignoring as snippet not available isn't a big deal + } + } + return snippet; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof Location) { + Location other = (Location)obj; + return this.line == other.getLineNumber() && this.column == other.getColumnNumber() + && testEquals(this.uri, other.getURI()) + && testEquals(this.description, other.getDescription()); + } + + return false; + } + + @Override + public int hashCode() { + int hash = line ^ column; + if (uri != null) hash ^= uri.hashCode(); + if (description != null) hash ^= description.hashCode(); + + return hash; + } + + @Override + public String toString() { + return LocationUtils.toString(this); + } + + /** + * Ensure serialized unknown location resolve to {@link Location#UNKNOWN}. + */ + private Object readResolve() { + return this.equals(Location.UNKNOWN) ? Location.UNKNOWN : this; + } + + private boolean testEquals(Object object1, Object object2) { + if (object1 == object2) { + return true; + } + if ((object1 == null) || (object2 == null)) { + return false; + } + return object1.equals(object2); + } +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/location/LocationUtils.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/location/LocationUtils.java b/core/src/main/java/com/opensymphony/xwork2/util/location/LocationUtils.java new file mode 100644 index 0000000..892d3c7 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/location/LocationUtils.java @@ -0,0 +1,305 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.location; + +import com.opensymphony.xwork2.util.ClassLoaderUtil; +import org.w3c.dom.Element; +import org.xml.sax.Locator; +import org.xml.sax.SAXParseException; + +import javax.xml.transform.SourceLocator; +import javax.xml.transform.TransformerException; +import java.lang.ref.WeakReference; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * Location-related utility methods. + */ +public class LocationUtils { + + /** + * The string representation of an unknown location: "<code>[unknown location]</code>". + */ + public static final String UNKNOWN_STRING = "[unknown location]"; + + private static List<WeakReference<LocationFinder>> finders = new ArrayList<>(); + + /** + * An finder or object locations + */ + public interface LocationFinder { + /** + * Get the location of an object + * @param obj the object for which to find a location + * @param description and optional description to be added to the object's location + * @return the object's location or <code>null</code> if object's class isn't handled + * by this finder. + */ + Location getLocation(Object obj, String description); + } + + private LocationUtils() { + // Forbid instanciation + } + + /** + * Builds a string representation of a location, in the + * "<code><em>descripton</em> - <em>uri</em>:<em>line</em>:<em>column</em></code>" + * format (e.g. "<code>foo - file://path/to/file.xml:3:40</code>"). For {@link Location#UNKNOWN an unknown location}, returns + * {@link #UNKNOWN_STRING}. + * + * @return the string representation + */ + public static String toString(Location location) { + StringBuilder result = new StringBuilder(); + + String description = location.getDescription(); + if (description != null) { + result.append(description).append(" - "); + } + + String uri = location.getURI(); + if (uri != null) { + result.append(uri).append(':').append(location.getLineNumber()).append(':').append(location.getColumnNumber()); + } else { + result.append(UNKNOWN_STRING); + } + + return result.toString(); + } + + /** + * Parse a location string of the form "<code><em>uri</em>:<em>line</em>:<em>column</em></code>" (e.g. + * "<code>path/to/file.xml:3:40</code>") to a Location object. Additionally, a description may + * also optionally be present, separated with an hyphen (e.g. "<code>foo - path/to/file.xml:3.40</code>"). + * + * @param text the text to parse + * @return the location (possibly <code>null</code> if text was null or in an incorrect format) + */ + public static LocationImpl parse(String text) throws IllegalArgumentException { + if (text == null || text.length() == 0) { + return null; + } + + // Do we have a description? + String description; + int uriStart = text.lastIndexOf(" - "); // lastIndexOf to allow the separator to be in the description + if (uriStart > -1) { + description = text.substring(0, uriStart); + uriStart += 3; // strip " - " + } else { + description = null; + uriStart = 0; + } + + try { + int colSep = text.lastIndexOf(':'); + if (colSep > -1) { + int column = Integer.parseInt(text.substring(colSep + 1)); + + int lineSep = text.lastIndexOf(':', colSep - 1); + if (lineSep > -1) { + int line = Integer.parseInt(text.substring(lineSep + 1, colSep)); + return new LocationImpl(description, text.substring(uriStart, lineSep), line, column); + } + } else { + // unkonwn? + if (text.endsWith(UNKNOWN_STRING)) { + return LocationImpl.UNKNOWN; + } + } + } catch(Exception e) { + // Ignore: handled below + } + + return LocationImpl.UNKNOWN; + } + + /** + * Checks if a location is known, i.e. it is not null nor equal to {@link Location#UNKNOWN}. + * + * @param location the location to check + * @return <code>true</code> if the location is known + */ + public static boolean isKnown(Location location) { + return location != null && !Location.UNKNOWN.equals(location); + } + + /** + * Checks if a location is unknown, i.e. it is either null or equal to {@link Location#UNKNOWN}. + * + * @param location the location to check + * @return <code>true</code> if the location is unknown + */ + public static boolean isUnknown(Location location) { + return location == null || Location.UNKNOWN.equals(location); + } + + /** + * Add a {@link LocationFinder} to the list of finders that will be queried for an object's + * location by {@link #getLocation(Object, String)}. + * <p> + * <b>Important:</b> LocationUtils internally stores a weak reference to the finder. This + * avoids creating strong links between the classloader holding this class and the finder's + * classloader, which can cause some weird memory leaks if the finder's classloader is to + * be reloaded. Therefore, you <em>have</em> to keep a strong reference to the finder in the + * calling code, e.g.: + * <pre> + * private static LocationUtils.LocationFinder myFinder = + * new LocationUtils.LocationFinder() { + * public Location getLocation(Object obj, String desc) { + * ... + * } + * }; + * + * static { + * LocationUtils.addFinder(myFinder); + * } + * </pre> + * + * @param finder the location finder to add + */ + public static void addFinder(LocationFinder finder) { + if (finder == null) { + return; + } + + synchronized(LocationFinder.class) { + // Update a clone of the current finder list to avoid breaking + // any iteration occuring in another thread. + List<WeakReference<LocationFinder>> newFinders = new ArrayList<>(finders); + newFinders.add(new WeakReference<LocationFinder>(finder)); + finders = newFinders; + } + } + + /** + * Get the location of an object. Some well-known located classes built in the JDK are handled + * by this method. Handling of other located classes can be handled by adding new location finders. + * + * @param obj the object of which to get the location + * @return the object's location, or {@link Location#UNKNOWN} if no location could be found + */ + public static Location getLocation(Object obj) { + return getLocation(obj, null); + } + + /** + * Get the location of an object. Some well-known located classes built in the JDK are handled + * by this method. Handling of other located classes can be handled by adding new location finders. + * + * @param obj the object of which to get the location + * @param description an optional description of the object's location, used if a Location object + * has to be created. + * @return the object's location, or {@link Location#UNKNOWN} if no location could be found + */ + public static Location getLocation(Object obj, String description) { + if (obj instanceof Location) { + return (Location) obj; + } + + if (obj instanceof Locatable) { + return ((Locatable)obj).getLocation(); + } + + // Check some well-known locatable exceptions + if (obj instanceof SAXParseException) { + SAXParseException spe = (SAXParseException)obj; + if (spe.getSystemId() != null) { + return new LocationImpl(description, spe.getSystemId(), spe.getLineNumber(), spe.getColumnNumber()); + } else { + return Location.UNKNOWN; + } + } + + if (obj instanceof TransformerException) { + TransformerException ex = (TransformerException)obj; + SourceLocator locator = ex.getLocator(); + if (locator != null && locator.getSystemId() != null) { + return new LocationImpl(description, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber()); + } else { + return Location.UNKNOWN; + } + } + + if (obj instanceof Locator) { + Locator locator = (Locator)obj; + if (locator.getSystemId() != null) { + return new LocationImpl(description, locator.getSystemId(), locator.getLineNumber(), locator.getColumnNumber()); + } else { + return Location.UNKNOWN; + } + } + + if (obj instanceof Element) { + return LocationAttributes.getLocation((Element)obj); + } + + List<WeakReference<LocationFinder>> currentFinders = finders; // Keep the current list + int size = currentFinders.size(); + for (int i = 0; i < size; i++) { + WeakReference<LocationFinder> ref = currentFinders.get(i); + LocationFinder finder = ref.get(); + if (finder == null) { + // This finder was garbage collected: update finders + synchronized(LocationFinder.class) { + // Update a clone of the current list to avoid breaking current iterations + List<WeakReference<LocationFinder>> newFinders = new ArrayList<>(finders); + newFinders.remove(ref); + finders = newFinders; + } + } else { + Location result = finder.getLocation(obj, description); + if (result != null) { + return result; + } + } + } + + if (obj instanceof Throwable) { + Throwable t = (Throwable) obj; + StackTraceElement[] stack = t.getStackTrace(); + if (stack != null && stack.length > 0) { + StackTraceElement trace = stack[0]; + if (trace.getLineNumber() >= 0) { + String uri = trace.getClassName(); + if (trace.getFileName() != null) { + uri = uri.replace('.','/'); + uri = uri.substring(0, uri.lastIndexOf('/') + 1); + uri = uri + trace.getFileName(); + URL url = ClassLoaderUtil.getResource(uri, LocationUtils.class); + if (url != null) { + uri = url.toString(); + } + } + if (description == null) { + StringBuilder sb = new StringBuilder(); + sb.append("Class: ").append(trace.getClassName()).append("\n"); + sb.append("File: ").append(trace.getFileName()).append("\n"); + sb.append("Method: ").append(trace.getMethodName()).append("\n"); + sb.append("Line: ").append(trace.getLineNumber()); + description = sb.toString(); + } + return new LocationImpl(description, uri, trace.getLineNumber(), -1); + } + } + } + + return Location.UNKNOWN; + } +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/location/package.html ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/location/package.html b/core/src/main/java/com/opensymphony/xwork2/util/location/package.html new file mode 100644 index 0000000..840814b --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/location/package.html @@ -0,0 +1,3 @@ +<html> + <body>Classes and utilities used to track location information.</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/Logger.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/Logger.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/Logger.java new file mode 100644 index 0000000..e7da798 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/Logger.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging; + +/** + * Main logger interface for logging things + */ +@Deprecated +public interface Logger { + + void trace(String msg, String... args); + + void trace(String msg, Object... args); + + void trace(String msg, Throwable ex, String... args); + + boolean isTraceEnabled(); + + void debug(String msg, String... args); + + void debug(String msg, Object... args); + + void debug(String msg, Throwable ex, String... args); + + boolean isDebugEnabled(); + + void info(String msg, String... args); + + void info(String msg, Throwable ex, String... args); + + boolean isInfoEnabled(); + + void warn(String msg, String... args); + + void warn(String msg, Object... args); + + void warn(String msg, Throwable ex, String... args); + + boolean isWarnEnabled(); + + void error(String msg, String... args); + + void error(String msg, Object... args); + + void error(String msg, Throwable ex, String... args); + + boolean isErrorEnabled(); + + void fatal(String msg, String... args); + + void fatal(String msg, Throwable ex, String... args); + + boolean isFatalEnabled(); + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/LoggerFactory.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/LoggerFactory.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/LoggerFactory.java new file mode 100644 index 0000000..29b70df --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/LoggerFactory.java @@ -0,0 +1,137 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging; + +import com.opensymphony.xwork2.XWorkConstants; +import com.opensymphony.xwork2.XWorkException; +import com.opensymphony.xwork2.util.logging.commons.CommonsLoggerFactory; +import com.opensymphony.xwork2.util.logging.jdk.JdkLoggerFactory; +import com.opensymphony.xwork2.util.logging.log4j2.Log4j2LoggerFactory; +import com.opensymphony.xwork2.util.logging.slf4j.Slf4jLoggerFactory; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Creates loggers. Static accessor will lazily try to decide on the best factory if none specified. + */ +@Deprecated +public abstract class LoggerFactory { + + private static final ReadWriteLock lock = new ReentrantReadWriteLock(); + private static LoggerFactory factory; + + private static final List<LoggerClass> loggers = new LinkedList<LoggerClass>(){ + { + add(new LoggerClass<CommonsLoggerFactory>("org.apache.commons.logging.LogFactory", CommonsLoggerFactory.class)); + add(new LoggerClass<Slf4jLoggerFactory>("org.slf4j.LoggerFactory", Slf4jLoggerFactory.class)); + add(new LoggerClass<Log4j2LoggerFactory>("org.apache.logging.log4j.LogManager", Log4j2LoggerFactory.class)); + } + }; + + public static void setLoggerFactory(LoggerFactory factory) { + lock.writeLock().lock(); + try { + LoggerFactory.factory = factory; + } finally { + lock.writeLock().unlock(); + } + } + + public static Logger getLogger(Class<?> cls) { + return getLoggerFactory().getLoggerImpl(cls); + } + + public static Logger getLogger(String name) { + return getLoggerFactory().getLoggerImpl(name); + } + + protected static LoggerFactory getLoggerFactory() { + lock.readLock().lock(); + try { + if (factory != null) { + return factory; + } + } finally { + lock.readLock().unlock(); + } + lock.writeLock().lock(); + try { + if (factory == null) { + createLoggerFactory(); + } + return factory; + } finally { + lock.writeLock().unlock(); + } + } + + private static void createLoggerFactory() { + String userLoggerFactory = System.getProperty(XWorkConstants.XWORK_LOGGER_FACTORY); + if (userLoggerFactory != null) { + try { + Class clazz = Class.forName(userLoggerFactory); + factory = (LoggerFactory) clazz.newInstance(); + } catch (Exception e) { + throw new XWorkException("System property [" + XWorkConstants.XWORK_LOGGER_FACTORY + + "] was defined as [" + userLoggerFactory + "] but there is a problem to use that LoggerFactory!", e); + } + } else { + factory = new JdkLoggerFactory(); + for (LoggerClass logger : loggers) { + if (logger.isSupported()) { + factory = logger.createInstance(); + break; + } + } + } + } + + protected abstract Logger getLoggerImpl(Class<?> cls); + + protected abstract Logger getLoggerImpl(String name); + + private static class LoggerClass<T extends LoggerFactory> { + + private final String loggerClazzName; + private final Class<T> loggerImplClazz; + + public LoggerClass(String loggerClazzName, Class<T> loggerImplClazz) { + this.loggerClazzName = loggerClazzName; + this.loggerImplClazz = loggerImplClazz; + } + + public boolean isSupported() { + try { + Class.forName(loggerClazzName); + return true; + } catch (ClassNotFoundException ignore) { + return false; + } + } + + public LoggerFactory createInstance() { + try { + return loggerImplClazz.newInstance(); + } catch (Exception e) { + throw new XWorkException(e); + } + } + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/LoggerUtils.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/LoggerUtils.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/LoggerUtils.java new file mode 100644 index 0000000..a513ed2 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/LoggerUtils.java @@ -0,0 +1,84 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging; + +import java.util.LinkedList; +import java.util.List; + +/** + * Logging utility methods + */ +@Deprecated +public class LoggerUtils { + + /** + * Formats messages using parameters. For example, the call: + * + * <pre> + * format("foo #0 #1", "bob", "joe"); + * </pre> + * + * will return: + * <pre> + * foo bob joe + * </pre> + * + * @param msg The message + * @param args A list of arguments. A maximum of 10 are supported. + * @return The formatted string + */ + public static String format(String msg, String... args) { + if (msg != null && msg.length() > 0 && msg.indexOf('#') > -1) { + StringBuilder sb = new StringBuilder(); + boolean isArg = false; + for (int x = 0; x < msg.length(); x++) { + char c = msg.charAt(x); + if (isArg) { + isArg = false; + if (Character.isDigit(c)) { + int val = Character.getNumericValue(c); + if (val >= 0 && val < args.length) { + sb.append(args[val]); + continue; + } + } + sb.append('#'); + } + if (c == '#') { + isArg = true; + continue; + } + sb.append(c); + } + + if (isArg) { + sb.append('#'); + } + return sb.toString(); + } + return msg; + + } + + public static String format(String msg, Object[] args) { + List<String> strArgs = new LinkedList<String>(); + for (Object arg : args) { + strArgs.add(arg != null ? arg.toString() : "(null)"); + } + return format(msg, strArgs.toArray(new String[strArgs.size()])); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/commons/CommonsLogger.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/commons/CommonsLogger.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/commons/CommonsLogger.java new file mode 100644 index 0000000..6ee4e0f --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/commons/CommonsLogger.java @@ -0,0 +1,125 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging.commons; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerUtils; +import org.apache.commons.logging.Log; + +/** + * Simple logger that delegates to commons logging + */ +@Deprecated +public class CommonsLogger implements Logger { + + private Log log; + + public CommonsLogger(Log log) { + this.log = log; + } + + public void error(String msg, String... args) { + log.error(LoggerUtils.format(msg, args)); + } + + public void error(String msg, Object... args) { + log.error(LoggerUtils.format(msg, args)); + } + + public void error(String msg, Throwable ex, String... args) { + log.error(LoggerUtils.format(msg, args), ex); + } + + public void info(String msg, String... args) { + log.info(LoggerUtils.format(msg, args)); + } + + public void info(String msg, Throwable ex, String... args) { + log.info(LoggerUtils.format(msg, args), ex); + } + + + + public boolean isInfoEnabled() { + return log.isInfoEnabled(); + } + + public void warn(String msg, String... args) { + log.warn(LoggerUtils.format(msg, args)); + } + + public void warn(String msg, Object... args) { + log.warn(LoggerUtils.format(msg, args)); + } + + public void warn(String msg, Throwable ex, String... args) { + log.warn(LoggerUtils.format(msg, args), ex); + } + + public boolean isDebugEnabled() { + return log.isDebugEnabled(); + } + + public void debug(String msg, String... args) { + log.debug(LoggerUtils.format(msg, args)); + } + + public void debug(String msg, Object... args) { + log.debug(LoggerUtils.format(msg, args)); + } + + public void debug(String msg, Throwable ex, String... args) { + log.debug(LoggerUtils.format(msg, args), ex); + } + + public boolean isTraceEnabled() { + return log.isTraceEnabled(); + } + + public void trace(String msg, String... args) { + log.trace(LoggerUtils.format(msg, args)); + } + + public void trace(String msg, Object... args) { + log.trace(LoggerUtils.format(msg, args)); + } + + public void trace(String msg, Throwable ex, String... args) { + log.trace(LoggerUtils.format(msg, args), ex); + } + + + public void fatal(String msg, String... args) { + log.fatal(LoggerUtils.format(msg, args)); + } + + public void fatal(String msg, Throwable ex, String... args) { + log.fatal(LoggerUtils.format(msg, args), ex); + } + + public boolean isErrorEnabled() { + return log.isErrorEnabled(); + } + + public boolean isFatalEnabled() { + return log.isFatalEnabled(); + } + + public boolean isWarnEnabled() { + return log.isWarnEnabled(); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/commons/CommonsLoggerFactory.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/commons/CommonsLoggerFactory.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/commons/CommonsLoggerFactory.java new file mode 100644 index 0000000..0e02293 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/commons/CommonsLoggerFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging.commons; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.logging.LogFactory; + +/** + * Creates commons-logging-backed loggers + */ +@Deprecated +public class CommonsLoggerFactory extends LoggerFactory { + + @Override + protected Logger getLoggerImpl(Class<?> cls) { + return new CommonsLogger(LogFactory.getLog(cls)); + } + + @Override + protected Logger getLoggerImpl(String name) { + return new CommonsLogger(LogFactory.getLog(name)); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/jdk/JdkLogger.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/jdk/JdkLogger.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/jdk/JdkLogger.java new file mode 100644 index 0000000..1a11346 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/jdk/JdkLogger.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging.jdk; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerUtils; + +import java.util.logging.Level; + +/** + * Delegates to jdk logger. Maps fatal to Level.SEVERE along with error. + */ +@Deprecated +public class JdkLogger implements Logger { + + private java.util.logging.Logger log; + + public JdkLogger(java.util.logging.Logger log) { + this.log = log; + } + + public void error(String msg, String... args) { + log.log(Level.SEVERE, LoggerUtils.format(msg, args)); + } + + public void error(String msg, Object... args) { + log.log(Level.SEVERE, LoggerUtils.format(msg, args)); + } + + public void error(String msg, Throwable ex, String... args) { + log.log(Level.SEVERE, LoggerUtils.format(msg, args), ex); + } + + public void fatal(String msg, String... args) { + log.log(Level.SEVERE, LoggerUtils.format(msg, args)); + } + + public void fatal(String msg, Throwable ex, String... args) { + log.log(Level.SEVERE, LoggerUtils.format(msg, args), ex); + } + + public void info(String msg, String... args) { + log.log(Level.INFO, LoggerUtils.format(msg, args)); + } + + public void info(String msg, Throwable ex, String... args) { + log.log(Level.INFO, LoggerUtils.format(msg, args), ex); + } + + public boolean isInfoEnabled() { + return log.isLoggable(Level.INFO); + } + + public void warn(String msg, String... args) { + log.log(Level.WARNING, LoggerUtils.format(msg, args)); + } + + public void warn(String msg, Object... args) { + log.log(Level.WARNING, LoggerUtils.format(msg, args)); + } + + public void warn(String msg, Throwable ex, String... args) { + log.log(Level.WARNING, LoggerUtils.format(msg, args), ex); + } + + public boolean isDebugEnabled() { + return log.isLoggable(Level.FINE); + } + + public void debug(String msg, String... args) { + log.log(Level.FINE, LoggerUtils.format(msg, args)); + } + + public void debug(String msg, Object... args) { + log.log(Level.FINE, LoggerUtils.format(msg, args)); + } + + public void debug(String msg, Throwable ex, String... args) { + log.log(Level.FINE, LoggerUtils.format(msg, args), ex); + } + + public boolean isTraceEnabled() { + return log.isLoggable(Level.FINEST); + } + + public void trace(String msg, String... args) { + log.log(Level.FINEST, LoggerUtils.format(msg, args)); + } + + public void trace(String msg, Object... args) { + log.log(Level.FINEST, LoggerUtils.format(msg, args)); + } + + public void trace(String msg, Throwable ex, String... args) { + log.log(Level.FINEST, LoggerUtils.format(msg, args), ex); + } + + public boolean isErrorEnabled() { + return log.isLoggable(Level.SEVERE); + } + + public boolean isFatalEnabled() { + return log.isLoggable(Level.SEVERE); + } + + public boolean isWarnEnabled() { + return log.isLoggable(Level.WARNING); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/jdk/JdkLoggerFactory.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/jdk/JdkLoggerFactory.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/jdk/JdkLoggerFactory.java new file mode 100644 index 0000000..dbf719c --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/jdk/JdkLoggerFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging.jdk; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +/** + * Creates jdk loggers + */ +@Deprecated +public class JdkLoggerFactory extends LoggerFactory { + + @Override + protected Logger getLoggerImpl(Class<?> cls) { + return new JdkLogger(java.util.logging.Logger.getLogger(cls.getName())); + } + + @Override + protected Logger getLoggerImpl(String name) { + return new JdkLogger(java.util.logging.Logger.getLogger(name)); + } +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/log4j2/Log4j2Logger.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/log4j2/Log4j2Logger.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/log4j2/Log4j2Logger.java new file mode 100644 index 0000000..228a860 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/log4j2/Log4j2Logger.java @@ -0,0 +1,122 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging.log4j2; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerUtils; + +/** + * Simple logger that delegates to log4j2 logging + */ +@Deprecated +public class Log4j2Logger implements Logger { + + private org.apache.logging.log4j.Logger log; + + public Log4j2Logger(org.apache.logging.log4j.Logger log) { + this.log = log; + } + + public void error(String msg, String... args) { + log.error(LoggerUtils.format(msg, args)); + } + + public void error(String msg, Object... args) { + log.error(LoggerUtils.format(msg, args)); + } + + public void error(String msg, Throwable ex, String... args) { + log.error(LoggerUtils.format(msg, args), ex); + } + + public void info(String msg, String... args) { + log.info(LoggerUtils.format(msg, args)); + } + + public void info(String msg, Throwable ex, String... args) { + log.info(LoggerUtils.format(msg, args), ex); + } + + public boolean isInfoEnabled() { + return log.isInfoEnabled(); + } + + public void warn(String msg, String... args) { + log.warn(LoggerUtils.format(msg, args)); + } + + public void warn(String msg, Object... args) { + log.warn(LoggerUtils.format(msg, args)); + } + + public void warn(String msg, Throwable ex, String... args) { + log.warn(LoggerUtils.format(msg, args), ex); + } + + public boolean isDebugEnabled() { + return log.isDebugEnabled(); + } + + public void debug(String msg, String... args) { + log.debug(LoggerUtils.format(msg, args)); + } + + public void debug(String msg, Object... args) { + log.debug(LoggerUtils.format(msg, args)); + } + + public void debug(String msg, Throwable ex, String... args) { + log.debug(LoggerUtils.format(msg, args), ex); + } + + public boolean isTraceEnabled() { + return log.isTraceEnabled(); + } + + public void trace(String msg, String... args) { + log.trace(LoggerUtils.format(msg, args)); + } + + public void trace(String msg, Object... args) { + log.trace(LoggerUtils.format(msg, args)); + } + + public void trace(String msg, Throwable ex, String... args) { + log.trace(LoggerUtils.format(msg, args), ex); + } + + + public void fatal(String msg, String... args) { + log.fatal(LoggerUtils.format(msg, args)); + } + + public void fatal(String msg, Throwable ex, String... args) { + log.fatal(LoggerUtils.format(msg, args), ex); + } + + public boolean isErrorEnabled() { + return log.isErrorEnabled(); + } + + public boolean isFatalEnabled() { + return log.isFatalEnabled(); + } + + public boolean isWarnEnabled() { + return log.isWarnEnabled(); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/log4j2/Log4j2LoggerFactory.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/log4j2/Log4j2LoggerFactory.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/log4j2/Log4j2LoggerFactory.java new file mode 100644 index 0000000..dd2799d --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/log4j2/Log4j2LoggerFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging.log4j2; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +/** + * Creates log4j2-logging-backed loggers + * + * You can use the same to explicit tell the framework which implementation to use and don't depend on class discovery: + * <pre> + * -Dxwork.loggerFactory=com.opensymphony.xwork2.util.logging.log4j2.Log4j2LoggerFactory + * </pre> + */ +@Deprecated +public class Log4j2LoggerFactory extends LoggerFactory { + + @Override + protected Logger getLoggerImpl(Class<?> cls) { + return new Log4j2Logger(org.apache.logging.log4j.LogManager.getLogger(cls)); + } + + @Override + protected Logger getLoggerImpl(String name) { + return new Log4j2Logger(org.apache.logging.log4j.LogManager.getLogger(name)); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/slf4j/Slf4jLogger.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/slf4j/Slf4jLogger.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/slf4j/Slf4jLogger.java new file mode 100644 index 0000000..a303032 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/slf4j/Slf4jLogger.java @@ -0,0 +1,123 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging.slf4j; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerUtils; + +/** + * Simple logger that delegates to slf4j logging + */ +@Deprecated +public class Slf4jLogger implements Logger { + + private org.slf4j.Logger log; + + public Slf4jLogger(org.slf4j.Logger log) { + this.log = log; + } + + public void error(String msg, String... args) { + log.error(LoggerUtils.format(msg, args)); + } + + public void error(String msg, Object... args) { + log.error(LoggerUtils.format(msg, args)); + } + + public void error(String msg, Throwable ex, String... args) { + log.error(LoggerUtils.format(msg, args), ex); + } + + public void info(String msg, String... args) { + log.info(LoggerUtils.format(msg, args)); + } + + public void info(String msg, Throwable ex, String... args) { + log.info(LoggerUtils.format(msg, args), ex); + } + + public boolean isInfoEnabled() { + return log.isInfoEnabled(); + } + + public void warn(String msg, String... args) { + log.warn(LoggerUtils.format(msg, args)); + } + + public void warn(String msg, Object... args) { + log.warn(LoggerUtils.format(msg, args)); + } + + public void warn(String msg, Throwable ex, String... args) { + log.warn(LoggerUtils.format(msg, args), ex); + } + + public boolean isDebugEnabled() { + return log.isDebugEnabled(); + } + + public void debug(String msg, String... args) { + log.debug(LoggerUtils.format(msg, args)); + } + + public void debug(String msg, Object... args) { + log.debug(LoggerUtils.format(msg, args)); + } + + public void debug(String msg, Throwable ex, String... args) { + log.debug(LoggerUtils.format(msg, args), ex); + } + + public boolean isTraceEnabled() { + return log.isTraceEnabled(); + } + + public void trace(String msg, String... args) { + log.trace(LoggerUtils.format(msg, args)); + } + + public void trace(String msg, Object... args) { + log.trace(LoggerUtils.format(msg, args)); + } + + public void trace(String msg, Throwable ex, String... args) { + log.trace(LoggerUtils.format(msg, args), ex); + } + + + public void fatal(String msg, String... args) { + log.error(LoggerUtils.format(msg, args)); + } + + public void fatal(String msg, Throwable ex, String... args) { + log.error(LoggerUtils.format(msg, args), ex); + } + + public boolean isErrorEnabled() { + return log.isErrorEnabled(); + } + + /** Fatal is not support by Slf4j */ + public boolean isFatalEnabled() { + return log.isErrorEnabled(); + } + + public boolean isWarnEnabled() { + return log.isWarnEnabled(); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/logging/slf4j/Slf4jLoggerFactory.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/logging/slf4j/Slf4jLoggerFactory.java b/core/src/main/java/com/opensymphony/xwork2/util/logging/slf4j/Slf4jLoggerFactory.java new file mode 100644 index 0000000..6e3e6d5 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/logging/slf4j/Slf4jLoggerFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2006,2009 The Apache Software Foundation. + * + * Licensed 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 com.opensymphony.xwork2.util.logging.slf4j; + +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; + +/** + * Creates slf4j-logging-backed loggers + * + * You can use the same to explicit tell the framework which implementation to use and don't depend on class discovery: + * <pre> + * -Dxwork.loggerFactory=com.opensymphony.xwork2.util.logging.slf4j.Slf4jLoggerFactory + * </pre> + */ +@Deprecated +public class Slf4jLoggerFactory extends LoggerFactory { + + @Override + protected Logger getLoggerImpl(Class<?> cls) { + return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(cls)); + } + + @Override + protected Logger getLoggerImpl(String name) { + return new Slf4jLogger(org.slf4j.LoggerFactory.getLogger(name)); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/package.html ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/package.html b/core/src/main/java/com/opensymphony/xwork2/util/package.html new file mode 100644 index 0000000..e400bf9 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/package.html @@ -0,0 +1 @@ +<body>XWork util classes.</body> http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/profiling/ObjectProfiler.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/profiling/ObjectProfiler.java b/core/src/main/java/com/opensymphony/xwork2/util/profiling/ObjectProfiler.java new file mode 100644 index 0000000..7438113 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/profiling/ObjectProfiler.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2002-2003, Atlassian Software Systems Pty Ltd All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * Neither the name of Atlassian Software Systems Pty Ltd nor the names of + * its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.opensymphony.xwork2.util.profiling; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * @author <a href="mailto:[email protected]">Scott Farquhar</a> + */ +public class ObjectProfiler { + + /** + * Given a class, and an interface that it implements, return a proxied version of the class that implements + * the interface. + * <p/> + * The usual use of this is to profile methods from Factory objects: + * <pre> + * public PersistenceManager getPersistenceManager() + * { + * return new DefaultPersistenceManager(); + * } + * + * instead write: + * public PersistenceManager getPersistenceManager() + * { + * return ObjectProfiler.getProfiledObject(PersistenceManager.class, new DefaultPersistenceManager()); + * } + * </pre> + * <p/> + * A side effect of this is that you will no longer be able to downcast to DefaultPersistenceManager. This is probably a *good* thing. + * + * @param interfaceClazz The interface to implement. + * @param o The object to proxy + * @return A proxied object, or the input object if the interfaceClazz wasn't an interface. + */ + public static Object getProfiledObject(Class interfaceClazz, Object o) { + //if we are not active - then do nothing + if (!UtilTimerStack.isActive()) { + return o; + } + + //this should always be true - you shouldn't be passing something that isn't an interface + if (interfaceClazz.isInterface()) { + InvocationHandler timerHandler = new TimerInvocationHandler(o); + return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), + new Class[]{interfaceClazz}, timerHandler); + } else { + return o; + } + } + + /** + * A profiled call {@link Method#invoke(java.lang.Object, java.lang.Object[])}. If {@link UtilTimerStack#isActive() } + * returns false, then no profiling is performed. + */ + public static Object profiledInvoke(Method target, Object value, Object[] args) throws IllegalAccessException, InvocationTargetException { + //if we are not active - then do nothing + if (!UtilTimerStack.isActive()) { + return target.invoke(value, args); + } + + String logLine = new String(getTrimmedClassName(target) + "." + target.getName() + "()"); + + UtilTimerStack.push(logLine); + try { + Object returnValue = target.invoke(value, args); + + //if the return value is an interface then we should also proxy it! + if (returnValue != null && target.getReturnType().isInterface()) { + InvocationHandler timerHandler = new TimerInvocationHandler(returnValue); + return Proxy.newProxyInstance(returnValue.getClass().getClassLoader(), + new Class[]{target.getReturnType()}, timerHandler); + } else { + return returnValue; + } + } finally { + UtilTimerStack.pop(logLine); + } + } + + /** + * Given a method, get the Method name, with no package information. + */ + public static String getTrimmedClassName(Method method) { + String classname = method.getDeclaringClass().getName(); + return classname.substring(classname.lastIndexOf('.') + 1); + } + +} + +class TimerInvocationHandler implements InvocationHandler { + protected Object target; + + public TimerInvocationHandler(Object target) { + if (target == null) { + throw new IllegalArgumentException("Target Object passed to timer cannot be null"); + } + this.target = target; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return ObjectProfiler.profiledInvoke(method, target, args); + } + +} http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/profiling/ProfilingTimerBean.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/com/opensymphony/xwork2/util/profiling/ProfilingTimerBean.java b/core/src/main/java/com/opensymphony/xwork2/util/profiling/ProfilingTimerBean.java new file mode 100644 index 0000000..a475c62 --- /dev/null +++ b/core/src/main/java/com/opensymphony/xwork2/util/profiling/ProfilingTimerBean.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2002-2003, Atlassian Software Systems Pty Ltd All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * Neither the name of Atlassian Software Systems Pty Ltd nor the names of + * its contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.opensymphony.xwork2.util.profiling; + +import java.util.ArrayList; +import java.util.List; + +/** + * Bean to contain information about the pages profiled + * + * @author <a href="mailto:[email protected]">Mike Cannon-Brookes</a> + * @author <a href="mailto:[email protected]">Scott Farquhar</a> + * @version $Date$ $Id$ + */ +public class ProfilingTimerBean implements java.io.Serializable { + + private static final long serialVersionUID = -6180672043920208784L; + + List<ProfilingTimerBean> children = new ArrayList<>(); + ProfilingTimerBean parent = null; + + String resource; + + long startTime; + long totalTime; + + public ProfilingTimerBean(String resource) { + this.resource = resource; + } + + protected void addParent(ProfilingTimerBean parent) { + this.parent = parent; + } + + public ProfilingTimerBean getParent() { + return parent; + } + + + public void addChild(ProfilingTimerBean child) { + children.add(child); + child.addParent(this); + } + + + public void setStartTime() { + this.startTime = System.currentTimeMillis(); + } + + public void setEndTime() { + this.totalTime = System.currentTimeMillis() - startTime; + } + + public String getResource() { + return resource; + } + + /** + * Get a formatted string representing all the methods that took longer than a specified time. + */ + + public String getPrintable(long minTime) { + return getPrintable("", minTime); + } + + protected String getPrintable(String indent, long minTime) { + //only print the value if we are larger or equal to the min time. + if (totalTime >= minTime) { + StringBuilder buffer = new StringBuilder(); + buffer.append(indent); + buffer.append("[" + totalTime + "ms] - " + resource); + buffer.append("\n"); + + for (ProfilingTimerBean aChildren : children) { + buffer.append((aChildren).getPrintable(indent + " ", minTime)); + } + + return buffer.toString(); + } else + return ""; + } +} +
