/**
* 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.management.web.logging.atom;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import org.apache.cxf.management.web.logging.LogRecord;
import org.apache.cxf.management.web.logging.atom.converter.Converter;
import org.apache.cxf.management.web.logging.atom.deliverer.Deliverer;
/**
* Handler pushing log records in batches as Atom Feeds or Entries 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 name of this class can be used:
* <ul>
* <li><b>url</b> - URL where feeds will be pushed (mandatory parameter)</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>
* Conversion of log records into ATOM Elements can be tuned up using following parameters. Note that not all
* combinations are meaningful, see {@link org.apache.cxf.jaxrs.ext.logging.atom.converter.StandardConverter}
* for details:
* <ul>
* <li><b>output</b> - ATOM Element type pushed out, either "feed" or "entry"; when not specified or invalid
* value provided "feed" is used.</li>
* <li><b>multiplicity</b> - multiplicity of subelement(entries in feed for output=="feed" or log records in
* entry for output=="entry"), either "one" or "many"; when not specified or invalid value provided "one" is
* used.</li>
* <li><b>format</b> - method of embedding data in entry, either "content" or "extension"; when not specified
* or invalid value provided "content" is used.</li>
* </ul>
* By default delivery is served by WebClientDeliverer which does not support reliability of transport.
* Availability of any of this parameters enables retrying of default delivery. Detailed explanation of these
* parameter, see {@link org.apache.cxf.jaxrs.ext.logging.atom.deliverer.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>
* Ultimate control on conversion and delivery is obtained specifying own implementation classes:
* <ul>
* <li><b>converter</b> - name of class implementing {@link Converter} class replacing default conversion and
* its specific parameters ("output", "multiplicity" and "format") are ignored. For classes located in same
* package as Converter interface only class name can be given e.g. instead of
* "org.apache.cxf.jaxrs.ext.logging.atom.converter.FooBarConverter" one can specify "FooBarConverter".</li>
* <li><b>deliverer</b> - name of class implementing {@link Deliverer} class replacing default delivery and
* its specific parameters ("retry.Xxx") are ignored. For classes located in same package as Deliverer
* interface only class name can be given e.g. instead of
* "org.apache.cxf.jaxrs.ext.logging.atom.deliverer.WebClientDeliverer" one can specify
* "WebClientDeliverer".</li>
* </ul>
* Example:
*
* <pre>
* handlers = org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler, java.util.logging.ConsoleHandler
* .level = INFO
*
* # deliver to given URL triggering after each batch of 10 log records
* org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.url = http://localhost:9080
* org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.batchSize = 10
*
* # enable retrying delivery every 10 seconds for 5 minutes
* 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 = 300
*
* # output for AtomPub: push entries not feeds, each entry with one log record as "atom:extension"
* org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.output = entry
* org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.multiplicity = one
* org.apache.cxf.jaxrs.ext.logging.atom.AtomPushHandler.format = extension
* ...
* </pre>
*/
public final class AtomPushHandler extends Handler {
private AtomPushEngine engine;
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 = new AtomPushEngine();
engine.setBatchSize(batchSize);
engine.setConverter(converter);
engine.setDeliverer(deliverer);
}
/**
* Creates handler using (package private).
*
* @param engine configured engine.
*/
AtomPushHandler(AtomPushEngine engine) {
this.engine = engine;
}
@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();
AtomPushEngineConfigurator conf = new AtomPushEngineConfigurator();
conf.setUrl(manager.getProperty(cname + ".url"));
conf.setDelivererClass(manager.getProperty(cname + ".deliverer"));
conf.setConverterClass(manager.getProperty(cname + ".converter"));
conf.setBatchSize(manager.getProperty(cname + ".batchSize"));
conf.setBatchCleanupTime(manager.getProperty(cname + ".batchCleanupTime"));
conf.setRetryPause(manager.getProperty(cname + ".retry.pause"));
conf.setRetryPauseTime(manager.getProperty(cname + ".retry.pause.time"));
conf.setRetryTimeout(manager.getProperty(cname + ".retry.timeout"));
conf.setOutput(manager.getProperty(cname + ".output"));
conf.setMultiplicity(manager.getProperty(cname + ".multiplicity"));
conf.setFormat(manager.getProperty(cname + ".format"));
engine = conf.createEngine();
}
}