/** * Copyright (C) 2012-2017 52°North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If the program is linked with libraries which are licensed under one of * the following licenses, the combination of the program with the linked * library is not considered a "derivative work" of the program: * * - Apache License, version 2.0 * - Apache Software License, version 1.0 * - GNU Lesser General Public License, version 3 * - Mozilla Public License, versions 1.0, 1.1 and 2.0 * - Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed * under the aforementioned licenses, is permitted by the copyright holders * if the distribution is compliant with both the GNU General Public * License version 2 and the aforementioned licenses. * * 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 General * Public License for more details. */ package org.n52.sos.config.sqlite; import static org.hibernate.criterion.Restrictions.and; import static org.hibernate.criterion.Restrictions.eq; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.hibernate.Session; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.n52.sos.cache.ContentCache; import org.n52.sos.config.CapabilitiesExtensionManager; import org.n52.sos.config.sqlite.SQLiteManager.HibernateAction; import org.n52.sos.config.sqlite.SQLiteManager.ThrowingHibernateAction; import org.n52.sos.config.sqlite.SQLiteManager.ThrowingVoidHibernateAction; import org.n52.sos.config.sqlite.SQLiteManager.VoidHibernateAction; import org.n52.sos.config.sqlite.entities.Activatable; import org.n52.sos.config.sqlite.entities.CapabilitiesExtensionImpl; import org.n52.sos.config.sqlite.entities.OfferingExtensionIdentifier; import org.n52.sos.config.sqlite.entities.OfferingExtensionImpl; import org.n52.sos.config.sqlite.entities.StaticCapabilitiesImpl; import org.n52.sos.ds.ConnectionProviderException; import org.n52.sos.exception.NoSuchExtensionException; import org.n52.sos.exception.NoSuchOfferingException; import org.n52.sos.ogc.ows.OfferingExtension; import org.n52.sos.ogc.ows.StaticCapabilities; import org.n52.sos.ogc.ows.StringBasedCapabilitiesExtension; import org.n52.sos.service.Configurator; import org.n52.sos.util.LinkedListMultiMap; import org.n52.sos.util.ListMultiMap; public class ExtensionEnabledSQLiteSettingsManager extends SQLiteSettingsManager implements CapabilitiesExtensionManager { private static final Logger LOGGER = LoggerFactory.getLogger(ExtensionEnabledSQLiteSettingsManager.class); private final ReadWriteLock scLock = new ReentrantReadWriteLock(); private final ReadWriteLock ceLock = new ReentrantReadWriteLock(); private final ReadWriteLock oeLock = new ReentrantReadWriteLock(); private String cachedScId; private String cachedSc; private Map<String, StringBasedCapabilitiesExtension> cachedCe; private Map<String, Map<String, String>> cachedOe; private void checkOffering(final String offering) throws NoSuchOfferingException { if (!getCache().hasOffering(offering)) { throw new NoSuchOfferingException(offering); } } @Override protected <T> T execute(final HibernateAction<T> action) { try { return super.execute(action); } catch (ConnectionProviderException ex) { throw new RuntimeException(ex); } } @Override protected <T> T throwingExecute(ThrowingHibernateAction<T> action) throws NoSuchExtensionException { try { return super.throwingExecute(action); } catch (Exception ex) { if (ex instanceof NoSuchExtensionException) { throw (NoSuchExtensionException) ex; } else { throw new RuntimeException(ex); } } } @Override public void setActiveStaticCapabilities(final String identifier) throws NoSuchExtensionException { if (identifier != null) { throwingExecute(new SetActiveStaticCapabilitiesAction(identifier)); } else { execute(new DeactivateStaticCapabilitiesAction()); } scLock.writeLock().lock(); try { cachedScId = identifier; cachedSc = null; } finally { scLock.writeLock().unlock(); } } @Override public String getActiveStaticCapabilities() { scLock.readLock().lock(); try { if (cachedScId == null) { cachedScId = execute(new GetActiveStaticCapabilitiesAction()); } } finally { scLock.readLock().unlock(); } return cachedScId; } @Override public String getActiveStaticCapabilitiesDocument() { boolean load = false; scLock.readLock().lock(); try { if (cachedScId != null && cachedSc == null) { load = true; } } finally { scLock.readLock().unlock(); } if (load) { scLock.readLock().lock(); try { if (cachedScId != null && cachedSc == null) { cachedSc = execute(new GetActiveStaticCapabilitiesDocumentAction()); } } finally { scLock.readLock().unlock(); } } return cachedSc; } @Override public boolean isStaticCapabilitiesActive() { return getActiveStaticCapabilities() != null; } @Override public ListMultiMap<String, OfferingExtension> getOfferingExtensions() { return execute(new GetOfferingExtensionsAction()); } @Override public ListMultiMap<String, OfferingExtension> getActiveOfferingExtensions() { boolean load = false; oeLock.readLock().lock(); try { if (cachedOe == null) { load = true; } } finally { oeLock.readLock().unlock(); } if (load) { oeLock.writeLock().lock(); try { if (cachedOe == null) { final ListMultiMap<String, OfferingExtension> execute = execute(new GetActiveOfferingExtensionsAction()); cachedOe = new HashMap<>(execute.size()); for (final String offering : execute.keySet()) { final List<OfferingExtension> oes = execute.get(offering); final Map<String, String> map = new HashMap<>(oes.size()); for (final OfferingExtension oe : oes) { map.put(oe.getIdentifier(), oe.getExtension()); } cachedOe.put(offering, map); } } } finally { oeLock.writeLock().unlock(); } } final ListMultiMap<String, OfferingExtension> map = new LinkedListMultiMap<>(); for (final String offering : cachedOe.keySet()) { final Map<String, String> oes = cachedOe.get(offering); if (oes != null) { for (final Entry<String, String> oe : oes.entrySet()) { map.add(offering, new OfferingExtensionImpl(offering, oe.getKey(), oe.getValue())); } } } return map; } protected ContentCache getCache() { return Configurator.getInstance().getCache(); } @Override public void saveOfferingExtension(final String offering, final String identifier, final String value) throws NoSuchOfferingException { checkOffering(offering); execute(new SaveOfferingExtensionAction(offering, identifier, value)); oeLock.writeLock().lock(); try { if (cachedOe != null) { Map<String, String> forOffering = cachedOe.get(offering); if (forOffering == null) { cachedOe.put(offering, forOffering = new HashMap<>()); } forOffering.put(identifier, value); } } finally { oeLock.writeLock().unlock(); } } @Override public void disableOfferingExtension(final String offering, final String identifier, final boolean disabled) throws NoSuchExtensionException, NoSuchOfferingException { checkOffering(offering); throwingExecute(new SetActiveOfferingExtensionAction(offering, identifier, disabled)); oeLock.writeLock().lock(); try { if (cachedOe != null) { Map<String, String> forOffering = cachedOe.get(offering); if (forOffering == null) { cachedOe.put(offering, forOffering = new HashMap<>()); } if (disabled) { forOffering.remove(identifier); } else if (!forOffering.containsKey(identifier)) { final OfferingExtension oe = execute(new GetOfferingExtensionAction(offering, identifier)); forOffering.put(oe.getIdentifier(), oe.getExtension()); } } } finally { oeLock.writeLock().unlock(); } } @Override public void deleteOfferingExtension(final String offering, final String identifier) throws NoSuchOfferingException, NoSuchExtensionException { checkOffering(offering); throwingExecute(new DeleteOfferingExtensionAction(offering, identifier)); oeLock.writeLock().lock(); try { if (cachedOe != null && cachedOe.get(offering) != null) { if (cachedOe.get(offering).remove(identifier)!=null) { LOGGER.debug("Removed extension '{}' for offering '{}' from offering extension cache.",identifier,offering); } else { LOGGER.debug("Removing failed for extension '{}' for offering '{}' from offering extension cache.",identifier,offering); } } } finally { oeLock.writeLock().unlock(); } } @Override public Map<String, StringBasedCapabilitiesExtension> getActiveCapabilitiesExtensions() { boolean load = false; ceLock.readLock().lock(); try { if (cachedCe == null) { load = true; } } finally { ceLock.readLock().unlock(); } ceLock.writeLock().lock(); try { if (load) { if (cachedCe == null) { final Map<String, StringBasedCapabilitiesExtension> ext = execute(new GetActiveCapabilitiesExtensionAction()); cachedCe = new HashMap<>(ext.size()); for (final Entry<String, StringBasedCapabilitiesExtension> e : ext.entrySet()) { cachedCe.put(e.getKey(), new CapabilitiesExtensionImpl( e.getValue().getSectionName(), e.getValue().getExtension())); } } } } finally { ceLock.writeLock().unlock(); } return Collections.unmodifiableMap(cachedCe); } @Override public Map<String, StringBasedCapabilitiesExtension> getAllCapabilitiesExtensions() { return execute(new GetAllCapabilitiesExtensionAction()); } @Override public void saveCapabilitiesExtension(final String identifier, final String value) { execute(new SaveCapabilitesExtensionAction(identifier, value)); ceLock.writeLock().lock(); try { if (cachedCe != null) { cachedCe.put(identifier, new CapabilitiesExtensionImpl(identifier, value)); } } finally { ceLock.writeLock().unlock(); } } @Override public void disableCapabilitiesExtension(final String identifier, final boolean disabled) throws NoSuchExtensionException { throwingExecute(new DisableCapabiliesExtensionAction(identifier, disabled)); ceLock.writeLock().lock(); try { if (cachedCe != null) { if (disabled) { cachedCe.remove(identifier); } else { cachedCe.put(identifier, execute(new GetCapabilitiesExtensionAction(identifier))); } } } finally { ceLock.writeLock().unlock(); } } @Override public void deleteCapabiltiesExtension(final String identifier) throws NoSuchExtensionException { throwingExecute(new DeleteCapabilitiesExtensionAction(identifier)); ceLock.writeLock().lock(); try { if (cachedCe != null) { cachedCe.remove(identifier); } } finally { ceLock.writeLock().unlock(); } } @Override public Map<String, StaticCapabilities> getStaticCapabilities() { return execute(new GetStaticCapabilitiesAction()); } @Override public void saveStaticCapabilities(final String identifier, final String document) { execute(new SaveStaticCapabilitiesAction(identifier, document)); scLock.writeLock().lock(); try { if (cachedScId != null && cachedScId.equals(identifier)) { cachedSc = document; } } finally { scLock.writeLock().unlock(); } } @Override public void deleteStaticCapabilities(final String identifier) throws NoSuchExtensionException { throwingExecute(new DeleteStaticCapabilitiesAction(identifier)); scLock.writeLock().lock(); try { if (cachedScId != null && cachedScId.equals(identifier)) { cachedScId = null; cachedSc = null; } } finally { scLock.writeLock().unlock(); } } @Override public StaticCapabilities getStaticCapabilities(final String id) { scLock.readLock().lock(); try { if (cachedScId != null && cachedScId.equals(id) && cachedSc != null) { return new StaticCapabilitiesImpl(id, cachedSc); } } finally { scLock.readLock().unlock(); } return execute(new GetStaticCapabilitiesWithIdAction(id)); } private class SetActiveStaticCapabilitiesAction extends ThrowingVoidHibernateAction { private final String identifier; SetActiveStaticCapabilitiesAction(final String identifier) { this.identifier = identifier; } @Override protected void run(final Session session) throws NoSuchExtensionException { final StaticCapabilitiesImpl sci = (StaticCapabilitiesImpl) session .get(StaticCapabilitiesImpl.class, identifier); if (sci == null) { throw new NoSuchExtensionException(identifier); } if (!sci.isActive()) { final StaticCapabilitiesImpl cur = (StaticCapabilitiesImpl) session .createCriteria(StaticCapabilitiesImpl.class) .add(eq(StaticCapabilitiesImpl.ACTIVE, true)) .uniqueResult(); if (cur != null) { session.update(cur.setActive(false)); } session.update(sci.setActive(true)); } } } private class GetActiveStaticCapabilitiesAction implements HibernateAction<String> { @Override public String call(final Session session) { final StaticCapabilitiesImpl cur = (StaticCapabilitiesImpl) session .createCriteria(StaticCapabilitiesImpl.class) .add(eq(StaticCapabilitiesImpl.ACTIVE, true)) .uniqueResult(); return cur == null ? null : cur.getIdentifier(); } } private class GetActiveStaticCapabilitiesDocumentAction implements HibernateAction<String> { @Override public String call(final Session session) { final StaticCapabilitiesImpl cur = (StaticCapabilitiesImpl) session .createCriteria(StaticCapabilitiesImpl.class) .add(eq(StaticCapabilitiesImpl.ACTIVE, true)) .uniqueResult(); return cur == null ? null : cur.getDocument(); } } private class GetOfferingExtensionsAction implements HibernateAction<ListMultiMap<String, OfferingExtension>> { @Override public ListMultiMap<String, OfferingExtension> call(final Session session) { @SuppressWarnings("unchecked") final List<OfferingExtensionImpl> extensions = session.createCriteria(OfferingExtensionImpl.class).list(); final ListMultiMap<String, OfferingExtension> map = new LinkedListMultiMap<>( getCache().getOfferings().size()); for (final OfferingExtensionImpl extension : extensions) { LOGGER.debug("Loaded OfferingExtension: {}", extension); map.add(extension.getOfferingName(), extension); } return map; } } private class GetOfferingExtensionAction implements HibernateAction<OfferingExtension> { private final String offering; private final String identifier; GetOfferingExtensionAction(final String offering, final String identifier) { this.offering = offering; this.identifier = identifier; } @Override public OfferingExtension call(final Session session) { return (OfferingExtension) session.createCriteria(OfferingExtensionImpl.class) .add(and(eq(Activatable.COMPOSITE_KEY + "." + OfferingExtensionIdentifier.IDENTIFIER, identifier), eq(Activatable.COMPOSITE_KEY + "." + OfferingExtensionIdentifier.OFFERING, offering))) .uniqueResult(); } } private class GetActiveOfferingExtensionsAction implements HibernateAction<ListMultiMap<String, OfferingExtension>> { @Override public ListMultiMap<String, OfferingExtension> call(final Session session) { @SuppressWarnings("unchecked") final List<OfferingExtensionImpl> extensions = session.createCriteria(OfferingExtensionImpl.class) .add(eq(OfferingExtensionImpl.ACTIVE, true)).list(); final ListMultiMap<String, OfferingExtension> map = new LinkedListMultiMap<>( getCache().getOfferings().size()); for (final OfferingExtensionImpl extension : extensions) { LOGGER.debug("Loaded OfferingExtension: {}", extension); map.add(extension.getOfferingName(), extension); } return map; } } private class SaveOfferingExtensionAction extends VoidHibernateAction { private final String offering; private final String identifier; private final String value; SaveOfferingExtensionAction(final String offering, final String identifier, final String value) { this.offering = offering; this.identifier = identifier; this.value = value; } @Override protected void run(final Session session) { session.saveOrUpdate(new OfferingExtensionImpl(offering, identifier, value)); } } private class SetActiveOfferingExtensionAction extends ThrowingVoidHibernateAction { private final String offering; private final String identifier; private final boolean disabled; SetActiveOfferingExtensionAction(final String offering, final String identifier, final boolean disabled) { this.offering = offering; this.identifier = identifier; this.disabled = disabled; } @Override protected void run(final Session session) throws NoSuchExtensionException { final OfferingExtensionImpl oe = (OfferingExtensionImpl) session .get(OfferingExtensionImpl.class, new OfferingExtensionIdentifier(offering, identifier)); if (oe == null) { throw new NoSuchExtensionException(identifier); } session.update(oe.setActive(!disabled)); } } private class DeleteOfferingExtensionAction extends ThrowingVoidHibernateAction { private final String offering; private final String identifier; DeleteOfferingExtensionAction(final String offering, final String identifier) { this.offering = offering; this.identifier = identifier; } @Override protected void run(final Session session) throws NoSuchExtensionException { final OfferingExtensionImpl oe = (OfferingExtensionImpl) session .get(OfferingExtensionImpl.class, new OfferingExtensionIdentifier(offering, identifier)); if (oe == null) { throw new NoSuchExtensionException(identifier); } session.delete(oe); } } private class GetActiveCapabilitiesExtensionAction implements HibernateAction<Map<String, StringBasedCapabilitiesExtension>> { @Override public Map<String, StringBasedCapabilitiesExtension> call(final Session session) { @SuppressWarnings("unchecked") final List<CapabilitiesExtensionImpl> extensions = session .createCriteria(CapabilitiesExtensionImpl.class) .add(eq(CapabilitiesExtensionImpl.ACTIVE, true)) .list(); final HashMap<String, StringBasedCapabilitiesExtension> map = new HashMap<>(extensions .size()); for (final CapabilitiesExtensionImpl extension : extensions) { LOGGER.debug("Loaded CapabiltiesExtension: {}", extension); map.put(extension.getKey(), extension); } return map; } } private class GetAllCapabilitiesExtensionAction implements HibernateAction<Map<String, StringBasedCapabilitiesExtension>> { @Override public Map<String, StringBasedCapabilitiesExtension> call(final Session session) { @SuppressWarnings("unchecked") final List<CapabilitiesExtensionImpl> extensions = session .createCriteria(CapabilitiesExtensionImpl.class).list(); final HashMap<String, StringBasedCapabilitiesExtension> map = new HashMap<>(extensions .size()); for (final CapabilitiesExtensionImpl extension : extensions) { LOGGER.debug("Loaded CapabiltiesExtension: {}", extension); map.put(extension.getKey(), extension); } return map; } } private class GetCapabilitiesExtensionAction implements HibernateAction<CapabilitiesExtensionImpl> { private final String identifier; GetCapabilitiesExtensionAction(final String identifier) { this.identifier = identifier; } @Override public CapabilitiesExtensionImpl call(final Session session) { return (CapabilitiesExtensionImpl) session.get(CapabilitiesExtensionImpl.class, identifier); } } private class SaveCapabilitesExtensionAction extends VoidHibernateAction { private final String identifier; private final String value; SaveCapabilitesExtensionAction(final String identifier, final String value) { this.identifier = identifier; this.value = value; } @Override protected void run(final Session session) { session.saveOrUpdate(new CapabilitiesExtensionImpl(identifier, value)); } } private class DisableCapabiliesExtensionAction extends ThrowingVoidHibernateAction { private final String identifier; private final boolean disabled; DisableCapabiliesExtensionAction(final String identifier, final boolean disabled) { this.identifier = identifier; this.disabled = disabled; } @Override protected void run(final Session session) throws NoSuchExtensionException { final CapabilitiesExtensionImpl ce = (CapabilitiesExtensionImpl) session .get(CapabilitiesExtensionImpl.class, identifier); if (ce == null) { throw new NoSuchExtensionException(identifier); } session.update(ce.setActive(!disabled)); } } private class DeleteCapabilitiesExtensionAction extends ThrowingVoidHibernateAction { private final String identifier; DeleteCapabilitiesExtensionAction(final String identifier) { this.identifier = identifier; } @Override protected void run(final Session session) throws NoSuchExtensionException { final CapabilitiesExtensionImpl ce = (CapabilitiesExtensionImpl) session .get(CapabilitiesExtensionImpl.class, identifier); if (ce == null) { throw new NoSuchExtensionException(identifier); } session.delete(ce); } } private class GetStaticCapabilitiesAction implements HibernateAction<Map<String, StaticCapabilities>> { @Override public Map<String, StaticCapabilities> call(final Session session) { @SuppressWarnings("unchecked") final List<StaticCapabilitiesImpl> scs = session.createCriteria(StaticCapabilitiesImpl.class).list(); final HashMap<String, StaticCapabilities> map = new HashMap<>(scs.size()); for (final StaticCapabilitiesImpl sc : scs) { LOGGER.debug("Loaded StaticCapabilities: {}", sc); map.put(sc.getIdentifier(), sc); } return map; } } private class SaveStaticCapabilitiesAction extends VoidHibernateAction { private final String identifier; private final String document; SaveStaticCapabilitiesAction(final String identifier, final String document) { this.identifier = identifier; this.document = document; } @Override protected void run(final Session session) { session.saveOrUpdate(new StaticCapabilitiesImpl(identifier, document)); } } private class DeleteStaticCapabilitiesAction extends ThrowingVoidHibernateAction { private final String identifier; DeleteStaticCapabilitiesAction(final String identifier) { this.identifier = identifier; } @Override protected void run(final Session session) throws NoSuchExtensionException { final StaticCapabilitiesImpl sc = (StaticCapabilitiesImpl) session .get(StaticCapabilitiesImpl.class, identifier); if (sc == null) { throw new NoSuchExtensionException(identifier); } session.delete(sc); } } private class DeactivateStaticCapabilitiesAction extends VoidHibernateAction { @Override protected void run(final Session session) { final StaticCapabilitiesImpl cur = (StaticCapabilitiesImpl) session .createCriteria(StaticCapabilitiesImpl.class) .add(eq(StaticCapabilitiesImpl.ACTIVE, true)) .uniqueResult(); if (cur != null) { session.update(cur.setActive(false)); } } } private class GetStaticCapabilitiesWithIdAction implements HibernateAction<StaticCapabilities> { private final String id; GetStaticCapabilitiesWithIdAction(final String id) { this.id = id; } @Override public StaticCapabilities call(final Session session) { return (StaticCapabilitiesImpl) session.get(StaticCapabilitiesImpl.class, id); } } }