<commit_before>/*
 * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the
 * @author tags. See the copyright.txt file in the distribution for a full
 * listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package org.zanata.service.impl;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import net.sf.ehcache.CacheManager;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.zanata.cache.CacheWrapper;
import org.zanata.cache.EhcacheWrapper;
import org.zanata.common.LocaleId;
import org.zanata.dao.DocumentDAO;
import org.zanata.dao.TextFlowDAO;
import org.zanata.dao.TextFlowTargetDAO;
import org.zanata.events.TextFlowTargetStateEvent;
import org.zanata.model.HDocument;
import org.zanata.model.HTextFlowTarget;
import org.zanata.service.TranslationStateCache;
import org.zanata.service.ValidationFactoryProvider;
import org.zanata.service.ValidationService;
import org.zanata.webtrans.shared.model.DocumentId;
import org.zanata.webtrans.shared.model.DocumentStatus;
import org.zanata.webtrans.shared.model.ValidationAction;
import org.zanata.webtrans.shared.model.ValidationId;

import com.google.common.cache.CacheLoader;

/**
 * Default Implementation of the Translation State Cache.
 *
 * @author <NAME> <a
 *         href="mailto:<EMAIL>"><EMAIL></a>
 */
@Name("translationStateCacheImpl")
@Scope(ScopeType.APPLICATION)
public class TranslationStateCacheImpl implements TranslationStateCache {
    private static final String BASE = TranslationStateCacheImpl.class
            .getName();
    private static final String DOC_STATUS_CACHE_NAME = BASE
            + ".docStatusCache";
    private static final String TFT_VALIDATION_CACHE_NAME = BASE
            + ".targetValidationCache";

    @In
    private TextFlowDAO textFlowDAO;

    @In
    private DocumentDAO documentDAO;

    @In
    private TextFlowTargetDAO textFlowTargetDAO;

    @In
    private ValidationService validationServiceImpl;

    private CacheManager cacheManager;
    private CacheWrapper<TranslatedDocumentKey, DocumentStatus> docStatusCache;
    private CacheWrapper<Long, Map<ValidationId, Boolean>> targetValidationCache;
    private CacheLoader<TranslatedDocumentKey, DocumentStatus> docStatusLoader;
    private CacheLoader<Long, Map<ValidationId, Boolean>> targetValidationLoader;

    public TranslationStateCacheImpl() {
        // constructor for Seam
        this.docStatusLoader = new HTextFlowTargetIdLoader();
        this.targetValidationLoader = new HTextFlowTargetValidationLoader();
    }

    public TranslationStateCacheImpl(
            CacheLoader<TranslatedDocumentKey, DocumentStatus> docStatsLoader,
            CacheLoader<Long, Map<ValidationId, Boolean>> targetValidationLoader,
            TextFlowTargetDAO textFlowTargetDAO,
            ValidationService validationServiceImpl) {
        // constructor for testing
        this.docStatusLoader = docStatsLoader;
        this.targetValidationLoader = targetValidationLoader;
        this.textFlowTargetDAO = textFlowTargetDAO;
        this.validationServiceImpl = validationServiceImpl;
    }

    @Create
    public void create() {
        cacheManager = CacheManager.create();
        docStatusCache =
                EhcacheWrapper.create(DOC_STATUS_CACHE_NAME, cacheManager,
                        docStatusLoader);
        targetValidationCache =
                EhcacheWrapper.create(TFT_VALIDATION_CACHE_NAME, cacheManager,
                        targetValidationLoader);
    }

    @Destroy
    public void destroy() {
        cacheManager.shutdown();
    }

    /**
     * This method contains all logic to be run immediately after a Text Flow
     * Target has been successfully translated.
     */
    @Observer(TextFlowTargetStateEvent.EVENT_NAME)
    @Override
    public void textFlowStateUpdated(TextFlowTargetStateEvent event) {
        // updateDocStatusCache(event.getDocumentId(), event.getLocaleId(),
        // event.getTextFlowTargetId());

        // TODO enhance TextFlowTargetStateEvent and update this cache directly
        // (no database I/O) rather than invalidating
        invalidateDocLastTranslatedCache(event.getDocumentId(),
                event.getLocaleId());
        invalidateTargetValidationCache(event.getTextFlowTargetId());
    }

