/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2016 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.server.http.webapp.symmetricDS; import java.util.LinkedList; import java.util.concurrent.Callable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jumpmind.db.model.Table; import org.jumpmind.symmetric.io.data.CsvData; import org.jumpmind.symmetric.io.data.DataContext; import org.jumpmind.symmetric.io.data.DataEventType; import org.jumpmind.symmetric.io.data.writer.DatabaseWriterFilterAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Component; import fr.gael.dhus.database.object.Product; import fr.gael.dhus.database.object.User; import fr.gael.dhus.datastore.processing.fair.FairThreadPoolTaskExecutor; import fr.gael.dhus.service.ProductService; import fr.gael.dhus.service.SearchService; import fr.gael.dhus.service.UserService; @Component public class SolrReplicationExtensionPoint extends DatabaseWriterFilterAdapter { private static final Logger LOGGER = LogManager.getLogger(); @Autowired private SearchService searchService; @Autowired private ProductService productService; @Autowired private UserService userService; @Autowired private CacheManager cacheManager; @Autowired private FairThreadPoolTaskExecutor taskExecutor; private final LinkedList<Long> newProducts = new LinkedList<>(); private final LinkedList<Long> productsToIndex = new LinkedList<>(); private final LinkedList<Long> productsIndexationList = new LinkedList<>(); private final LinkedList<String> usersToCacheUpdate = new LinkedList<> (); private boolean clearProductsCache = false; private static String lastTable = ""; @Override public void afterWrite(DataContext context, Table table, CsvData data) { if (LOGGER.isDebugEnabled () && table.getName () != lastTable) { LOGGER.debug ("Adding "+table.getName ()); lastTable = table.getName (); } if (table.getName ().equalsIgnoreCase ("USERS") && (data.getDataEventType () == DataEventType.INSERT || data.getDataEventType () == DataEventType.UPDATE)) { int idIdx = table.getColumnIndex("UUID"); String[] rowData = data.getParsedData(CsvData.ROW_DATA); String id = rowData[idIdx]; if (!usersToCacheUpdate.contains(id)) { usersToCacheUpdate.add(id); } if (data.getDataEventType () == DataEventType.INSERT) { int loginIdx = table.getColumnIndex("LOGIN"); LOGGER.info("User '{}' was replicated", rowData[loginIdx]); } } if (table.getName().equalsIgnoreCase("USER_RESTRICTIONS") || table.getName().equalsIgnoreCase("USER_ROLES")) { int idIdx = table.getColumnIndex("USER_UUID"); String[] rowData; if ((data.getDataEventType() == DataEventType.INSERT || data.getDataEventType() == DataEventType.UPDATE)) { rowData = data.getParsedData(CsvData.ROW_DATA); } else { rowData = data.getParsedData(CsvData.OLD_DATA); } String id = rowData[idIdx]; if (!usersToCacheUpdate.contains(id)) { usersToCacheUpdate.add(id); } } if (table.getName().equalsIgnoreCase("METADATA_INDEXES") && ( data.getDataEventType() == DataEventType.INSERT || data.getDataEventType() == DataEventType.UPDATE)) { int idIdx = table.getColumnIndex("PRODUCT_ID"); String[] rowData = data.getParsedData(CsvData.ROW_DATA); Long id = new Long(rowData[idIdx]); if (!productsToIndex.contains(id)) { productsToIndex.add(id); } } if (table.getName().equalsIgnoreCase("PRODUCTS") && data.getDataEventType() == DataEventType.UPDATE) { int idIdx = table.getColumnIndex("ID"); String[] rowData = data.getParsedData(CsvData.ROW_DATA); Long id = new Long(rowData[idIdx]); if (!productsToIndex.contains(id)) { productsToIndex.add(id); } int identifierIdx = table.getColumnIndex("IDENTIFIER"); LOGGER.info("Product '{}' was replicated", rowData[identifierIdx]); } if (table.getName().equalsIgnoreCase("PRODUCTS") && data.getDataEventType() == DataEventType.INSERT) { int idIdx = table.getColumnIndex("ID"); String[] rowData = data.getParsedData(CsvData.ROW_DATA); Long id = new Long(rowData[idIdx]); newProducts.add(id); int identifierIdx = table.getColumnIndex("IDENTIFIER"); String identifier = rowData[identifierIdx]; if (identifier != null) { LOGGER.info("Product '{}' was replicated", identifier); } } if (table.getName().equalsIgnoreCase("COLLECTION_PRODUCT")) { int idIdx = table.getColumnIndex("PRODUCTS_ID"); String[] rowData = data.getDataEventType() == DataEventType.DELETE ? data.getParsedData(CsvData.OLD_DATA) : data.getParsedData(CsvData.ROW_DATA); Long id = new Long(rowData[idIdx]); if (!productsIndexationList.contains(id)) { productsIndexationList.add(id); } } } @Override public boolean beforeWrite(DataContext context, Table table, CsvData data) { if ((table.getName().equalsIgnoreCase("PRODUCTS") && data.getDataEventType() == DataEventType.DELETE)) { int idIdx = table.getColumnIndex("ID"); String[] rowData = data.getParsedData(CsvData.OLD_DATA); String id = rowData[idIdx]; Product product = productService.getProductNoCache (Long.parseLong (id)); searchService.remove(product); Cache cache = cacheManager.getCache ("product"); if (cache != null) { synchronized (cache) { if (cache.get (product.getUuid ()) != null) { cache.evict (product.getUuid ()); } if (cache.get (product.getId ()) != null) { cache.evict (product.getId ()); } } } cache = cacheManager.getCache ("product_count"); if (cache != null) { synchronized (cache) { Integer old_value = cache.get ("all", Integer.class); // removing all other keys of cache cache.clear (); if (old_value != null) { cache.put ("all", (old_value - 1)); } } } clearProductsCache = true; } if ((table.getName().equalsIgnoreCase("USERS") && data.getDataEventType() == DataEventType.DELETE)) { int idIdx = table.getColumnIndex("UUID"); String[] rowData = data.getParsedData(CsvData.OLD_DATA); String uId = rowData[idIdx]; User user = userService.getUserNoCache (uId); if (user != null) { Cache cache = cacheManager.getCache ("user"); if (cache != null) { synchronized (cache) { if (cache.get (user.getUUID ()) != null) { cache.evict (user.getUUID ()); } } } cache = cacheManager.getCache ("userByName"); if (cache != null) { synchronized (cache) { if (cache.get (user.getUsername ()) != null) { cache.evict (user.getUsername ()); } } } } } return true; } @Override public void batchCommitted(DataContext context) { String uId; while ((uId = usersToCacheUpdate.poll()) != null) { User user = userService.getUserNoCache (uId); if (user == null) { continue; } Cache cache = cacheManager.getCache ("user"); if (cache != null) { synchronized (cache) { if (cache.get (user.getUUID ()) != null) { cache.put (user.getUUID (), user); } } } cache = cacheManager.getCache ("userByName"); if (cache != null) { synchronized (cache) { if (cache.get (user.getUsername ()) != null) { cache.put (user.getUsername (), user); } } } } if (!newProducts.isEmpty() || !productsIndexationList.isEmpty() || !productsToIndex.isEmpty() || clearProductsCache) { Cache cache = cacheManager.getCache ("products"); if (cache != null) { cache.clear (); } clearProductsCache = false; } Long pId; while ((pId = productsToIndex.poll()) != null) { Product product = productService.getProductWithIndexes(pId); if (product == null) { continue; } // Add products to index them in // thread if (!productsIndexationList.contains(pId)) { productsIndexationList.add(pId); } Cache cache = cacheManager.getCache ("product"); if (cache != null) { synchronized (cache) { if (cache.get (product.getUuid ()) != null) { cache.put (product.getUuid (), product); } if (cache.get (product.getId ()) != null) { cache.put (product.getId (), product); } } } } while ((pId = newProducts.poll()) != null) { Cache cache = cacheManager.getCache ("product_count"); if (cache != null) { synchronized (cache) { Integer old_value = cache.get ("all", Integer.class); // removing all other keys of cache cache.clear (); if (old_value != null) { cache.put ("all", (old_value + 1)); } } } } taskExecutor.submit(new Callable<Void>() { @Override public Void call() throws Exception { Long pId; while ((pId = productsIndexationList.poll()) != null) { Product product = productService.getProductWithIndexes(pId); if (product == null) { continue; } searchService.index(product); } return null; } }); } }