You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by am...@apache.org on 2009/11/17 23:50:00 UTC

svn commit: r881592 - in /cxf/trunk: rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/ systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/ systests/jaxrs/src/...

Author: amichalec
Date: Tue Nov 17 22:49:55 2009
New Revision: 881592

URL: http://svn.apache.org/viewvc?rev=881592&view=rev
Log:
Initial version of ATOM push-style logger

Added:
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogLevel.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecord.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecordsList.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushBean.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushEngine.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushHandler.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/ContentSingleEntryConverter.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Converter.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Deliverer.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/LoggingThread.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/RetryingDeliverer.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/WebClientDeliverer.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/package-info.java
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/package-info.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush.properties
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_batch.properties
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_disabled.properties

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogLevel.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogLevel.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogLevel.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogLevel.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,66 @@
+/**
+ * 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.cxf.jaxrs.ext.logging;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlTransient;
+
+/**
+ * Log level of {@link LogRecord}. Based on SLF4J being popular facade for loggers like JUL, Log4J, JCL and so
+ * on. Severities order is: FATAL > ERROR > WARN > INFO > DEBUG > TRACE.
+ * <p>
+ * Mapping of levels:
+ * <ol>
+ * <li>JUL - same as <a href="http://www.slf4j.org/apidocs/org/slf4j/bridge/SLF4JBridgeHandler.html">SLF4J
+ * approach</a>.</li>
+ * <li>Log4J - levels are identical</li>
+ * </ol>
+ */
+@XmlEnum
+public enum LogLevel {
+    FATAL,
+    ERROR,
+    WARN,
+    INFO,
+    DEBUG,
+    TRACE;
+    
+    @XmlTransient
+    private static Map<Level, LogLevel> julLogLevels = new HashMap<Level, LogLevel>();
+
+    static {
+        julLogLevels.put(Level.SEVERE, LogLevel.ERROR);
+        julLogLevels.put(Level.WARNING, LogLevel.WARN);
+        julLogLevels.put(Level.INFO, LogLevel.INFO);
+        julLogLevels.put(Level.FINE, LogLevel.DEBUG);
+        julLogLevels.put(Level.FINER, LogLevel.DEBUG);
+        julLogLevels.put(Level.FINEST, LogLevel.TRACE);
+    }
+    
+    /**
+     * Creates this enum from JUL {@link Level}.
+     */
+    public static LogLevel fromJUL(Level level) {
+        return julLogLevels.get(level);
+    }
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecord.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecord.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecord.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecord.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,167 @@
+/**
+ * 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.cxf.jaxrs.ext.logging;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+import java.util.Date;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.commons.lang.Validate;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+/**
+ * Log entry serializable to XML. Based on common set of {@link java.util.logging.LogRecord} and
+ * {@link org.apache.log4j.spi.LoggingEvent} attributes.
+ * <p>
+ * LogRecord are never null; if some attributes are not set (e.g. logger name, or rendered cause taken from
+ * Throwable) empty strings are returned.
+ */
+@XmlRootElement
+public class LogRecord {
+
+    private Date eventTimestamp = new Date();
+    private LogLevel level = LogLevel.INFO;
+    private String message = "";
+    private String loggerName = "";
+    private String threadName = "";
+    private String throwable = "";
+
+    /**
+     * Creates this object from JUL LogRecord. Most attributes are copied, others are converted as follows:
+     * raw {@link java.util.logging.LogRecord#getMessage() message} is formatted with
+     * {@link java.util.logging.LogRecord#getParameters() parameters} using {@link MessageFormat}, attached
+     * {@link java.util.logging.LogRecord#getThrown() throwable} has full stack trace dumped, and log levels
+     * are mapped as specified in {@link LogRecord}.
+     * 
+     * @param julRecord log record to convert.
+     * @return conversion result.
+     */
+    public static LogRecord fromJUL(java.util.logging.LogRecord julRecord) {
+        Validate.notNull(julRecord, "julRecord is null");
+        LogRecord record = new LogRecord();
+        record.setEventTimestamp(new Date(julRecord.getMillis()));
+        record.setLevel(LogLevel.fromJUL(julRecord.getLevel()));
+        record.setLoggerName(julRecord.getLoggerName());
+        if (julRecord.getThrown() != null) {
+            record.setThrowable(julRecord.getThrown());
+        }
+        if (julRecord.getParameters() != null) {
+            record.setMessage(MessageFormat.format(julRecord.getMessage(), julRecord.getParameters()));
+        } else {
+            record.setMessage(julRecord.getMessage());
+        }
+        record.setThreadName(Integer.toString(julRecord.getThreadID()));
+        return record;
+    }
+
+    @XmlElement
+    public Date getEventTimestamp() {
+        return eventTimestamp;
+    }
+
+    public void setEventTimestamp(Date eventTimestamp) {
+        Validate.notNull(eventTimestamp, "eventTimestamp is null");
+        this.eventTimestamp = eventTimestamp;
+    }
+
+    @XmlElement
+    public LogLevel getLevel() {
+        return level;
+    }
+
+    public void setLevel(LogLevel level) {
+        Validate.notNull(level, "level is null");
+        this.level = level;
+    }
+
+    /**
+     * Formatted message with parameters filled in.
+     */
+    @XmlElement
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String renderedMessage) {
+        Validate.notNull(level, "message is null");
+        this.message = renderedMessage;
+    }
+
+    @XmlElement
+    public String getLoggerName() {
+        return loggerName;
+    }
+
+    public void setLoggerName(String loggerName) {
+        Validate.notNull(level, "loggerName is null");
+        this.loggerName = loggerName;
+    }
+
+    @XmlElement
+    public String getThreadName() {
+        return threadName;
+    }
+
+    public void setThreadName(String threadName) {
+        Validate.notNull(level, "threadName is null");
+        this.threadName = threadName;
+    }
+
+    /**
+     * Full stack trace of {@link Throwable} associated with log record.
+     */
+    @XmlElement
+    public String getThrowable() {
+        return throwable;
+    }
+
+    public void setThrowable(String throwable) {
+        Validate.notNull(throwable, "throwable is null");
+        this.throwable = throwable;
+    }
+
+    public void setThrowable(Throwable thr) {
+        Validate.notNull(thr, "throwable is null");
+        StringWriter sw = new StringWriter();
+        thr.printStackTrace(new PrintWriter(sw));
+        this.throwable = sw.getBuffer().toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return EqualsBuilder.reflectionEquals(obj, this);
+    }
+
+    @Override
+    public int hashCode() {
+        return HashCodeBuilder.reflectionHashCode(this);
+    }
+
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+    }
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecordsList.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecordsList.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecordsList.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/LogRecordsList.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,65 @@
+/**
+ * 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.cxf.jaxrs.ext.logging;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.commons.lang.Validate;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+/**
+ * List of {@link LogRecord}s. Necessary wrapper for {@link List} used in JAXB context.
+ */
+@XmlRootElement(namespace = "zzz")
+public class LogRecordsList {
+
+    private List<LogRecord> logRecords = new ArrayList<LogRecord>();
+    
+    @XmlElement(name = "logRecord", namespace = "zzz")
+    public List<LogRecord> getLogRecords() {
+        return logRecords;
+    }
+
+    public void setLogRecords(List<LogRecord> logRecords) {
+        Validate.notNull(logRecords, "logRecords is null");
+        this.logRecords = logRecords;
+    }
+    
+    @Override
+    public boolean equals(Object obj) {
+        return EqualsBuilder.reflectionEquals(obj, this);
+    }
+
+    @Override
+    public int hashCode() {
+        return HashCodeBuilder.reflectionHashCode(this);
+    }
+
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+    }    
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushBean.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushBean.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushBean.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushBean.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,23 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+public class AtomPushBean {
+
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushEngine.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushEngine.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushEngine.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushEngine.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,145 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.abdera.model.Element;
+import org.apache.commons.lang.Validate;
+import org.apache.cxf.jaxrs.ext.logging.LogRecord;
+
+/**
+ * ATOM push-style realization class. Engine enqueues log records as they are {@link #publish(LogRecord)
+ * published}. After queue size exceeds {@link #getBatchSize() batch size} processing of collection of these
+ * records (in size of batch size) is triggered.
+ * <p>
+ * Processing is done in separate thread not to block publishing interface. Processing is two step: first list
+ * of log records is transformed by {@link Converter converter} to ATOM {@link Element element} and then it is
+ * pushed out by {@link Deliverer deliverer} to client. Next to transport deliverer is indirectly responsible
+ * for marshaling ATOM element to XML.
+ * <p>
+ * Processing is done by single threaded {@link java.util.concurrent.Executor executor}; next batch of records
+ * is taken from queue only when currently processed batch finishes and queue has enough elements to proceed.
+ * <p>
+ * First failure of any delivery shuts engine down. To avoid this situation engine must have registered
+ * reliable deliverer or use wrapping {@link RetryingDeliverer}.
+ */
+// TODO add internal diagnostics - log messages somewhere except for logger :D
+public class AtomPushEngine {
+    private List<LogRecord> queue = new ArrayList<LogRecord>();
+    private ExecutorService executor = Executors.newSingleThreadExecutor();
+    private int batchSize = 1;
+    private Converter converter;
+    private Deliverer deliverer;
+
+    /**
+     * Put record to publishing queue. Engine accepts published records only if is in proper state - is
+     * properly configured (has deliverer and converter registered) and is not shot down; otherwise calls to
+     * publish are ignored.
+     * 
+     * @param record record to be published.
+     */
+    public synchronized void publish(LogRecord record) {
+        Validate.notNull(record, "record is null");
+        if (isValid()) {
+            queue.add(record);
+            if (queue.size() >= batchSize) {
+                publishBatch(queue);
+                queue = new ArrayList<LogRecord>();
+            }
+        }
+    }
+
+    /**
+     * Shuts engine down.
+     */
+    public synchronized void shutdown() {
+        executor.shutdownNow();
+    }
+
+    private boolean isValid() {
+        if (deliverer == null) {
+            // TODO report cause
+            System.err.println("deliverer is not set");
+            return false;
+        }
+        if (converter == null) {
+            System.err.println("converter is not set");
+            return false;
+        }
+        if (executor.isShutdown()) {
+            System.err.println("engine shutdown");
+            return false;
+        }
+        return true;
+    }
+
+    private void publishBatch(final List<LogRecord> batch) {
+        executor.execute(new Runnable() {
+            public void run() {
+                try {
+                    LoggingThread.markSilent(true);
+                    // syncing for safe converter/deliverer on the fly replacement
+                    synchronized (this) {
+                        // TODO diagnostic output here: System.out.println(element.toString());
+                        Element element = converter.convert(batch);
+                        if (!deliverer.deliver(element)) {
+                            System.err.println("Delivery failed, shutting engine down");
+                            executor.shutdownNow();
+                        }
+                    }
+                } catch (InterruptedException e) {
+                    // no action on executor.shutdownNow();
+                } finally {
+                    LoggingThread.markSilent(false);
+                }
+            }
+        });
+    }
+
+    public int getBatchSize() {
+        return batchSize;
+    }
+
+    public void setBatchSize(int batchSize) {
+        Validate.isTrue(batchSize > 0, "batch size is not greater than zero");
+        this.batchSize = batchSize;
+    }
+
+    public Converter getConverter() {
+        return converter;
+    }
+
+    public synchronized void setConverter(Converter converter) {
+        Validate.notNull(converter, "converter is null");
+        this.converter = converter;
+    }
+
+    public Deliverer getDeliverer() {
+        return deliverer;
+    }
+
+    public synchronized void setDeliverer(Deliverer deliverer) {
+        Validate.notNull(deliverer, "deliverer is null");
+        this.deliverer = deliverer;
+    }
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushHandler.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushHandler.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushHandler.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/AtomPushHandler.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,215 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+import java.lang.reflect.Constructor;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+
+import org.apache.cxf.jaxrs.ext.logging.LogRecord;
+
+/**
+ * Handler pushing log records in batches as Atom Feeds to registered client. Handler responsibility is to
+ * adapt to JUL framework while most of job is delegated to {@link AtomPushEngine}.
+ * <p>
+ * For simple configuration using properties file (one global root-level handler of this class) following
+ * properties prefixed with full class name can be used:
+ * <ul>
+ * <li><b>url</b> - URL where feeds will be pushed (mandatory parameter)</li>
+ * <li><b>converter</b> - name of class implementing {@link Converter} class. For classes from this package
+ * only class name can be given e.g. instead of
+ * "org.apache.cxf.jaxrs.ext.logging.atom.ContentSingleEntryConverter" one can specify
+ * "ContentSingleEntryConverter". If parameter is not set {@link ContentSingleEntryConverter} is used.</li>
+ * <li><b>deliverer</b> - name of class implementing {@link Deliverer} class. For classes from this package
+ * only class name can be given e.g. instead of "org.apache.cxf.jaxrs.ext.logging.atom.WebClientDeliverer" one
+ * can specify "WebClientDeliverer". If parameter is not set {@link WebClientDeliverer} is used.</li>
+ * <li><b>batchSize</b> - integer number specifying minimal number of published log records that trigger
+ * processing and pushing ATOM document. If parameter is not set, is not greater than zero or is not a number,
+ * batch size is set to 1.</li>
+ * </ul>
+ * Family of <tt>retry</tt> parameters below; availability of any of this parameters enables delivery retrying
+ * (e.g. for default non-reliable deliverers) with {@link RetryingDeliverer} that can be combined with
+ * provided non-reliable deliverers. Detailed explanation of these parameter, see {@link RetryingDeliverer}
+ * class description.
+ * <ul>
+ * <li><b>retry.pause</b> - pausing strategy of delivery retries, either <b>linear</b> or <b>exponential</b>
+ * value (mandatory parameter). If mispelled linear is used.</li>
+ * <li><b>retry.pause.time</b> - pause time (in seconds) between retries. If parameter is not set, pause is
+ * set to 30 seconds.</li>
+ * <li><b>retry.timeout</b> - maximum time (in seconds) retrying will be continued. If not set timeout is not
+ * set (infinite loop of retries).</li>
+ * </ul>
+ * Example:
+ * 
+ * <pre>
+ * handlers = org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler, java.util.logging.ConsoleHandler
+ * .level = INFO
+ * ...
+ * org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.url = http://localhost:9080
+ * org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.batchSize = 10
+ * org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.deliverer = WebClientDeliverer 
+ * org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.converter = foo.bar.MyConverter
+ * org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.pause = linear
+ * org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.pause.time = 10
+ * org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.timeout = 360
+ * ...
+ * </pre>
+ */
+public final class AtomPushHandler extends Handler {
+
+    private AtomPushEngine engine = new AtomPushEngine();
+    private boolean lazyConfig;
+
+    /**
+     * Creates handler with configuration taken from properties file.
+     */
+    public AtomPushHandler() {
+        // deferred configuration: configure() called from here would use utilities that attempt to log
+        // and create this handler instance in recursion; configure() will be called on first publish()
+        lazyConfig = true;
+    }
+
+    /**
+     * Creates handler with custom parameters.
+     * 
+     * @param batchSize batch size, see {@link AtomPushEngine#getBatchSize()}
+     * @param converter converter transforming logs into ATOM elements
+     * @param deliverer deliverer pushing ATOM elements to client
+     */
+    public AtomPushHandler(int batchSize, Converter converter, Deliverer deliverer) {
+        engine.setBatchSize(batchSize);
+        engine.setConverter(converter);
+        engine.setDeliverer(deliverer);
+    }
+
+    @Override
+    public synchronized void publish(java.util.logging.LogRecord record) {
+        if (LoggingThread.isSilent()) {
+            return;
+        }
+        LoggingThread.markSilent(true);
+        try {
+            if (lazyConfig) {
+                lazyConfig = false;
+                configure();
+            }
+            LogRecord rec = LogRecord.fromJUL(record);
+            engine.publish(rec);
+        } finally {
+            LoggingThread.markSilent(false);
+        }
+    }
+
+    @Override
+    public synchronized void close() throws SecurityException {
+        engine.shutdown();
+    }
+
+    @Override
+    public synchronized void flush() {
+        // no-op
+    }
+
+    /**
+     * Configuration from properties. Aligned to JUL strategy - properties file is only for simple
+     * configuration: it allows configure one root handler with its parameters. What is even more dummy, JUL
+     * does not allow to iterate over configuration properties to make interpretation automated (e.g. using
+     * commons-beanutils)
+     */
+    private void configure() {
+        LogManager manager = LogManager.getLogManager();
+        String cname = getClass().getName();
+        String url = manager.getProperty(cname + ".url");
+        if (url == null) {
+            // cannot proceed
+            return;
+        }
+        String deliverer = manager.getProperty(cname + ".deliverer");
+        if (deliverer != null) {
+            engine.setDeliverer(createDeliverer(deliverer, url));
+        } else {
+            // default
+            engine.setDeliverer(new WebClientDeliverer(url));
+        }
+        String converter = manager.getProperty(cname + ".converter");
+        if (converter != null) {
+            engine.setConverter(createConverter(converter));
+        } else {
+            // default
+            engine.setConverter(new ContentSingleEntryConverter());
+        }
+        engine.setBatchSize(toInt(manager.getProperty(cname + ".batchSize"), 1, 1));
+        String retryType = manager.getProperty(cname + ".retry.pause");
+        if (retryType != null) {
+            int timeout = toInt(manager.getProperty(cname + ".retry.timeout"), 0, 0);
+            int pause = toInt(manager.getProperty(cname + ".retry.pause.time"), 1, 30);
+            boolean linear = !retryType.equalsIgnoreCase("exponential");
+            Deliverer wrapped = new RetryingDeliverer(engine.getDeliverer(), timeout, pause, linear);
+            engine.setDeliverer(wrapped);
+        }
+    }
+
+    private int toInt(String property, int defaultValue) {
+        try {
+            return Integer.parseInt(property);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
+    private int toInt(String property, int lessThan, int defaultValue) {
+        int ret = toInt(property, defaultValue);
+        if (ret < lessThan) {
+            ret = defaultValue;
+        }
+        return ret;
+    }
+
+    private Deliverer createDeliverer(String clazz, String url) {
+        try {
+            Constructor<?> ctor = loadClass(clazz).getConstructor(String.class);
+            return (Deliverer)ctor.newInstance(url);
+        } catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    private Converter createConverter(String clazz) {
+        try {
+            Constructor<?> ctor = loadClass(clazz).getConstructor();
+            return (Converter)ctor.newInstance();
+        } catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    private Class<?> loadClass(String clazz) throws ClassNotFoundException {
+        try {
+            return getClass().getClassLoader().loadClass(clazz);
+        } catch (ClassNotFoundException e) {
+            try {
+                // clazz could be shorted (stripped package name) retry
+                String clazz2 = getClass().getPackage().getName() + "." + clazz;
+                return getClass().getClassLoader().loadClass(clazz2);
+            } catch (Exception e1) {
+                throw new ClassNotFoundException(e.getMessage() + " or " + e1.getMessage());
+            }
+        }
+    }
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/ContentSingleEntryConverter.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/ContentSingleEntryConverter.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/ContentSingleEntryConverter.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/ContentSingleEntryConverter.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,73 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+import java.io.StringWriter;
+import java.util.List;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.factory.Factory;
+import org.apache.abdera.model.Content;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Feed;
+import org.apache.cxf.jaxrs.ext.logging.LogRecord;
+import org.apache.cxf.jaxrs.ext.logging.LogRecordsList;
+
+/**
+ * Single entry in feed with content set to list of log records.
+ */
+public class ContentSingleEntryConverter implements Converter {
+
+    private Factory factory;
+    private Marshaller marsh;
+
+    public ContentSingleEntryConverter() {
+        factory = Abdera.getNewFactory();
+        try {
+            marsh = JAXBContext.newInstance(LogRecordsList.class).createMarshaller();
+        } catch (JAXBException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Element convert(List<LogRecord> records) {
+        Feed feed = factory.newFeed();
+        Entry entry = factory.newEntry();
+        feed.addEntry(entry);
+        Content content = factory.newContent();
+        content.setContentType(Content.Type.XML);
+        entry.setContent(content);
+        StringWriter writer = new StringWriter();
+        LogRecordsList list = new LogRecordsList();
+        list.setLogRecords(records);
+        try {
+            marsh.marshal(list, writer);
+        } catch (JAXBException e) {
+            throw new RuntimeException(e);
+        }
+        content.setValue(writer.toString());
+        return feed;
+    }
+
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Converter.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Converter.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Converter.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Converter.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,39 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+import java.util.List;
+
+import org.apache.abdera.model.Element;
+import org.apache.cxf.jaxrs.ext.logging.LogRecord;
+
+/**
+ * Converts batch of log records into ATOM element to deliver. Represents strategies of conversion e.g. as
+ * ATOM format extensions, as Entry content etc.
+ */
+public interface Converter {
+
+    /**
+     * Converts collection of log records into ATOM element.
+     * 
+     * @param records not-null collection of records
+     * @return ATOM document representing records
+     */
+    Element convert(List<LogRecord> records);
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Deliverer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Deliverer.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Deliverer.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/Deliverer.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,36 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+import org.apache.abdera.model.Element;
+
+/**
+ * ATOM element deliverer. Represents transport strategy e.g. using
+ * {@link org.apache.cxf.jaxrs.client.WebClient}, SOAP reliable messaging etc.
+ */
+public interface Deliverer {
+
+    /**
+     * Delivers ATOM element.
+     * 
+     * @param element element to deliver.
+     * @return true if delivery successful, false otherwise.
+     */
+    boolean deliver(Element element) throws InterruptedException;
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/LoggingThread.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/LoggingThread.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/LoggingThread.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/LoggingThread.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,51 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+/**
+ * Helps disable logging from calls of the same thread. Motivation: log handlers in this package causes other
+ * threads (from executor) to start logging (by using JAXB that itself uses JUL) which in turn can be caught
+ * by the same handler leading to infinite loop.
+ * <p>
+ * Other approach than using thread local storage would be scanning of stack trace of current thread to see if
+ * root of call comes from same package as package of handler - it's less effective so TLS is using here.
+ */
+public final class LoggingThread {
+
+    private static ThreadLocal<LoggingThread> threadLocal = new ThreadLocal<LoggingThread>() {
+        @Override
+        protected LoggingThread initialValue() {
+            return new LoggingThread();
+        }
+    };
+
+    private boolean isSilent;
+
+    private LoggingThread() {
+    }
+
+    public static void markSilent(boolean silent) {
+        LoggingThread lt = threadLocal.get();
+        lt.isSilent = silent;
+    }
+
+    public static boolean isSilent() {
+        return threadLocal.get().isSilent;
+    }
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/RetryingDeliverer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/RetryingDeliverer.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/RetryingDeliverer.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/RetryingDeliverer.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,124 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.apache.abdera.model.Element;
+import org.apache.commons.lang.Validate;
+
+/**
+ * Wrapper on other deliverer retrying delivery in case of failure. Delivery attempts repeat in loop with some
+ * pause time between retries until successful delivery or exceeding time limit. Time delay between delivery
+ * is configurable strategy. Two predefined strategies are given: each time pause same amount of time (linear)
+ * and each next time pause time doubles (exponential).
+ */
+public class RetryingDeliverer implements Deliverer {
+
+    private Deliverer deliverer;
+    private PauseCalculator pauser;
+    private int timeout;
+
+    /**
+     * Creates retrying deliverer with predefined retry strategy.
+     * 
+     * @param worker real deliverer used to push data out.
+     * @param timeout maximum time range (in seconds) that retrial is continued; time spent on delivery call
+     *            is included. No timeout (infinite loop) if set to zero.
+     * @param pause time of pause (in seconds) greater than zero.
+     * @param linear if true linear strategy (each time pause same amount of time), exponential otherwise
+     *            (each next time pause time doubles).
+     */
+    public RetryingDeliverer(Deliverer worker, int timeout, int pause, boolean linear) {
+        Validate.notNull(worker, "worker is null");
+        Validate.isTrue(timeout >= 0, "timeout is negative");
+        Validate.isTrue(pause > 0, "pause is not greater than zero");
+        deliverer = worker;
+        this.timeout = timeout;
+        this.pauser = linear ? new ConstantPause(pause) : new ExponentialPause(pause);
+    }
+
+    /**
+     * Creates retrying deliverer with custom retry strategy.
+     * 
+     * @param worker real deliverer used to push data out.
+     * @param timeout maximum time range (in seconds) that retrial is continued; time spent on delivery call
+     *            is included. No timeout (infinite loop) if set to zero.
+     * @param strategy custom retry pausing strategy.
+     */
+    public RetryingDeliverer(Deliverer worker, int timeout, PauseCalculator strategy) {
+        Validate.notNull(worker, "worker is null");
+        Validate.notNull(strategy, "strategy is null");
+        Validate.isTrue(timeout >= 0, "timeout is negative");
+        deliverer = worker;
+        pauser = strategy;
+        this.timeout = timeout;
+    }
+
+    public boolean deliver(Element element) throws InterruptedException {
+        Calendar cal = Calendar.getInstance();
+        cal.add(Calendar.SECOND, timeout);
+        Date timeoutDate = cal.getTime();
+        while (!deliverer.deliver(element)) {
+            int sleep = pauser.nextPause();
+            cal = Calendar.getInstance();
+            cal.add(Calendar.SECOND, sleep);
+            if (timeout == 0 || timeoutDate.after(cal.getTime())) {
+                Thread.sleep(sleep * 1000);
+            } else {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Calculates time of subsequent pauses between delivery attempts. */
+    public interface PauseCalculator {
+        /** Time of next pause (in seconds). */
+        int nextPause();
+    }
+
+    private static class ConstantPause implements PauseCalculator {
+        private int pause;
+
+        public ConstantPause(int pause) {
+            this.pause = pause;
+        }
+
+        public int nextPause() {
+            return pause;
+        }
+    }
+
+    private static class ExponentialPause implements PauseCalculator {
+        private int pause;
+
+        public ExponentialPause(int pause) {
+            this.pause = pause;
+        }
+
+        public int nextPause() {
+            int curr = pause;
+            pause *= 2;
+            return curr;
+        }
+    }
+
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/WebClientDeliverer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/WebClientDeliverer.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/WebClientDeliverer.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/WebClientDeliverer.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,58 @@
+/**
+ * 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.cxf.jaxrs.ext.logging.atom;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.abdera.model.Element;
+import org.apache.commons.lang.Validate;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.provider.AtomEntryProvider;
+import org.apache.cxf.jaxrs.provider.AtomFeedProvider;
+
+public class WebClientDeliverer implements Deliverer {
+    private WebClient wc;
+
+    public WebClientDeliverer(String deliveryAddress) {
+        this.wc = create(deliveryAddress);
+    }
+
+    public WebClientDeliverer(WebClient wc) {
+        Validate.notNull(wc, "wc is null");
+        this.wc = wc;
+    }
+
+    public boolean deliver(Element element) {
+        Response res = wc.post(element);
+        int status = res.getStatus();
+        return status >= 200 && status <= 299;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static WebClient create(String baseAddress) {
+        Validate.notEmpty(baseAddress, "baseAddress is empty or null");
+        List<?> providers = Arrays.asList(new AtomFeedProvider(), new AtomEntryProvider());
+        WebClient wc = WebClient.create(baseAddress, providers);
+        wc.type("application/atom+xml");
+        return wc;
+    }
+}

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/package-info.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/package-info.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/package-info.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/atom/package-info.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+
+/**
+ * Support for producing logs in
+ * <a href="http://tools.ietf.org/html/rfc4287">ATOM Syndication Format</a>.
+ * Allows to configure <tt>java.util.logging</tt> (JUL) loggers to use
+ * handlers producing ATOM feeds that are either pushed to or pulled by client.  
+ */
+package org.apache.cxf.jaxrs.ext.logging.atom;
+

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/package-info.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/package-info.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/package-info.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/logging/package-info.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,28 @@
+/**
+ * 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.
+ */
+
+/**
+ * JAX-RS specific logging support. Based on <tt>java.util.logging</tt> (JUL)
+ * with use of different logging frameworks factored out; assumes that client 
+ * with source code logging to other systems, like Log4J, can bridge 
+ * to this implementation applying <a href="www.slf4j.org">SLF4J</a> 
+ * that JAXRS already depends on.
+ */
+package org.apache.cxf.jaxrs.ext.logging;
+

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java?rev=881592&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSLoggingAtomPushTest.java Tue Nov 17 22:49:55 2009
@@ -0,0 +1,157 @@
+/**
+ * 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.cxf.systest.jaxrs;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Feed;
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler;
+import org.apache.cxf.jaxrs.ext.logging.atom.ContentSingleEntryConverter;
+import org.apache.cxf.jaxrs.ext.logging.atom.WebClientDeliverer;
+import org.apache.cxf.jaxrs.provider.AtomEntryProvider;
+import org.apache.cxf.jaxrs.provider.AtomFeedProvider;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class JAXRSLoggingAtomPushTest {
+    private static final Logger LOG = LogUtils.getL7dLogger(JAXRSLoggingAtomPushTest.class);
+    private static Server server;
+    private static List<Element> received;
+
+    @Ignore
+    @Path("/")
+    public static class Resource {
+        @POST
+        public void consume(Feed feed) {
+            System.out.println(feed);
+            received.add(feed);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        // disable logging for server startup
+        configureLogging("resources/logging_atompush_disabled.properties");
+
+        JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+        sf.setResourceClasses(JAXRSLoggingAtomPushTest.Resource.class);
+        sf.setAddress("http://localhost:9080/");
+        sf.setProviders(Arrays.asList(new AtomFeedProvider(), new AtomEntryProvider()));
+        server = sf.create();
+        server.start();
+    }
+
+    /** Configures global logging */
+    private static void configureLogging(String propFile) throws Exception {
+        LogManager lm = LogManager.getLogManager();
+        InputStream ins = JAXRSLoggingAtomPushTest.class.getResourceAsStream(propFile);
+        lm.readConfiguration(ins);
+    }
+
+    private static void logSixEvents(Logger log) {
+        log.severe("severe message");
+        log.warning("warning message");
+        log.info("info message");
+        LogRecord r = new LogRecord(Level.FINE, "fine message");
+        r.setThrown(new IllegalArgumentException("tadaam"));
+        log.log(r);
+        r = new LogRecord(Level.FINER, "finer message with {0} and {1}");
+        r.setParameters(new Object[] {
+            "param1", "param2"
+        });
+        r.setLoggerName("faky-logger");
+        log.log(r);
+        log.finest("finest message");
+    }
+
+    @AfterClass
+    public static void afterClass() throws Exception {
+        if (server != null) {
+            server.stop();
+        }
+        LogManager lm = LogManager.getLogManager();
+        try {
+            // restoring original configuration to not use tested logging handlers
+            lm.readConfiguration();
+        } catch (Exception e) {
+            // ignore missing config file
+        }
+    }
+
+    @Before
+    public void before() throws Exception {
+        received = new ArrayList<Element>();
+    }
+
+    @Test
+    public void testOneElementBatch() throws Exception {
+        configureLogging("resources/logging_atompush.properties");
+        logSixEvents(LOG);
+        // need to wait: multithreaded and client-server journey
+        Thread.sleep(1000);
+        assertEquals("Different logged events count;", 6, received.size());
+    }
+
+    @Test
+    public void testMultiElementBatch() throws Exception {
+        configureLogging("resources/logging_atompush_batch.properties");
+        logSixEvents(LOG);
+        // need to wait: multithreaded and client-server journey
+        Thread.sleep(1000);
+        // 6 events / 3 element batch = 2 feeds expected
+        assertEquals("Different logged events count;", 2, received.size());
+    }
+
+    @Test
+    public void testPrivateLogger() throws Exception {
+        configureLogging("resources/logging_atompush_disabled.properties");
+        Logger log = LogUtils.getL7dLogger(JAXRSLoggingAtomPushTest.class, null, "private-log");
+        Handler h = new AtomPushHandler(2, new ContentSingleEntryConverter(),
+                                        new WebClientDeliverer("http://localhost:9080"));
+        log.addHandler(h);
+        log.setLevel(Level.ALL);
+        logSixEvents(log);
+        // need to wait: multithreaded and client-server journey
+        Thread.sleep(1000);
+        // 6 events / 2 element batch = 3 feeds expected
+        assertEquals("Different logged events count;", 3, received.size());
+    }
+}

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush.properties
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush.properties?rev=881592&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush.properties (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush.properties Tue Nov 17 22:49:55 2009
@@ -0,0 +1,33 @@
+# Atom logger plus echo on console
+handlers = org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler, java.util.logging.ConsoleHandler
+
+# Set the default logging level for the root logger
+.level = ALL
+
+# Set logging levels for the package-named loggers 
+org.apache.cxf.systest.jaxrs.level = ALL
+
+# Need to turn off logging from surrounding environment to properly count log entries in tests
+# (specified sub-entries since root level overrides sub-levels... yes, JUL is dumb :)
+org.apache.cxf.jaxrs.level = OFF
+org.apache.cxf.phase.level = OFF
+org.apache.cxf.service.level = OFF
+org.apache.cxf.interceptor.level = OFF
+org.apache.cxf.transport.level = OFF
+org.apache.cxf.bus.level = OFF
+org.apache.cxf.configuration.level = OFF
+org.apache.cxf.endpoint.level = OFF
+org.apache.cxf.resource.level = OFF
+org.springframework.level = OFF
+org.mortbay.level = OFF
+org.apache.axiom.level = OFF
+
+# Atom handler specific settings
+org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.url = http://localhost:9080
+org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.batchSize = 1
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.converter = ContentSingleEntryConverter
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.deliverer = WebClientDeliverer
+
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.pause = linear
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.pause.time = 5
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.timeout = 10

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_batch.properties
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_batch.properties?rev=881592&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_batch.properties (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_batch.properties Tue Nov 17 22:49:55 2009
@@ -0,0 +1,33 @@
+# Atom logger plus echo on console
+handlers = org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler, java.util.logging.ConsoleHandler
+
+# Set the default logging level for the root logger
+.level = ALL
+
+# Set logging levels for the package-named loggers 
+org.apache.cxf.systest.jaxrs.level = ALL
+
+# Need to turn off logging from surrounding environment to properly count log entries in tests
+# (specified sub-entries since root level overrides sub-levels... yes, JUL is dumb :)
+org.apache.cxf.jaxrs.level = OFF
+org.apache.cxf.phase.level = OFF
+org.apache.cxf.service.level = OFF
+org.apache.cxf.interceptor.level = OFF
+org.apache.cxf.transport.level = OFF
+org.apache.cxf.bus.level = OFF
+org.apache.cxf.configuration.level = OFF
+org.apache.cxf.endpoint.level = OFF
+org.apache.cxf.resource.level = OFF
+org.springframework.level = OFF
+org.mortbay.level = OFF
+org.apache.axiom.level = OFF
+
+# Atom handler specific settings
+org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.url = http://localhost:9080
+org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.batchSize = 3
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.converter = ContentSingleEntryConverter
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.deliverer = WebClientDeliverer
+
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.pause = linear
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.pause.time = 5
+#org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.retry.timeout = 10

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_disabled.properties
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_disabled.properties?rev=881592&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_disabled.properties (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/logging_atompush_disabled.properties Tue Nov 17 22:49:55 2009
@@ -0,0 +1,15 @@
+handlers = java.util.logging.ConsoleHandler
+.level = OFF
+org.apache.cxf.jaxrs.level = OFF
+org.apache.cxf.phase.level = OFF
+org.apache.cxf.service.level = OFF
+org.apache.cxf.interceptor.level = OFF
+org.apache.cxf.transport.level = OFF
+org.apache.cxf.bus.level = OFF
+org.apache.cxf.configuration.level = OFF
+org.apache.cxf.endpoint.level = OFF
+org.apache.cxf.resource.level = OFF
+org.springframework.level = OFF
+org.mortbay.level = OFF
+org.apache.axiom.level = OFF
+