Commit 031eb66c authored by Woonsan Ko's avatar Woonsan Ko

HSTTWO-3392 moving HDC API into commons to use it even in non-hst applications

parent d9589ba1
......@@ -12,6 +12,11 @@ src/main/java/org/hippoecm/hst/core/parameters/ImageSetPath.java -text
src/main/java/org/hippoecm/hst/core/parameters/JcrPath.java -text
src/main/java/org/hippoecm/hst/core/parameters/Parameter.java -text
src/main/java/org/hippoecm/hst/core/parameters/ParametersInfo.java -text
src/main/java/org/hippoecm/hst/diagnosis/DefaultTaskImpl.java -text svneol=unset#text/plain
src/main/java/org/hippoecm/hst/diagnosis/HDC.java -text svneol=unset#text/plain
src/main/java/org/hippoecm/hst/diagnosis/NOOPTaskImpl.java -text svneol=unset#text/plain
src/main/java/org/hippoecm/hst/diagnosis/Task.java -text svneol=unset#text/plain
src/main/java/org/hippoecm/hst/diagnosis/TaskLogFormatUtils.java -text svneol=unset#text/plain
src/main/java/org/hippoecm/hst/security/AuthenticationProvider.java -text
src/main/java/org/hippoecm/hst/security/Role.java -text
src/main/java/org/hippoecm/hst/security/TransientRole.java -text
......@@ -27,5 +32,6 @@ src/main/java/org/onehippo/cms7/logging/log4j/MdcOrJndiPropertyFilter.java -text
src/main/java/org/onehippo/cms7/logging/log4j/MdcPropertyFilter.java -text
src/main/java/org/onehippo/cms7/util/WeakIdentityMap.java -text
src/main/java/org/onehippo/sso/UrlSafeBase64.java -text
src/test/java/org/hippoecm/hst/diagnosis/TestHDC.java -text svneol=unset#text/plain
src/test/java/org/onehippo/cms7/event/HippoEventTest.java -text
src/test/java/org/onehippo/sso/CredentialCipherTest.java -text
/**
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.hst.diagnosis;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* DefaultTaskImpl
*/
class DefaultTaskImpl implements Task {
private static final Logger log = LoggerFactory.getLogger(DefaultTaskImpl.class);
private final String name;
private Map<String, Object> attributes;
private final Task parentTask;
private List<Task> childTasks;
private long startTimeMillis;
private long durationTimeMillis = -1L;
private boolean stopped;
DefaultTaskImpl(final Task parentTask, final String name) {
this.parentTask = parentTask;
this.name = name;
this.startTimeMillis = System.currentTimeMillis();
}
@Override
public String getName() {
return name;
}
@Override
public Map<String, Object> getAttributeMap() {
if (attributes == null) {
return Collections.emptyMap();
}
return Collections.unmodifiableMap(attributes);
}
@Override
public Enumeration<String> getAttributeNames() {
if (attributes != null) {
return Collections.enumeration(attributes.keySet());
} else {
List<String> emptyAttrNames = Collections.emptyList();
return Collections.enumeration(emptyAttrNames);
}
}
@Override
public void setAttribute(String key, Object value) {
if (attributes == null) {
// keep order of insertion thus Linked
attributes = new LinkedHashMap<String, Object> ();
}
attributes.put(key, value);
}
@Override
public Object getAttribute(String key) {
if (attributes == null) {
return null;
}
return attributes.get(key);
}
@Override
public Object removeAttribute(String key) {
if (attributes != null) {
return attributes.remove(key);
}
return null;
}
@Override
public Task getParentTask() {
return parentTask;
}
@Override
public Task startSubtask(String name) {
if (stopped) {
throw new IllegalStateException("The task was already stopped.");
}
if (childTasks == null) {
childTasks = new LinkedList<Task>();
}
Task childTask = new DefaultTaskImpl(this, name);
childTasks.add(childTask);
HDC.setCurrentTask(childTask);
return childTask;
}
@Override
public void stop() {
if (stopped) {
log.warn("Task '{}' was already stopped.", name);
return;
}
stopped = true;
durationTimeMillis = System.currentTimeMillis() - startTimeMillis;
HDC.setCurrentTask(parentTask);
}
@Override
public Collection<Task> getChildTasks() {
if (childTasks == null) {
return Collections.emptyList();
}
return Collections.unmodifiableCollection(childTasks);
}
@Override
public boolean isRunning() {
return !stopped;
}
@Override
public long getDurationTimeMillis() {
if (!stopped) {
log.warn("Task '{}' was not stopped hence duration time unknown.", name);
}
return durationTimeMillis;
}
}
/**
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.hst.diagnosis;
/**
* Hierarchical Diagnostic Context
*/
public class HDC {
public static final Task NOOP_TASK = new NOOPTaskImpl();
private static ThreadLocal<Task> tlRootTask = new ThreadLocal<Task>();
private static ThreadLocal<Task> tlCurrentTask = new ThreadLocal<Task>();
private HDC() {
}
public static Task start(String name) {
Task rootTask = tlRootTask.get();
if (rootTask != null) {
throw new IllegalStateException("The root task was already started.");
}
rootTask = new DefaultTaskImpl(null, name);
tlRootTask.set(rootTask);
return rootTask;
}
public static boolean isStarted() {
return (tlRootTask.get() != null);
}
public static Task getRootTask() {
Task rootTask = tlRootTask.get();
return (rootTask != null ? rootTask : NOOP_TASK);
}
public static Task getCurrentTask() {
Task current = tlCurrentTask.get();
if (current != null) {
return current;
}
return getRootTask();
}
public static void setCurrentTask(Task currentTask) {
tlCurrentTask.set(currentTask);
}
public static void cleanUp() {
tlCurrentTask.remove();
tlRootTask.remove();
}
}
/**
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.hst.diagnosis;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
/**
* NOOPTaskImpl
*/
class NOOPTaskImpl implements Task {
private boolean stopped;
NOOPTaskImpl() {
}
@Override
public String getName() {
return "<noop/>";
}
@Override
public Map<String, Object> getAttributeMap() {
return Collections.emptyMap();
}
@Override
public Enumeration<String> getAttributeNames() {
List<String> emptyAttrNames = Collections.emptyList();
return Collections.enumeration(emptyAttrNames);
}
@Override
public void setAttribute(String key, Object value) {
}
@Override
public Object getAttribute(String key) {
return null;
}
@Override
public Object removeAttribute(String key) {
return null;
}
@Override
public Task getParentTask() {
return null;
}
@Override
public Task startSubtask(String name) {
HDC.setCurrentTask(this);
return this;
}
@Override
public void stop() {
stopped = true;
}
@Override
public Collection<Task> getChildTasks() {
return Collections.emptyList();
}
@Override
public boolean isRunning() {
return !stopped;
}
@Override
public long getDurationTimeMillis() {
return 0L;
}
}
/**
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.hst.diagnosis;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Map;
/**
* A unit of execution.
* A task may start a subtask, and may contain multiple child subtasks.
* Each task may have attributes map of necessary data needed for diagnostics.
* By the way, the root task should be given by the container.
*/
public interface Task {
/**
* returns the task name
* @return
*/
String getName();
/**
* Returns attribute map which is unmodifiable. So, do not try to put or remove items directly from the returned map.
* @return
*/
Map<String, Object> getAttributeMap();
/**
* Enumerates the attribute names
*/
public Enumeration<String> getAttributeNames();
/**
* Set an attribute for the task. The object <code>value</code> should have a proper #toString method
* as by default, the #toString method is used for displaying the object in the diagnostics.
* @param key attribute name
* @param value attribute value
*/
void setAttribute(String key, Object value);
/**
* Retrieve the attribute value by the attribute name. When not found, <code>null</code>
* is returned
*/
Object getAttribute(String key);
/**
* Removes the attribute by the attribute name. When an Object was
* removed for <code>key</code>, this object is returned. Otherwise <code>null</code> is returned.
*/
Object removeAttribute(String key);
/**
* @return Returns the parent task and <code>null</code> if this is the root task
*/
Task getParentTask();
/**
* Starts and returns a child subtask with the name.
* @param name
* @return
*/
Task startSubtask(String name);
/**
* Stops the task
*/
void stop();
/**
* Returns the child tasks collection
* @return
*/
Collection<Task> getChildTasks();
/**
* Returns true if the task was started but not stopped.
* @return
*/
boolean isRunning();
/**
* Returns the task execution duration time in milliseconds
* @return
*/
long getDurationTimeMillis();
}
/*
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.hst.diagnosis;
import java.util.BitSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class to get a pretty printed hierarchical task log
*/
public class TaskLogFormatUtils {
private static final Logger log = LoggerFactory.getLogger(TaskLogFormatUtils.class);
/**
* @return returns the <code>task</code> nicely hierarchical formatted
*/
public static String getTaskLog(Task task) {
return getTaskLog(task, -1);
}
/**
*
* @param task the task to log
* @param maxDepth the maximum depth until how deep child tasks should be logged. <code>maxDepth</code> of <code>-1</code>
* will log all descendant tasks, <code>maxDepth</code> of <code>0</code> only the rootTask, <code>maxDepth</code>
* of <code>1</code> the rootTask plus its direct children, etc.
* @return
*/
public static String getTaskLog(Task task, final int maxDepth) {
StringBuilder sb = new StringBuilder(256);
appendTaskLog(sb, task, 0, new BitSet(0), false, maxDepth);
return sb.toString();
}
private static void appendTaskLog(final StringBuilder sb,
final Task task,
final int depth,
final BitSet bitset,
final boolean lastChild,
final int maxDepth) {
if (maxDepth > -1 && depth > maxDepth) {
return;
}
BitSet hidePipeAt = new BitSet(depth);
hidePipeAt.or(bitset);
for (int i = 0; i < depth; i++) {
if (i > 0) {
if (hidePipeAt.get(i)) {
sb.append(" ");
} else {
sb.append("|");
}
}
sb.append(" ");
}
if (depth > 0) {
if (lastChild) {
sb.append("`");
hidePipeAt.set(depth);
} else {
sb.append("|");
}
}
try {
String msg = "- " + task.getName() + " (" + task.getDurationTimeMillis() + "ms): " + task.getAttributeMap();
sb.append(msg).append('\n');
} catch (Throwable e) {
if (log.isDebugEnabled()) {
log.warn("Exception during writing task", e);
} else {
log.warn("Exception during writing task : {}", e.toString());
}
}
int count = 0;
for (Task childTask : task.getChildTasks()) {
count++;
if (count == task.getChildTasks().size()) {
appendTaskLog(sb, childTask, depth + 1,hidePipeAt, true, maxDepth);
} else {
appendTaskLog(sb, childTask, depth + 1,hidePipeAt, false, maxDepth);
}
}
}
}
/**
* Copyright 2012-2015 Hippo B.V. (http://www.onehippo.com)
*
* 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 org.hippoecm.hst.diagnosis;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.hippoecm.hst.diagnosis.HDC;
import org.hippoecm.hst.diagnosis.Task;
import org.hippoecm.hst.diagnosis.TaskLogFormatUtils;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TestHDC
*/
public class TestHDC {
private static Logger log = LoggerFactory.getLogger(TestHDC.class);
private Valve1 valve1 = new Valve1();
private Valve2 valve2 = new Valve2();
private Component1 comp1 = new Component1();
private Component2 comp2 = new Component2();
private Query query1 = new Query();
private Query query2 = new Query();
@Test
public void testDefaultExample() throws Exception {
// first, the HST container will start the root task first somewhere. e.g., HstFilter or InitializationValve
Task rootTask = HDC.start("request-processing");
// when invoking each valve, HST container can start a subtask
{
Task valveTask = HDC.getCurrentTask().startSubtask("valve1");
valve1.execute();
// if the container started a subtask, then it should stop the task.
// in reality, it should use try ~ finally to guarantee this call.
valveTask.stop();
}
{
Task valveTask = HDC.getCurrentTask().startSubtask("valve2");
valve2.execute();
valveTask.stop();
}
// also the container will stop the root task.
rootTask.stop();
// all the task execution information can be collected and reported later (maybe in another valve before cleanupValve)
final String logSummary = logSummary();
assertTrue(logSummary.contains("valve2"));
final String logSummaryWithDepth0 = logSummary(0);
assertFalse(logSummaryWithDepth0.contains("valve2"));
final String logSummaryWithDepthMinus1 = logSummary(-1);
assertTrue(logSummaryWithDepthMinus1.contains("valve2"));
final String logSummaryWithDepth1 = logSummary(1);
assertTrue(logSummaryWithDepth1.contains("valve2"));
// clean up all the stored thread context information..
HDC.cleanUp();
}
@Test
public void testNOOPExample() throws Exception {
// when invoking each valve, HST container can start a subtask
{
Task valveTask = HDC.getCurrentTask().startSubtask("valve1");
valve1.execute();
// if the container started a subtask, then it should stop the task.
// in reality, it should use try ~ finally to guarantee this call.
valveTask.stop();
}
{
Task valveTask = HDC.getCurrentTask().startSubtask("valve2");
valve2.execute();
valveTask.stop();
}
// all the task execution information can be collected and reported later (maybe in another valve before cleanupValve)
final String logSummary = logSummary();
assertFalse(logSummary.contains("valve2"));
// clean up all the stored thread context information..
HDC.cleanUp();
}
@Test
public void testRootTaskOnlyExample() throws Exception {
// first, the HST container will start the root task first somewhere. e.g., HstFilter or InitializationValve
Task rootTask = HDC.start("request-processing");
HDC.setCurrentTask(HDC.NOOP_TASK);
// when invoking each valve, HST container can start a subtask
{
Task valveTask = HDC.getCurrentTask().startSubtask("valve1");
valve1.execute();
// if the container started a subtask, then it should stop the task.
// in reality, it should use try ~ finally to guarantee this call.
valveTask.stop();
}
{
Task valveTask = HDC.getCurrentTask().startSubtask("valve2");
valve2.execute();
valveTask.stop();
}
// also the container will stop the root task.
rootTask.stop();
// all the task execution information can be collected and reported later (maybe in another valve before cleanupValve)
final String logSummary = logSummary();
assertFalse(logSummary.contains("valve2"));
// clean up all the stored thread context information..
HDC.cleanUp();
}
private String logSummary() {
Task rootTask = HDC.getRootTask();
return TaskLogFormatUtils.getTaskLog(rootTask);
}
private String logSummary(final int depth) {
Task rootTask = HDC.getRootTask();
return TaskLogFormatUtils.getTaskLog(rootTask, depth);
}
private void sleepRandom(long max) {
try {
Thread.sleep(Math.abs(Math.round(Math.random() * max)));
} catch (InterruptedException e) {
}
}
class Valve1 {
public void execute() {
sleepRandom(10);
// A valve can also start a subtask from its current context task.
Task compTask = HDC.getCurrentTask().startSubtask("comp1");
Task compTaskA = HDC.getCurrentTask().startSubtask("comp1A");