/*
* Copyright 2010 FatWire Corporation. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fatwire.gst.foundation.properties;
import COM.FutureTense.Interfaces.ICS;
import COM.FutureTense.Interfaces.ISyncHash;
import COM.FutureTense.Util.ftErrors;
import com.fatwire.assetapi.common.AssetAccessException;
import com.fatwire.assetapi.common.SiteAccessException;
import com.fatwire.assetapi.data.*;
import com.fatwire.assetapi.query.OpTypeEnum;
import com.fatwire.assetapi.query.Query;
import com.fatwire.assetapi.site.SiteInfo;
import com.fatwire.assetapi.site.SiteManager;
import com.fatwire.gst.foundation.CSRuntimeException;
import com.fatwire.gst.foundation.facade.assetapi.AttributeDataUtils;
import com.fatwire.gst.foundation.facade.assetapi.QueryBuilder;
import com.fatwire.gst.foundation.facade.runtag.render.LogDep;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
*
* Class representing properties stored as an asset. Can be basic or flex. Property name field must be a core
* asset field ("name" is usually chosen). Other fields can be set as needed.
*
* Adding a new property adds it to the current site, as defined by the pubid session variable.
* @author Tony Field
* @since 2011-09-02
* @deprecated - moved to new namespace
* @see "tools.gsf.properties.AssetApiPropertyDao"
*/
public final class AssetApiPropertyDao implements PropertyDao {
private static final Logger LOG = LoggerFactory.getLogger("tools.gsf.legacy.properties.AssetApiPropertyDao");
private static final int TIMEOUT_MINUTES = 60 * 24; // one day
private static final int MAX_SIZE = 1000000; // a million
private static final String ID = "id";
private static final String PUBLIST = "Publist";
private final ISyncHash _props;
private final AssetDataManager assetDataManager;
private final SiteManager siteManager;
private final String assetType;
private final String assetFlexDefinition;
private final String propertyNameAttr;
private final String propertyDescriptionAttr;
private final String propertyValueAttr;
private final ICS _ics;
/**
* Property dao backed by a basic or flex asset.
* @param adm asset data manager
* @param siteManager site manager
* @param type asset type to be used
* @param flexDefName flex definition, if using a flex asset; null if using a basic asset
* @param propNameAttr attribute name holding property name. Must be a core asset field (i.e. in the main row of the asset) so that lookups can be done properly. Must be a string attribute type.
* @param propDescAttr attribute name holding the description of the property Must be a string attribute type.
* @param propValueAttr attribute name holding the property value. Must be a string attribute type.
* @param ics ics context
*/
public AssetApiPropertyDao(AssetDataManager adm, SiteManager siteManager, String type, String flexDefName, String propNameAttr, String propDescAttr, String propValueAttr, ICS ics) {
this.assetDataManager = adm;
this.siteManager = siteManager;
this.assetType = type;
this.assetFlexDefinition = flexDefName;
this.propertyNameAttr = propNameAttr;
this.propertyDescriptionAttr = propDescAttr;
this.propertyValueAttr = propValueAttr;
this._props = ics.GetSynchronizedHash(AssetApiPropertyDao.class.getName(), true, TIMEOUT_MINUTES, MAX_SIZE, true, true, Collections.singletonList(ics.GetProperty("cs.dsn") + assetType));
this._ics = ics;
}
public synchronized Property getProperty(String name) {
PropertyHolder ph = (PropertyHolder) _props.get(name);
if (ph == null) {
ph = _readProperty(name);
_props.put(name, ph);
}
if (ph.getId() != null) {
LogDep.logDep(_ics, ph.getId()); // can't rely on asset API to do this for us since we're caching
}
return ph.getProp();
}
@SuppressWarnings("unchecked")
public synchronized Collection<String> getPropertyNames() {
ArrayList<String> keys = new ArrayList<>();
for (Object key : _props.keySet()) {
String sKey = (String)key;
PropertyHolder ph = (PropertyHolder)_props.get(sKey);
if (ph != PropertyHolder.EMPTY_HOLDER) {
keys.add(sKey);
}
}
return keys;
}
private PropertyHolder _readProperty(String name) {
Query loadQuery = new QueryBuilder(assetType, assetFlexDefinition).attributes(ID, propertyNameAttr, propertyDescriptionAttr, propertyValueAttr)
.condition("status", OpTypeEnum.NOT_EQUALS, "VO")
.condition(propertyNameAttr, OpTypeEnum.EQUALS, name)
.setBasicSearch(true) // note must be a core field for lookup to work
.setFixedList(true) // we are being precise
.toQuery();
try {
for (AssetData propData : assetDataManager.read(loadQuery)) {
AttributeData oName = propData.getAttributeData(propertyNameAttr);
AttributeData oDesc = propData.getAttributeData(propertyDescriptionAttr);
AttributeData oVal = propData.getAttributeData(propertyValueAttr);
AssetId id = propData.getAssetId();
if (oName == null) throw new IllegalStateException("Property name attribute configured in PropertyDao not found in asset: "+propertyNameAttr);
if (oDesc == null) throw new IllegalStateException("Property description attribute configured in PropertyDao not found in asset" +propertyDescriptionAttr);
if (oVal == null) throw new IllegalStateException("Property value attribute configured in PropertyDao not found in asset" +propertyValueAttr);
LOG.debug("Loaded property {}",name);
return new PropertyHolder(name, AttributeDataUtils.asString(oDesc), AttributeDataUtils.asString(oVal), id);
}
} catch (AssetAccessException e) {
LOG.error("Failure reading property: {}", name, e);
return AssetApiPropertyDao.PropertyHolder.EMPTY_HOLDER;
}
LOG.debug("Property not found: {}", name);
return AssetApiPropertyDao.PropertyHolder.EMPTY_HOLDER;
}
/**
* Convenience method to set (or re-set) a property value
*
* @param name property name
* @param description property description (optional)
* @param value value as a string
*/
public synchronized void setProperty(String name, String description, String value) {
if (name == null) throw new IllegalArgumentException("Cannot set a null property name");
String pubid = _ics.GetSSVar("pubid");
String site = _lookupSiteName(pubid);
PropertyHolder holder = _readProperty(name);
if (holder == PropertyHolder.EMPTY_HOLDER) {
// add
try {
MutableAssetData data = assetDataManager.newAssetData(assetType, assetFlexDefinition);
data.getAttributeData(propertyNameAttr).setData(name);
data.getAttributeData(propertyDescriptionAttr).setData(description);
data.getAttributeData(propertyValueAttr).setData(value);
_appendToPublist(data.getAttributeData(PUBLIST), site);
assetDataManager.insert(Collections.singletonList(data));
holder = new PropertyHolder(name, description, value, data.getAssetId());
} catch (AssetAccessException e) {
throw new CSRuntimeException("Could not add new property "+name, ftErrors.exceptionerr, e);
}
} else {
// replace
try {
ArrayList<AssetData> sAssets = new ArrayList<>();
for (MutableAssetData data : assetDataManager.readForUpdate(Collections.singletonList(holder.getId()))) {
// sorry can't reset 'name'
data.getAttributeData(propertyDescriptionAttr).setData(description);
data.getAttributeData(propertyValueAttr).setData(value);
_appendToPublist(data.getAttributeData(PUBLIST), site);
sAssets.add(data);
holder = new PropertyHolder(name, description, value, holder.getId());
}
assetDataManager.update(sAssets); // there can only be one since we are loading by id
} catch (AssetAccessException e) {
throw new CSRuntimeException("Could not update property "+name, ftErrors.exceptionerr, e);
}
}
// cache or replace in cache
_props.put(name, holder);
}
/**
* Set (or re-set) a property value
*
* @param property property object with name and value
*/
public synchronized void setProperty(Property property) {
if (property == null) throw new IllegalArgumentException("Can't set a null property object");
setProperty(property.getName(), property.getDescription(), property.asString());
}
@SuppressWarnings("unchecked")
private void _appendToPublist(AttributeData data, String ... siteName) {
if (siteName != null) {
HashSet<String> pubs = new HashSet<>();
pubs.addAll(Arrays.asList(siteName));
pubs.addAll(data.getDataAsList());
data.setDataAsList(new ArrayList<>(pubs));
}
}
private String _lookupSiteName(String pubid) {
if (pubid != null) {
long id = Long.valueOf(pubid);
try {
for (SiteInfo si : siteManager.list()) {
if (si.getId() == id) return si.getName();
}
} catch (SiteAccessException e) {
throw new CSRuntimeException("Could not determine name of current site: " + e, ftErrors.exceptionerr, e);
}
}
return null;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public synchronized void addToSite(String name, String ... site) {
if (name == null) throw new IllegalArgumentException("Invalid property name null");
PropertyHolder holder = _readProperty(name);
if (holder == PropertyHolder.EMPTY_HOLDER) throw new IllegalArgumentException("Could not locate property "+name);
try {
ArrayList<AssetData> sAssets = new ArrayList<>();
for (MutableAssetData data : assetDataManager.readForUpdate(Collections.singletonList(holder.getId()))) {
AttributeData publistData = data.getAttributeData(PUBLIST);
_appendToPublist(publistData, site);
sAssets.add(data);
}
assetDataManager.update(sAssets);
} catch (AssetAccessException e) {
throw new CSRuntimeException("Failure adding property "+name+" to sites "+Arrays.asList(site), ftErrors.exceptionerr, e);
}
}
private static class PropertyHolder {
private static final PropertyHolder EMPTY_HOLDER = new PropertyHolder();
private final Property prop;
private final AssetId id;
/**
* Default constructor for empty object.
*/
private PropertyHolder() {
prop = null;
id = null;
}
/**
* Deep copy constructor, to be used when adding, to prevent messy cache issues
* @param name property name
* @param description property description
* @param value property value
* @param id property id
*/
private PropertyHolder(String name, String description, String value, AssetId id) {
if (name == null) throw new IllegalArgumentException("Null name not allowed");
if (value == null) throw new IllegalArgumentException("Null value not allowed");
this.id = id;
this.prop = new PropertyImpl(id.getType(), name, description, value);
}
private Property getProp() {
return prop;
}
private AssetId getId() {
return id;
}
}
}