/*
* Copyright (C) 2012 Red Hat, Inc. and/or its affiliates.
*
* 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.jboss.errai.config.rebind;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.typeinfo.TypeOracleException;
import org.jboss.errai.common.metadata.ScannerSingleton;
import org.jboss.errai.common.rebind.CacheUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @author Mike Brock
*/
public final class AsyncGenerators {
private static final Logger log = LoggerFactory.getLogger(AsyncGenerators.class);
private AsyncGenerators() {
}
private static final Object lock = new Object();
private static volatile boolean started = false;
private static volatile GeneratorContext currentContext;
private static final Map<Class, AsyncCodeGenerator> codeGenerators = new ConcurrentHashMap<Class, AsyncCodeGenerator>();
private static final Map<Class, Future<String>> activeFutures = new ConcurrentHashMap<Class, Future<String>>();
private static final Set<Object> startingNotifiedSet = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
private static final Set<Object> startedNotifiedSet = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
static Future<String> getFutureFor(final AsyncGenerationJob job) {
synchronized (lock) {
startAll(job);
if (!codeGenerators.containsKey(job.getInterfaceType())) {
throw new RuntimeException("no generator found for interface: " + job.getInterfaceType().getName());
}
if (activeFutures.containsKey(job.getInterfaceType())) {
return activeFutures.get(job.getInterfaceType());
}
else {
throw new RuntimeException("could not find future for interface: " + job.getInterfaceType().getName());
}
}
}
public static Future<String> getFutureFor(final Class clazz) {
synchronized (lock) {
if (!codeGenerators.containsKey(clazz)) {
throw new RuntimeException("no generator found for interface: " + clazz.getName());
}
if (activeFutures.containsKey(clazz)) {
return activeFutures.get(clazz);
}
else {
throw new RuntimeException("could not find future for interface: " + clazz.getName());
}
}
}
private static class FutureWrapper implements Future<String> {
private final Class interfaceType;
private final Future<String> delegate;
private FutureWrapper(final Class interfaceType, final Future<String> delegate) {
this.interfaceType = interfaceType;
this.delegate = delegate;
}
@Override
public boolean cancel(final boolean mayInterruptIfRunning) {
return delegate.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return delegate.isCancelled();
}
@Override
public boolean isDone() {
return delegate.isDone();
}
@Override
public String get() throws InterruptedException, ExecutionException {
final String val = delegate.get();
activeFutures.remove(interfaceType);
return val;
}
@Override
public String get(final long timeout, final TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
final String val = delegate.get(timeout, unit);
activeFutures.remove(interfaceType);
return val;
}
}
private static void startAll(final AsyncGenerationJob job) {
synchronized (lock) {
if (started && job.getGeneratorContext() != currentContext) {
codeGenerators.clear();
activeFutures.clear();
startedNotifiedSet.clear();
startingNotifiedSet.clear();
started = false;
}
if (!started) {
started = true;
EnvUtil.recordEnvironmentState();
CacheUtil.clearAll();
currentContext = job.getGeneratorContext();
notifyStarting(job);
notifyStarted(job);
final Set<Class<?>> typesAnnotatedWith = ScannerSingleton.getOrCreateInstance().getTypesAnnotatedWith(GenerateAsync.class);
for (final Class<?> cls : typesAnnotatedWith) {
try {
final AsyncCodeGenerator asyncCodeGenerator
= cls.asSubclass(AsyncCodeGenerator.class).newInstance();
final GenerateAsync generateAsync = cls.getAnnotation(GenerateAsync.class);
try {
job.getGeneratorContext().getTypeOracle().getType(generateAsync.value().getName());
codeGenerators.put(generateAsync.value(), asyncCodeGenerator);
log.info("discovered async generator " + cls.getName() + "; for type: " + generateAsync.value().getName());
}
catch (TypeOracleException e) {
codeGenerators.remove(generateAsync.value());
// e.printStackTrace();
// ignore because not inherited in an active module.
}
}
catch (Throwable e) {
log.error("Generator failed to launch", e);
}
}
for (final Map.Entry<Class, AsyncCodeGenerator> entry : codeGenerators.entrySet()) {
final Future<String> value = entry.getValue().generateAsync(job.getTreeLogger(), job.getGeneratorContext());
activeFutures.put(entry.getKey(), new FutureWrapper(entry.getKey(), value));
}
}
else {
notifyStarted(job);
}
}
}
private static void notifyStarting(AsyncGenerationJob job) {
if (startingNotifiedSet.contains(job)) {
return;
}
startingNotifiedSet.add(job);
job.notifyStarting();
}
private static void notifyStarted(AsyncGenerationJob job) {
if (startedNotifiedSet.contains(job)) {
return;
}
startedNotifiedSet.add(job);
job.notifyStarted();
}
}