    @Override
    public DocumentStatus getDocumentStatus(Long documentId, LocaleId localeId) {
        return docStatusCache.getWithLoader(new TranslatedDocumentKey(
                documentId, localeId));
    }

    private void invalidateDocLastTranslatedCache(Long documentId,
            LocaleId localeId) {
        docStatusCache.remove(new TranslatedDocumentKey(documentId, localeId));
    }

    @Override
    public Boolean textFlowTargetHasWarningOrError(Long targetId,
            ValidationId validationId) {
        Map<ValidationId, Boolean> cacheEntry =
                targetValidationCache.getWithLoader(targetId);
        if (!cacheEntry.containsKey(validationId)) {
            Boolean result = loadTargetValidation(targetId, validationId);
            cacheEntry.put(validationId, result);
        }
        return cacheEntry.get(validationId);
    }

    private void updateDocStatusCache(Long documentId, LocaleId localeId,
            Long updatedTargetId) {
        DocumentStatus documentStatus =
                docStatusCache.get(new TranslatedDocumentKey(documentId,
                        localeId));
        HTextFlowTarget target =
                textFlowTargetDAO.findById(updatedTargetId, false);
        updateDocumentStatus(documentStatus, documentId, localeId, target);
    }

    private void invalidateTargetValidationCache(Long textFlowTargetId) {
        targetValidationCache.remove(textFlowTargetId);
    }

