/* * Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 David Berkman * * This file is part of the SmallMind Code Project. * * The SmallMind Code Project is free software, you can redistribute * it and/or modify it under either, at your discretion... * * 1) The terms of 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. * * ...or... * * 2) The terms of the Apache License, Version 2.0. * * The SmallMind Code Project 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 or Apache License for more details. * * You should have received a copy of the GNU Affero General Public License * and the Apache License along with the SmallMind Code Project. If not, see * <http://www.gnu.org/licenses/> or <http://www.apache.org/licenses/LICENSE-2.0>. * * Additional permission under the GNU Affero GPL version 3 section 7 * ------------------------------------------------------------------ * If you modify this Program, or any covered work, by linking or * combining it with other code, such other code is not for that reason * alone subject to any of the requirements of the GNU Affero GPL * version 3. */ package org.smallmind.persistence.nosql.hector; import java.io.Serializable; import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import me.prettyprint.cassandra.serializers.CompositeSerializer; import me.prettyprint.cassandra.serializers.StringSerializer; import me.prettyprint.cassandra.service.template.ColumnFamilyResult; import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate; import me.prettyprint.cassandra.service.template.ColumnFamilyUpdater; import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate; import me.prettyprint.hector.api.Keyspace; import me.prettyprint.hector.api.beans.Composite; import org.smallmind.nutsnbolts.reflection.FieldUtility; import org.smallmind.persistence.Durable; import org.smallmind.persistence.NaturalKey; import org.smallmind.persistence.PersistenceException; import org.smallmind.persistence.cache.WideVectoredDao; import org.smallmind.persistence.nosql.NoSqlDao; public abstract class HectorDao<W extends Serializable & Comparable<W>, I extends Serializable & Comparable<I>, D extends HectorDurable<I, D>> extends NoSqlDao<W, I, D> { private ColumnFamilyTemplate<Composite, Composite> hectorTemplate; public HectorDao (String metricSource, Keyspace keyspace, WideVectoredDao<W, I, D> wideVectoredDao, boolean cacheEnabled) { super(metricSource, wideVectoredDao, cacheEnabled); hectorTemplate = new ThriftColumnFamilyTemplate<Composite, Composite>(keyspace, getManagedClass().getSimpleName(), CompositeSerializer.get(), CompositeSerializer.get()); hectorTemplate.setCount(Integer.MAX_VALUE); } public abstract I createId (); @Override public void remove (String context, Class<? extends Durable<W>> parentClass, W parentId, Class<D> durableClass) { WideVectoredDao<W, I, D> wideVectoredDao; hectorTemplate.deleteRow(new Composite(context, parentClass.getSimpleName(), HectorType.getTranslator(getParentIdClass(), "parentId").toHectorValue(parentId))); if (isCacheEnabled() && ((wideVectoredDao = getWideVectoredDao()) != null)) { wideVectoredDao.delete(context, parentClass, parentId, durableClass); } } @Override public List<D> get (String context, Class<? extends Durable<W>> parentClass, W parentId, Class<D> durableClass) { List<D> durables; WideVectoredDao<W, I, D> wideVectoredDao; ColumnFamilyResult<Composite, Composite> hectorResult; if (isCacheEnabled() && ((wideVectoredDao = getWideVectoredDao()) != null)) { if ((durables = wideVectoredDao.get(context, parentClass, parentId, durableClass)) != null) { return durables; } } if ((hectorResult = hectorTemplate.queryColumns(new Composite(context, parentClass.getSimpleName(), HectorType.getTranslator(getParentIdClass(), "parentId").toHectorValue(parentId)))).hasResults()) { HashMap<NaturalKey, D> naturalMap = new HashMap<NaturalKey, D>(); for (Composite columnName : hectorResult.getColumnNames()) { D durable; NaturalKey naturalKey; Field[] naturalKeyFields = NaturalKey.getNaturalKeyFields(durableClass); Field nonKeyField; Object[] naturalKeyValues = new Object[naturalKeyFields.length]; String nonKeyFieldName; int naturalKeyIndex; if ((nonKeyField = FieldUtility.getField(durableClass, nonKeyFieldName = columnName.get(naturalKeyFields.length, StringSerializer.get()))) == null) { throw new PersistenceException("Unknown field(%s) in return values", nonKeyFieldName); } naturalKeyIndex = 0; for (Field naturalKeyField : naturalKeyFields) { naturalKeyValues[naturalKeyIndex] = HectorType.getTranslator(naturalKeyField.getType(), naturalKeyField.getName()).toEntityValue(naturalKeyField.getType(), naturalKeyIndex++, columnName); } if ((durable = naturalMap.get(naturalKey = new NaturalKey<D>(durableClass, naturalKeyValues))) == null) { try { naturalMap.put(naturalKey, durable = durableClass.newInstance()); naturalKeyIndex = 0; for (Field naturalKeyField : naturalKeyFields) { naturalKeyField.set(durable, naturalKeyValues[naturalKeyIndex++]); } } catch (Exception exception) { throw new PersistenceException(exception); } } try { if (nonKeyFieldName.equals("id")) { nonKeyField.set(durable, HectorType.getTranslator(getIdClass(), "id").toEntityValue(getIdClass(), columnName, hectorResult)); } else { Class<?> nonKeyType = nonKeyField.getType(); nonKeyField.set(durable, HectorType.getTranslator(nonKeyType, nonKeyFieldName).toEntityValue(nonKeyType, columnName, hectorResult)); } } catch (IllegalAccessException illegalAccessException) { throw new PersistenceException(illegalAccessException); } } durables = new LinkedList<D>(naturalMap.values()); if (isCacheEnabled() && ((wideVectoredDao = getWideVectoredDao()) != null)) { wideVectoredDao.persist(context, parentClass, parentId, durableClass, durables); } return durables; } return Collections.emptyList(); } @Override public List<D> persist (String context, Class<? extends Durable<W>> parentClass, W parentId, Class<D> durableClass, List<D> durables) { if ((durables != null) && (!durables.isEmpty())) { ColumnFamilyUpdater<Composite, Composite> updater = hectorTemplate.createUpdater(new Composite(context, parentClass.getSimpleName(), HectorType.getTranslator(getParentIdClass(), "parentId").toHectorValue(parentId))); WideVectoredDao<W, I, D> wideVectoredDao; try { for (Field nonKeyField : NaturalKey.getNonKeyFields(durableClass)) { for (D durable : durables) { I id; if ((id = durable.getId()) == null) { durable.setId(id = createId()); } Composite nonKeyComposite = new Composite(); for (Field naturalKeyField : NaturalKey.getNaturalKeyFields(durableClass)) { nonKeyComposite.add(HectorType.getTranslator(naturalKeyField.getType(), naturalKeyField.getName()).toHectorValue(naturalKeyField.get(durable))); } nonKeyComposite.add(nonKeyField.getName()); if (nonKeyField.getName().equals("id")) { HectorTranslator idTranslator = HectorType.getTranslator(getIdClass(), "id"); updater.setValue(nonKeyComposite, idTranslator.toHectorValue(id), idTranslator.getSerializer()); } else { Object nonKeyValue = nonKeyField.get(durable); if (nonKeyValue == null) { updater.deleteColumn(nonKeyComposite); } else { HectorTranslator nonKeyTranslator = HectorType.getTranslator(nonKeyField.getType(), nonKeyField.getName()); updater.setValue(nonKeyComposite, nonKeyTranslator.toHectorValue(nonKeyValue), nonKeyTranslator.getSerializer()); } } } } } catch (IllegalAccessException illegalAccessException) { throw new PersistenceException(illegalAccessException); } if (isCacheEnabled() && ((wideVectoredDao = getWideVectoredDao()) != null)) { List<D> cachedInstance; if ((cachedInstance = wideVectoredDao.get(context, parentClass, parentId, durableClass)) != null) { for (D durable : durables) { int cachedIndex; if ((cachedIndex = cachedInstance.indexOf(durable)) >= 0) { cachedInstance.set(cachedIndex, durable); } else { cachedInstance.add(durable); } } wideVectoredDao.persist(context, parentClass, parentId, durableClass, cachedInstance); } } hectorTemplate.update(updater); } return durables; } @Override public void delete (String context, Class<? extends Durable<W>> parentClass, W parentId, Class<D> durableClass, List<D> durables) { if ((durables != null) && (!durables.isEmpty())) { ColumnFamilyUpdater<Composite, Composite> updater = hectorTemplate.createUpdater(new Composite(context, parentClass.getSimpleName(), HectorType.getTranslator(getParentIdClass(), "parentId").toHectorValue(parentId))); WideVectoredDao<W, I, D> wideVectoredDao; try { for (Field nonKeyField : NaturalKey.getNonKeyFields(durableClass)) { for (D durable : durables) { Composite nonKeyComposite = new Composite(); for (Field naturalKeyField : NaturalKey.getNaturalKeyFields(durableClass)) { nonKeyComposite.add(HectorType.getTranslator(naturalKeyField.getType(), naturalKeyField.getName()).toHectorValue(naturalKeyField.get(durable))); } nonKeyComposite.add(nonKeyField.getName()); updater.deleteColumn(nonKeyComposite); } } } catch (IllegalAccessException illegalAccessException) { throw new PersistenceException(illegalAccessException); } if (isCacheEnabled() && ((wideVectoredDao = getWideVectoredDao()) != null)) { List<D> cachedInstance; if ((cachedInstance = wideVectoredDao.get(context, parentClass, parentId, durableClass)) != null) { for (D durable : durables) { cachedInstance.remove(durable); } wideVectoredDao.persist(context, parentClass, parentId, durableClass, cachedInstance); } } hectorTemplate.update(updater); } } }