Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions jdave-core/src/java/jdave/Parallel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2011 the original author or authors.
*
* 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 jdave;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Mark specification contexts suitable for parallel execution.
* <p/>
* To disable either of context or behaviour parallelization, set the corresponding
* of parameter to <code>1</code>.
* <p/>
* The maximum number of active threads on executing behaviours is thus:
* <code>contextThreads * behaviourThreads</code>.
* @author Kim Blomqvist
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Parallel {

public static int defaultContextThreads = 1;
public static int defaultBehaviourThreads = 2;

/**
* Number of threads to use for the running of <b>Contexts</b> of a specification.
* @return defaults to 4.
*/
int contextThreads() default defaultContextThreads;

/**
* Number of threads to use for the running of <b>Behaviours</b> of each Context. Note:
* Each context of a Specification gets its own set of behaviour executing threads.
* @return the number of threads executing behaviours of each context. Defaults to 8.
*/
int behaviourThreads() default defaultBehaviourThreads;

}
73 changes: 41 additions & 32 deletions jdave-core/src/java/jdave/runner/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@
package jdave.runner;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import java.util.concurrent.Callable;
import jdave.Parallel;
import jdave.Specification;
import jdave.support.ParallelExecutor;
import jdave.support.Reflection;

/**
* @author Joni Freeman
* @author Pekka Enberg
* @author Kim Blomqvist
*/
public abstract class Context {
private final Class<? extends Specification<?>> specType;
private final Class<?> contextType;
private ISpecIntrospection introspection;
private ISpecExecutor executor;

public Context(final Class<? extends Specification<?>> specType, final Class<?> contextType) {
this.specType = specType;
Expand All @@ -43,50 +46,56 @@ protected abstract Behavior newBehavior(Method method,
Class<? extends Specification<?>> specType, Class<?> contextType);

void run(final ISpecVisitor callback) {
final ISpecExecutor runExecutor = getExecutor();
for (final Method method : ClassMemberSorter.getMethods(contextType)) {
if (isBehavior(method)) {
callback.onBehavior(newBehavior(method, specType, contextType));
runExecutor.schedule(new Callable<Void>() {
public Void call() throws Exception {
callback.onBehavior(newBehavior(method, specType, contextType));
return null;
}
});
}
}
runExecutor.getResults();
}

private boolean isBehavior(final Method method) {
return newIntrospection().isBehavior(method);
return getIntrospection().isBehavior(method);
}

public boolean isContextClass() {
return newIntrospection().isContextClass(specType, contextType);
return getIntrospection().isContextClass(specType, contextType);
}

private ISpecIntrospection newIntrospection() {
try {
Class<?> clazz = specType;
do {
final Collection<Class<?>> types = typesOf(clazz);
for (final Class<?> type : types) {
if (hasStrategy(type)) {
return type.getAnnotation(IntrospectionStrategy.class).value()
.newInstance();
}
}
} while ((clazz = clazz.getSuperclass()) != null);
} catch (final Exception e) {
throw new RuntimeException(e);
synchronized private ISpecIntrospection getIntrospection() {
if (null != introspection) {
return introspection;
}
return new DefaultSpecIntrospection();
IntrospectionStrategy strategy = Reflection.getAnnotation(specType, IntrospectionStrategy.class);
if (null != strategy) {
try {
introspection = strategy.value().newInstance();
} catch (final Exception e) {
throw new RuntimeException(e);
}
} else {
introspection = new DefaultSpecIntrospection();
}
return introspection;
}

private Collection<Class<?>> typesOf(final Class<?> clazz) {
final List<Class<?>> types = new ArrayList<Class<?>>();
types.add(clazz);
final Class<?>[] interfaces = clazz.getInterfaces();
for (final Class<?> anInterface : interfaces) {
types.add(anInterface);
synchronized protected ISpecExecutor getExecutor() {
if (null != executor) {
return executor;
}
Parallel parallelAnnotation = Reflection.getAnnotation(specType, Parallel.class);
if (null != parallelAnnotation && parallelAnnotation.behaviourThreads() > 1) {
executor = new ParallelExecutor(parallelAnnotation.behaviourThreads());
} else {
executor = SerialSpecExecutor.getInstance();
}
return types;
return executor;
}

boolean hasStrategy(final Class<?> type) {
return type.isAnnotationPresent(IntrospectionStrategy.class);
}
}
10 changes: 8 additions & 2 deletions jdave-core/src/java/jdave/runner/DefaultSpecIntrospection.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
*/
package jdave.runner;

import org.junit.Ignore;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import jdave.Parallel;
import jdave.support.Reflection;
import org.junit.Ignore;

/**
* @author Joni Freeman
Expand Down Expand Up @@ -56,6 +57,11 @@ public boolean isContextClass(final Class<?> specType, final Class<?> possibleCo
if (!hasBehaviors(possibleContext)) {
return false;
}
if (Reflection.getAnnotation(possibleContext, Parallel.class) != null) {
throw new UnsupportedOperationException(
"@Parallel only supported on Specifications." +
" It was found on context: " + possibleContext);
}
return isInnerClass(possibleContext);
}

Expand Down
29 changes: 29 additions & 0 deletions jdave-core/src/java/jdave/runner/ISpecExecutor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2011 the original author or authors.
*
* 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 jdave.runner;

import java.util.concurrent.Callable;

/**
* @author Kim Blomqvist
*/
public interface ISpecExecutor {

void schedule(Callable<Void> contextCallable);

void getResults();

}
42 changes: 42 additions & 0 deletions jdave-core/src/java/jdave/runner/SerialSpecExecutor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2011 the original author or authors.
*
* 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 jdave.runner;

import java.util.concurrent.Callable;

/**
* @author Kim Blomqvist
*/
public class SerialSpecExecutor implements ISpecExecutor {

private static final SerialSpecExecutor instance = new SerialSpecExecutor();

public static SerialSpecExecutor getInstance() {
return instance;
}

public void schedule(Callable<Void> contextCallable) {
try {
contextCallable.call();
} catch (Exception e) {
e.printStackTrace();
}
}

public void getResults() {
}

}
41 changes: 30 additions & 11 deletions jdave-core/src/java/jdave/runner/SpecRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import java.util.concurrent.Callable;
import jdave.Parallel;
import jdave.Specification;
import jdave.support.ParallelExecutor;
import jdave.support.Reflection;

import org.junit.Ignore;

/**
Expand Down Expand Up @@ -70,17 +71,25 @@ private <T> void runPublicStaticVoidMethodNamed(String name, Class<T> specType)
}
}

private <T extends Specification<?>> void runContexts(Class<T> specType, ISpecVisitor callback) {
for (Class<?> contextType : getContextsOf(specType)) {
Context context = new Context(specType, contextType) {
@Override
protected Behavior newBehavior(Method method,
Class<? extends Specification<?>> specType, Class<?> contextType) {
return new ExecutingBehavior(method, specType, contextType);
protected <T extends Specification<?>> void runContexts(final Class<T> specType, final ISpecVisitor callback) {
ISpecExecutor executor = getExecutor(specType);
final SpecRunner self = this;
for (final Class<?> contextType : getContextsOf(specType)) {
executor.schedule(new Callable<Void>() {
public Void call() {
Context context = new Context(specType, contextType) {
@Override
protected Behavior newBehavior(Method method,
Class<? extends Specification<?>> specType, Class<?> contextType) {
return new ExecutingBehavior(method, specType, contextType);
}
};
self.run(callback, context);
return null;
}
};
run(callback, context);
});
}
executor.getResults();
}

private void run(ISpecVisitor callback, Context context) {
Expand Down Expand Up @@ -113,4 +122,14 @@ private boolean annotationIsPresent(Class<?> clazz, Class<? extends Annotation>
}
return false;
}

protected ISpecExecutor getExecutor(final Class<?> specType) {
Parallel parallelAnnotation = Reflection.getAnnotation(specType, Parallel.class);
if (null != parallelAnnotation && parallelAnnotation.contextThreads() > 1) {
return new ParallelExecutor(parallelAnnotation.contextThreads());
} else {
return SerialSpecExecutor.getInstance();
}
}

}
Loading