    private final class HTextFlowTargetIdLoader extends
            CacheLoader<TranslatedDocumentKey, DocumentStatus> {
        @Override
        public DocumentStatus load(TranslatedDocumentKey key) throws Exception {
            HTextFlowTarget target =
                    documentDAO.getLastTranslatedTarget(key.getDocumentId(),
                            key.getLocaleId());
            DocumentStatus documentStatus = new DocumentStatus();

            return updateDocumentStatus(documentStatus, key.getDocumentId(),
                    key.getLocaleId(), target);<commit_msg>Synchronize access to validation HashMap<commit_after>/*
 * Copyright 2010, Red Hat, Inc. and individual contributors as indicated by the
 * @author tags. See the copyright.txt file in the distribution for a full
 * listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package org.zanata.service.impl;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import net.sf.ehcache.CacheManager;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.zanata.cache.CacheWrapper;
import org.zanata.cache.EhcacheWrapper;
import org.zanata.common.LocaleId;
import org.zanata.dao.DocumentDAO;
import org.zanata.dao.TextFlowDAO;
import org.zanata.dao.TextFlowTargetDAO;
import org.zanata.events.TextFlowTargetStateEvent;
import org.zanata.model.HDocument;
import org.zanata.model.HTextFlowTarget;
import org.zanata.service.TranslationStateCache;
import org.zanata.service.ValidationFactoryProvider;
import org.zanata.service.ValidationService;
import org.zanata.webtrans.shared.model.DocumentId;
import org.zanata.webtrans.shared.model.DocumentStatus;
import org.zanata.webtrans.shared.model.ValidationAction;
import org.zanata.webtrans.shared.model.ValidationId;

import com.google.common.cache.CacheLoader;

/**
 * Default Implementation of the Translation State Cache.
 *
 * @author <NAME> <a
 *         href="mailto:<EMAIL>"><EMAIL></a>
 */
@Name("translationStateCacheImpl")
@Scope(ScopeType.APPLICATION)
public class TranslationStateCacheImpl implements TranslationStateCache {
    private static final String BASE = TranslationStateCacheImpl.class
            .getName();
    private static final String DOC_STATUS_CACHE_NAME = BASE
            + ".docStatusCache";
    private static final String TFT_VALIDATION_CACHE_NAME = BASE
            + ".targetValidationCache";

    @In
    private TextFlowDAO textFlowDAO;

    @In
    private DocumentDAO documentDAO;

    @In
    private TextFlowTargetDAO textFlowTargetDAO;

    @In
    private ValidationService validationServiceImpl;

    private CacheManager cacheManager;
    private CacheWrapper<TranslatedDocumentKey, DocumentStatus> docStatusCache;
    private CacheWrapper<Long, Map<ValidationId, Boolean>> targetValidationCache;
    private CacheLoader<TranslatedDocumentKey, DocumentStatus> docStatusLoader;
    private CacheLoader<Long, Map<ValidationId, Boolean>> targetValidationLoader;

    public TranslationStateCacheImpl() {
        // constructor for Seam
        this.docStatusLoader = new HTextFlowTargetIdLoader();
        this.targetValidationLoader = new HTextFlowTargetValidationLoader();
    }

    public TranslationStateCacheImpl(
            CacheLoader<TranslatedDocumentKey, DocumentStatus> docStatsLoader,
            CacheLoader<Long, Map<ValidationId, Boolean>> targetValidationLoader,
            TextFlowTargetDAO textFlowTargetDAO,
            ValidationService validationServiceImpl) {
        // constructor for testing
        this.docStatusLoader = docStatsLoader;
        this.targetValidationLoader = targetValidationLoader;
        this.textFlowTargetDAO = textFlowTargetDAO;
        this.validationServiceImpl = validationServiceImpl;
    }

    @Create
    public void create() {
        cacheManager = CacheManager.create();
        docStatusCache =
                EhcacheWrapper.create(DOC_STATUS_CACHE_NAME, cacheManager,
                        docStatusLoader);
        targetValidationCache =
                EhcacheWrapper.create(TFT_VALIDATION_CACHE_NAME, cacheManager,
                        targetValidationLoader);
    }

    @Destroy
    public void destroy() {
        cacheManager.shutdown();
    }

    /**
     * This method contains all logic to be run immediately after a Text Flow
     * Target has been successfully translated.
     */
    @Observer(TextFlowTargetStateEvent.EVENT_NAME)
    @Override
    public void textFlowStateUpdated(TextFlowTargetStateEvent event) {
        // updateDocStatusCache(event.getDocumentId(), event.getLocaleId(),
        // event.getTextFlowTargetId());

        // TODO enhance TextFlowTargetStateEvent and update this cache directly
        // (no database I/O) rather than invalidating
        invalidateDocLastTranslatedCache(event.getDocumentId(),
                event.getLocaleId());
        invalidateTargetValidationCache(event.getTextFlowTargetId());
    }

    @Override
    public DocumentStatus getDocumentStatus(Long documentId, LocaleId localeId) {
        return docStatusCache.getWithLoader(new TranslatedDocumentKey(
                documentId, localeId));
    }

    private void invalidateDocLastTranslatedCache(Long documentId,
            LocaleId localeId) {
        docStatusCache.remove(new TranslatedDocumentKey(documentId, localeId));
    }

    @Override
    public Boolean textFlowTargetHasWarningOrError(Long targetId,
            ValidationId validationId) {
        Map<ValidationId, Boolean> cacheEntry =
                targetValidationCache.getWithLoader(targetId);
        synchronized (cacheEntry) {
            if (!cacheEntry.containsKey(validationId)) {
                Boolean result = loadTargetValidation(targetId, validationId);
                cacheEntry.put(validationId, result);
            }
            return cacheEntry.get(validationId);
        }
    }

    private void updateDocStatusCache(Long documentId, LocaleId localeId,
            Long updatedTargetId) {
        DocumentStatus documentStatus =
                docStatusCache.get(new TranslatedDocumentKey(documentId,
                        localeId));
        HTextFlowTarget target =
                textFlowTargetDAO.findById(updatedTargetId, false);
        updateDocumentStatus(documentStatus, documentId, localeId, target);
    }

    private void invalidateTargetValidationCache(Long textFlowTargetId) {
        targetValidationCache.remove(textFlowTargetId);
    }

    private final class HTextFlowTargetIdLoader extends
            CacheLoader<TranslatedDocumentKey, DocumentStatus> {
        @Override
        public DocumentStatus load(TranslatedDocumentKey key) throws Exception {
            HTextFlowTarget target =
                    documentDAO.getLastTranslatedTarget(key.getDocumentId(),
                            key.getLocaleId());
            DocumentStatus documentStatus = new DocumentStatus();

            return updateDocumentStatus(documentStatus, key.getDocumentId(),
                    key.getLocaleId(), target);
{'fraction_non_alphanumeric': 0.07393125457083971, 'fraction_numerical': 0.0022604879994681205, 'mean_word_length': 3.4280247277009126, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4558583', 'n_tokens_mistral': 3966, 'n_tokens_neox': 3781, 'n_words': 979}