package org.netbeans.gradle.project.license;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jtrim.cancel.Cancellation;
import org.jtrim.cancel.CancellationToken;
import org.jtrim.concurrent.CancelableTask;
import org.jtrim.concurrent.MonitorableTaskExecutor;
import org.jtrim.concurrent.TaskExecutor;
import org.jtrim.concurrent.TaskExecutors;
import org.jtrim.property.PropertySource;
import org.jtrim.utils.ExceptionHelper;
import org.netbeans.gradle.project.properties.NbProperties;
import org.netbeans.gradle.project.util.CloseableAction;
import org.netbeans.gradle.project.util.NbBiFunction;
public final class LicenseManager<T> {
private static final Logger LOGGER = Logger.getLogger(LicenseManager.class.getName());
private final Impl<T, ?, ?> impl;
public <LK, LD extends LicenseDef> LicenseManager(
TaskExecutor executor,
LicenseStore<LD> licenseStore,
NbBiFunction<? super T, ? super LicenseHeaderInfo, ? extends LK> licenseKeyFactory,
NbBiFunction<? super T, ? super LK, ? extends LD> licenseDefFactory) {
this.impl = new Impl<>(executor, licenseStore, licenseKeyFactory, licenseDefFactory);
}
public String tryGetRegisteredLicenseName(T ownerModel, LicenseHeaderInfo headerInfo) {
return impl.tryGetRegisteredLicenseName(ownerModel, headerInfo);
}
public PropertySource<CloseableAction> getRegisterListenerAction(
PropertySource<? extends T> modelProperty,
PropertySource<? extends LicenseHeaderInfo> headerProperty) {
ExceptionHelper.checkNotNullArgument(modelProperty, "modelProperty");
ExceptionHelper.checkNotNullArgument(headerProperty, "headerProperty");
return NbProperties.combine(headerProperty, modelProperty, new NbBiFunction<LicenseHeaderInfo, T, CloseableAction>() {
@Override
public CloseableAction apply(LicenseHeaderInfo headerInfo, T model) {
return getRegisterListenerAction(model, headerInfo);
}
});
}
private CloseableAction getRegisterListenerAction(
final T ownerModel,
final LicenseHeaderInfo header) {
ExceptionHelper.checkNotNullArgument(ownerModel, "ownerModel");
return new CloseableAction() {
@Override
public CloseableAction.Ref open() {
return impl.registerLicense(ownerModel, header);
}
};
}
private static final class Impl<T, LK, LD extends LicenseDef> {
private final MonitorableTaskExecutor syncExecutor;
private final LicenseStore<LD> licenseStore;
private final NbBiFunction<? super T, ? super LicenseHeaderInfo, ? extends LK> licenseKeyFactory;
private final NbBiFunction<? super T, ? super LK, ? extends LD> licenseDefFactory;
private final Map<LK, RegisteredLicense<LD>> licenseRegistartions;
public Impl(
TaskExecutor executor,
LicenseStore<LD> licenseStore,
NbBiFunction<? super T, ? super LicenseHeaderInfo, ? extends LK> licenseKeyFactory,
NbBiFunction<? super T, ? super LK, ? extends LD> licenseDefFactory) {
ExceptionHelper.checkNotNullArgument(licenseStore, "licenseStore");
ExceptionHelper.checkNotNullArgument(licenseKeyFactory, "licenseKeyFactory");
ExceptionHelper.checkNotNullArgument(licenseDefFactory, "licenseDefFactory");
this.syncExecutor = TaskExecutors.inOrderExecutor(executor);
this.licenseStore = licenseStore;
this.licenseKeyFactory = licenseKeyFactory;
this.licenseDefFactory = licenseDefFactory;
this.licenseRegistartions = new HashMap<>();
}
public String tryGetRegisteredLicenseName(T ownerModel, LicenseHeaderInfo headerInfo) {
ExceptionHelper.checkNotNullArgument(ownerModel, "ownerModel");
ExceptionHelper.checkNotNullArgument(headerInfo, "headerInfo");
LK key = tryGetLicenseKey(ownerModel, headerInfo);
RegisteredLicense<LD> registration = key != null
? licenseRegistartions.get(key)
: null;
String licenseId = registration != null
? registration.getLicenseId()
: headerInfo.getLicenseName();
return licenseStore.containsLicense(licenseId) ? licenseId : null;
}
private void removeLicense(RegisteredLicense<LD> registration) throws IOException {
assert syncExecutor.isExecutingInThis();
licenseStore.removeLicense(registration.getLicenseId());
}
private void addLicense(RegisteredLicense<LD> registration) throws IOException {
assert syncExecutor.isExecutingInThis();
licenseStore.addLicense(registration.licenseDef);
}
private void doUnregister(final LK key) {
syncExecutor.execute(Cancellation.UNCANCELABLE_TOKEN, new CancelableTask() {
@Override
public void execute(CancellationToken cancelToken) throws IOException {
RegisteredLicense<LD> registration = licenseRegistartions.get(key);
if (registration == null) {
LOGGER.log(Level.WARNING, "Too many unregister call to LicenseManager.", new Exception());
return;
}
if (registration.release()) {
licenseRegistartions.remove(key);
removeLicense(registration);
}
}
}, null);
}
private void doRegister(final T ownerModel, final LK key) {
syncExecutor.execute(Cancellation.UNCANCELABLE_TOKEN, new CancelableTask() {
@Override
public void execute(CancellationToken cancelToken) throws IOException {
RegisteredLicense<LD> registration = licenseRegistartions.get(key);
if (registration == null) {
registration = new RegisteredLicense<>(getLicenseDef(ownerModel, key));
licenseRegistartions.put(key, registration);
addLicense(registration);
}
else {
registration.use();
}
}
}, null);
}
public CloseableAction.Ref registerLicense(T ownerModel, LicenseHeaderInfo header) {
ExceptionHelper.checkNotNullArgument(ownerModel, "ownerModel");
if (header == null) {
return CloseableAction.CLOSED_REF;
}
final LK key = tryGetLicenseKey(ownerModel, header);
if (key == null) {
return CloseableAction.CLOSED_REF;
}
doRegister(ownerModel, key);
return new CloseableAction.Ref() {
private final AtomicBoolean unregistered = new AtomicBoolean(false);
@Override
public void close() {
if (unregistered.compareAndSet(false, true)) {
doUnregister(key);
}
}
};
}
private LK tryGetLicenseKey(T ownerModel, LicenseHeaderInfo headerInfo) {
return licenseKeyFactory.apply(ownerModel, headerInfo);
}
private LD getLicenseDef(T ownerModel, LK key) {
return licenseDefFactory.apply(ownerModel, key);
}
}
private static final class RegisteredLicense<LD extends LicenseDef> {
private final LD licenseDef;
private int useCount;
public RegisteredLicense(LD licenseDef) {
this.useCount = 1;
this.licenseDef = licenseDef;
}
public String getLicenseId() {
return licenseDef.getLicenseId();
}
public void use() {
useCount++;
}
public boolean release() {
useCount--;
return useCount <= 0;
}
}
}