Dépôt officiel du code source de l'ERP OpenConcerto



Blame | Last modification | View Log | RSS feed

 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at See the License for the specific
 * language governing permissions and limitations under the License.
 * When distributing the software, include this License Header Notice in each file.
 package org.openconcerto.sql.view;

import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.RowRef;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.SQLCache;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.ThreadFactory;
import org.openconcerto.utils.cache.CacheResult;

import java.util.Collections;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;

public final class RowMetadataCache {
    private static RowMetadata createMD(final SQLRowAccessor r, final SQLField creation, final SQLField userCreate, final SQLField modification, final SQLField userModify) {
        return new RowMetadata(getDateMD(r, creation), getForeignIntegerMD(r, userCreate), getDateMD(r, modification), getForeignIntegerMD(r, userModify));

    private static Date getDateMD(final SQLRowAccessor row, final SQLField f) {
        return f == null ? null : row.getObjectAs(f.getName(), Date.class);

    private static Integer getForeignIntegerMD(final SQLRowAccessor row, final SQLField f) {
        if (f == null)
            return null;
        final Number n = row.getNonEmptyForeignIDNumber(f.getName());
        return n == null ? null : n.intValue();

    private final SQLCache<RowRef, RowMetadata> cache;
    private final ExecutorService exec;

    public RowMetadataCache(final int timeoutInSeconds, final int maxCount, final String name) {
        this.cache = new SQLCache<>(timeoutInSeconds, maxCount, "Cache of metadata for " + name);
        this.exec = Executors.newSingleThreadExecutor(new ThreadFactory("Metadata cache thread for " + name, true));

    public final RowMetadata get(final RowRef ref) {
        final CacheResult<RowMetadata> cacheRes = this.cache.get(ref);
        if (cacheRes.getState() == CacheResult.State.INTERRUPTED) {
            // shouldn't happen since we don't use check()/removeRunning()
            throw new RTInterruptedException("interrupted while waiting for the cache");
        } else if (cacheRes.getState() == CacheResult.State.VALID) {
            final RowMetadata res = cacheRes.getRes();
            assert res != null : "Null ambiguity";
            return res;
        } else {
            return null;

    public final void fetch(final RowRef ref, final Set<Number> withAdditionalIDs) {
        this.exec.submit(() -> {
            final CacheResult<RowMetadata> cacheResult = this.cache.get(ref);
            if (cacheResult.getState() == CacheResult.State.VALID)
            final SQLTable table = ref.getTable();
            final SQLField creationDateField = table.getCreationDateField();
            final SQLField creationUserField = table.getCreationUserField();
            final SQLField modifDateField = table.getModifDateField();
            final SQLField modifUserField = table.getModifUserField();
            final SQLSelect sel = new SQLSelect();
            sel.addAllSelect(creationDateField, creationUserField, modifDateField, modifUserField);
            sel.setWhere(Where.inValues(table.getKey(), withAdditionalIDs));

            final Set<SQLTable> data = Collections.singleton(table);

            // wait :
            // 1. check errors
            // 2. avoid fetching the same IDs more than once
            try {
                for (final SQLRowAccessor v : SQLRowListRSH.execute(sel)) {
                    this.cache.put(v.getRowRef(), createMD(v, creationDateField, creationUserField, modifDateField, modifUserField), data);
            } catch (Exception exn) {
                Log.get().log(Level.WARNING, "Couldn't fetch metadata for " + ref, exn);