com.day.crx.statistics.Statistics Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 1997 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.day.crx.statistics;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Statistics
acts as a facade for adding entries and running
* reports.
*
* @author mreutegg
*/
public class Statistics implements Runnable {
private static final Logger log = LoggerFactory.getLogger(Statistics.class);
/**
* Set to false
when stopped.
*/
private volatile boolean running = true;
/**
* The writer thread, which writes back statistic entries.
*/
private final Thread writerThread;
/**
* A shared Session. Access to this session must be synchronized!
*/
private final Session session;
/**
* The task queue.
*/
private final BlockingQueue tasks = new LinkedBlockingQueue();
public Statistics(Session session) {
this.session = session;
this.writerThread = new Thread(this, "Statistics write back");
this.writerThread.start();
}
/**
* Stops the statistics instance and releases resource.
*/
public void stop() {
running = false;
tasks.add(Task.SHUTDOWN);
// wait at most ten seconds
try {
writerThread.join(10 * 1000);
} catch (InterruptedException e) {
// ignore
}
if (writerThread.isAlive()) {
// if it is still alive interrupt it
writerThread.interrupt();
// then join again
try {
writerThread.join(10 * 1000);
} catch (InterruptedException e) {
// ignore
}
}
synchronized (session) {
// finally close session
session.logout();
}
}
/**
* Runs a report and returns the result of the report. Please note that this
* implementation serializes access to the underlying session that runs the
* report. For improved concurrency, use {@link #runReport(Session, Report)}
* instead and provide your own session.
*
* This method is thread-safe.
*
* @param report the report to run.
* @return the result of the report.
* @throws RepositoryException if an error occurs while reading from the
* workspace.
*/
public Iterator runReport(Report report) throws RepositoryException {
synchronized (session) {
session.refresh(false);
return report.getResult(session);
}
}
/**
* Runs a report and returns the result of the report.
*
* This method is thread-safe.
*
* @param session The Session to access the data from the repository to
* generate the report
* @param report the report to run.
* @return the result of the report.
* @throws RepositoryException if an error occurs while reading from the
* workspace.
*/
public Iterator runReport(Session session, Report report)
throws RepositoryException {
return report.getResult(session);
}
/**
* Adds an entry to the statistics workspace.
*
* This method is thread-safe.
*
* @param entry the entry to add.
* @throws RepositoryException if an error occurs while writing to the
* workspace.
*/
public void addEntry(Entry entry) throws RepositoryException {
addEntryInternal(entry, false);
}
/**
* Adds an entry to the statistics workspace but does not guarantee that the
* entry is persisted when the method returns. The entry may be persisted
* at some time in the future or may even fail (silently) because of an
* exception.
*
* This method is thread-safe.
*
* @param entry the entry to add.
* @throws RepositoryException if an error occurs while writing to the
* workspace.
*/
public void addEntryAsync(Entry entry) throws RepositoryException {
addEntryInternal(entry, true);
}
//--------------------------------< Runnable >------------------------------
public void run() {
List work = new ArrayList();
while (running) {
work.clear();
// get all currently pending tasks
try {
Task t = tasks.take();
if (t == Task.SHUTDOWN) {
return;
}
work.add(t);
// loop until empty
while (tasks.peek() != null) {
t = tasks.take();
if (t == Task.SHUTDOWN) {
return;
}
work.add(t);
}
} catch (InterruptedException e) {
if (!running) {
return;
}
}
// perform the tasks
RepositoryException exception = null;
synchronized (session) {
try {
session.refresh(false);
} catch (RepositoryException e) {
log.warn("exception while refreshing session", e);
}
for (Task task : work) {
try {
task.performWork();
} catch (Throwable e) {
if (e instanceof RepositoryException) {
exception = (RepositoryException) e;
} else {
exception = new RepositoryException(e);
}
break;
}
}
if (exception == null) {
// success so far
// try to save
try {
session.save();
log.debug("persisted {} entries", work.size());
} catch (Throwable e) {
if (e instanceof RepositoryException) {
exception = (RepositoryException) e;
} else {
exception = new RepositoryException(e);
}
}
}
}
if (exception == null) {
// successfully saved -> set all tasks done
for (Task task : work) {
task.setDone();
}
} else {
try {
session.refresh(false);
} catch (Throwable e) {
log.warn("exception while refreshing session", e);
}
for (Task task : work) {
task.setException(exception);
}
}
}
}
//--------------------------------< internal >------------------------------
/**
* Adds an entry to the statistics workspace.
*
* This method is thread-safe.
*
* @param entry the entry to add.
* @param async when set to false
the method guarantees that
* the entry is persisted when the call returns.
* @throws RepositoryException if an error occurs while writing to the
* workspace.
*/
private void addEntryInternal(Entry entry, boolean async)
throws RepositoryException {
if (!running) {
throw new RepositoryException("Statistics stopped");
}
Task task = new Task(session, entry);
try {
tasks.put(task);
if (async) {
return;
}
task.get();
} catch (InterruptedException e) {
throw new RepositoryException("interrupted while waiting for write back of entry");
} catch (ExecutionException e) {
if (e.getCause() instanceof RepositoryException) {
throw (RepositoryException) e.getCause();
} else {
throw new RepositoryException(e.getCause());
}
}
}
}