/* * Copyright 2012 The Solmix Project * * 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 may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.gnu.org/licenses/ * or see the FSF site: http://www.fsf.org. */ package org.solmix.api.datasource; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.solmix.api.VelocityExpression; import org.solmix.api.criterion.IEvaluator; import org.solmix.api.criterion.Operator; import org.solmix.api.event.IValidationEvent; import org.solmix.api.jaxb.Eoperation; import org.solmix.api.jaxb.EserverType; import org.solmix.api.jaxb.TdataSource; import org.solmix.api.jaxb.Tfield; import org.solmix.api.jaxb.ToperationBinding; import org.solmix.commons.logs.SlxLog; import org.solmix.commons.util.DataUtils; /** * This class used as a datasource context bean,cache ds static configuration , dynamical configuration and runtime * variable. * <p> * <h1>Merge Specification</h1> {@link org.solmix.api.datasource.DataSourceData DataSourceData} and * {@link org.solmix.api.jaxb.TdataSource TdataSource} Merge Specification * <table border=1 > * <tr> * <td COLSPAN="2" width="100%" align="center" style="background-color : #BBFFFF;font-size:12pt">The Type of DataSource * configuration</td> * </tr> * <tr width="100%" > * <td>From top to bottom</td> * <td>From bottom to top</td> * </tr> * <tr> * <td> * <li> * <li></td> * <td> * <li> * <li></td> * </tr> * </table> * For <code>From top to bottom<code>:use {@link org.solmix.api.datasource.DataSourceData DataSourceData} * <p> * For <code>From bottom to top<code>:use {@link org.solmix.api.jaxb.TdataSource TdataSource} * * @author solmix.f@gmail.com * @since 0.0.1 * @version 110035 2010-12-19 solmix-api */ @SuppressWarnings("unchecked") public class DataSourceData implements Serializable { private static final Logger log = LoggerFactory.getLogger(DataSourceData.class.getName()); private Map<String, Tfield> mapFields; private Map<Object, Object> derivedClientToServerFieldMap = new LinkedHashMap<Object, Object>(); private Map<Object, Object> ds2NativeFieldMap; private Map<Object, Object> native2DSFieldMap; private String superDSName; private boolean waitForFree; private Map<Object, Object> otherAttributes; /** * */ private static final long serialVersionUID = 3894686313931868579L; /** * <B><li>ANNOTATE:</B> This is DS static configuration form xml config file. */ private TdataSource tdataSource; private Map<String, ?> customerConfig; private DataSource superDS; // private List<Tfield> fields; private List<String> primaryKeys; private Object autoDeriveSchema; private boolean autoJoinTransaction; /** * <B><li>ANNOTATE:</B> This is DS runtime variable */ private long configTimestamp = -1; private final long configLastModified=-1; /** * <B><li>ANNOTATE:</B> This is DS dynamical config. */ private String urlString; /** * <B><li>ANNOTATE:</B> This is DS runtime variable */ private IEvaluator evaluator; private VelocityExpression requires; private List<String> requiresRoles; private final Map<OpBindHolder, ToperationBinding> _cachedBindings = new LinkedHashMap<OpBindHolder, ToperationBinding>(); private final Map<String, IType> fieldTypeCache = new Hashtable<String, IType>(); /** * Return this datasource needed resource.if the {@link #requires} is null,used {@link #tdataSource}to initial it. * * @return the requires */ public VelocityExpression getRequires() { return requires; } /** * Flag for control pooled datasource to return only once. * * @return */ public boolean isWaitForFree() { return waitForFree; } /** * @param waitForFree the waitForFree to set */ public void setWaitForFree(boolean waitForFree) { this.waitForFree = waitForFree; } /** * @param requires the requires to set */ public void setRequires(VelocityExpression requires) { this.requires = requires; } /** * Return this datasource needed roles.if the {@link #requiresRoles} is null,used {@link #tdataSource}to initial it. * * @return the requiresRoles */ public List<String> getRequiresRoles() { return requiresRoles; } /** * @param requiresRoles the requiresRoles to set */ public void setRequiresRoles(List<String> requiresRoles) { this.requiresRoles = requiresRoles; } /** * Get auto generated schema object. used for cache auto derived schema. * * @return the autoDeriveSchema */ public Object getAutoDeriveSchema() { return autoDeriveSchema; } /** * Cache schema object which is auto generated by datasource. * * @param autoDeriveSchema the autoDeriveSchema to set */ public void setAutoDeriveSchema(Object autoDeriveSchema) { this.autoDeriveSchema = autoDeriveSchema; } /** * @return the mapFields */ public Map<String, Tfield> getMapFields() { if (mapFields == null) mapFields = new LinkedHashMap<String, Tfield>(); return mapFields; } /** * @param mapFields the mapFields to set */ public void setMapFields(Map<String, Tfield> mapFields) { this.mapFields = mapFields; } public void addField(Tfield field) { this.mapFields.put(field.getName(), field); } public IType getCachedFiledType(String fieldName) { return fieldTypeCache.get(fieldName); } public void addCachedFieldType(String fieldName, IType type) { fieldTypeCache.put(fieldName, type); } public void addToPrimaryKeys(String key) { if (primaryKeys == null) { primaryKeys = new ArrayList<String>(5); } if (!primaryKeys.contains(key)) { primaryKeys.add(key); } } /** * @return the primaryKeys */ public List<String> getPrimaryKeys() { return primaryKeys; } /** * * @return unique primary key. */ public String getPrimaryKey() { List<String> keys = getPrimaryKeys(); if (keys == null || keys.get(0) == null) return null; else return keys.get(0); } /** * @return the fields */ public List<Tfield> getFields() { if (getMapFields() == null) LoggerFactory.getLogger(SlxLog.GLOBAL).warn("try to CALL getFields() Method ,before datasource initialized"); List<Tfield> mapFields = new ArrayList<Tfield>(); for (Tfield f : getMapFields().values()) mapFields.add(f); return mapFields; } /** * @return the autoJoinTransaction */ public boolean shouldAutoJoinTransaction() { return autoJoinTransaction; } /** * @param autoJoinTransaction the autoJoinTransaction to set */ public void setAutoJoinTransaction(boolean autoJoinTransaction) { this.autoJoinTransaction = autoJoinTransaction; } /** * The source of superDSName is form {@link org.solmix.api.jaxb.TdataSource}. * * @return the superDSName */ public String getSuperDSName() { return superDSName; } public Map<Object, Object> getDerivedClientToServerFieldMap() { return derivedClientToServerFieldMap; } public void setDerivedClientToServerFieldMap(Map<Object, Object> derivedClientToServerFieldMap) { this.derivedClientToServerFieldMap = derivedClientToServerFieldMap; } public Map<Object, Object> getDs2NativeFieldMap() { return ds2NativeFieldMap; } /** * @return the native2DSFieldMap */ public Map<Object, Object> getNative2DSFieldMap() { return native2DSFieldMap; } /** * @param native2dsFieldMap the native2DSFieldMap to set */ public void setNative2DSFieldMap(Map<Object, Object> native2dsFieldMap) { native2DSFieldMap = native2dsFieldMap; } public void setDs2NativeFieldMap(Map<Object, Object> ds2NativeFieldMap) { this.ds2NativeFieldMap = ds2NativeFieldMap; } public void setDs2NativeFieldMap(Object key, Object value) { if (ds2NativeFieldMap == null) ds2NativeFieldMap = new LinkedHashMap<Object, Object>(); ds2NativeFieldMap.put(key, value); } /** * The source of superDSName is form {@link org.solmix.api.jaxb.TdataSource},so the <code>set</code> method should * update the source value. * * @param superDSName the superDSName to set */ public void setSuperDSName(String superDSName) { this.superDSName = superDSName; } /** * @return the superDS */ public DataSource getSuperDS() { return superDS; } /** * @param superDS the superDS to set */ public void setSuperDS(DataSource superDS) { this.superDS = superDS; } /** * <B><li>ANNOTATE:</B> This is DS dynamical runtime variable */ private List<IValidationEvent> validationList; private String repositoryId; /** * @return the customerConfig */ public Map<String, ?> getCustomerConfig() { return customerConfig; } /** * <B><li>ANNOTATE:</B> This is DS dynamical runtime variable * * @param event */ public void addValidationEvent(IValidationEvent event) { if (validationList == null) validationList = new ArrayList<IValidationEvent>(); validationList.add(event); } public List<IValidationEvent> getValidationEvents() { return validationList; } /** * @param customerConfig the customerConfig to set */ public void setCustomerConfig(Map<String, ?> customerConfig) { this.customerConfig = customerConfig; } @SuppressWarnings("unused") private DataSourceData() { } public DataSourceData(TdataSource datasource) { if(datasource==null) throw new java.lang.IllegalStateException("The TdataSource must be not null to initial DataSourceData!"); this.setTdataSource(datasource); } /** * <B><li>ANNOTATE:</B> This is DS runtime variable * * @param op */ public void addSearchOperator(Operator op) { getEvaluator().addSearchOperator(op); } /** * Indicate the operation type. * <p> * <B> * <li>ANNOTATE:</B> here for convenience. * * @param operation * @return */ public boolean isModificationAction(Eoperation operation) { if (operation == Eoperation.ADD || operation == Eoperation.REMOVE || operation == Eoperation.UPDATE || operation == Eoperation.REPLACE) return true; else return false; } /** * <p> * <B> * <li>ANNOTATE:</B> here for convenience. * * @param opType * @param opId * @return */ public boolean isModificationAction(Eoperation opType, String opId) { if (isModificationAction(opType)) return true; else return false; } public String getAutoOperationId(Eoperation operationType) { return getName() + "_" + operationType.value(); } /** * Return the datasource context's name.Any reference to DataSource identification must from this method.not form * {@link org.solmix.api.jaxb.TdataSource#getID()} * <p> * <B> * <li>ANNOTATE:</B> * <p> * This accessor method returns a reference to the <B>group:</B>{@link org.solmix.api.jaxb.TdataSource#getID() ID}, * not a snapshot. Therefore any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a <CODE>set</CODE> method for the serviceOrReferenceListOrBean property. * <p> * For example, to add a new item, do as follows: * * <pre> * getTdataSource().setID(newItem); * </pre> * * @return */ public String getName() { return tdataSource.getID(); } public void setName(String name) { tdataSource.setID(name); } public ToperationBinding getOperationBinding(Eoperation opType) { return getOperationBinding(opType, null); } protected List<ToperationBinding> getOperationBindings(Eoperation opType) { List<ToperationBinding> _return = new ArrayList<ToperationBinding>(); if (tdataSource.getOperationBindings() != null) { for (ToperationBinding action : tdataSource.getOperationBindings().getOperationBinding()) { if (action.getOperationType() == opType) { _return.add(action); } } } return _return; } public ToperationBinding getOperationBinding(DSRequest request) { return getOperationBinding(request.getContext().getOperationType(), request.getContext().getOperationId()); } /** * <B><li>ANNOTATE:</B> * <p> * This accessor method returns a reference to the {@link org.solmix.api.jaxb.TdataSource}, not a snapshot. * Therefore any modification you make to the returned list will be present inside the JAXB object. This is why * there is not a <CODE>set</CODE> method for the serviceOrReferenceListOrBean property. * <p> * For example, to add a new item, do as follows: * * <pre> * getTdataSource().getActions().setAction(newItem); * </pre> * * @param opType * @param opId * @return */ public ToperationBinding getOperationBinding(Eoperation opType, String opId) { if (tdataSource == null) return null; OpBindHolder holder = new OpBindHolder(opType, opId); ToperationBinding finded = this._cachedBindings.get(holder); if (finded != null) { return finded; } ToperationBinding autoOperationBinding = null; // is auto generate operation id. boolean operationIdIsAuto = opId != null && opType != null && opId.equals(getAutoOperationId(opType)); if (tdataSource.getOperationBindings() != null) { for (ToperationBinding action : tdataSource.getOperationBindings().getOperationBinding()) { if (action.getOperationId() != null && action.getOperationId().equals(opId) && action.getOperationType() == opType){ autoOperationBinding= action; break; } if (action.getOperationType() == opType && action.getOperationId() == null && (operationIdIsAuto || opId == null)) autoOperationBinding = action; } // if not found operation bindings ,used default type of bindings. if (autoOperationBinding == null && (operationIdIsAuto || opId == null)) { List<ToperationBinding> binds = getOperationBindings(opType); if (binds == null || binds.isEmpty()) { return null; } else if (binds.size() == 1) { autoOperationBinding = binds.get(0); if (log.isDebugEnabled()) log.debug(new StringBuilder().append("Checkout operation TYPE:[") .append(opType).append("] ID:[").append(opId).append("] but not found ,Used auto discoveried bind -TYPE:[").append(opType) .append("] ID:[").append(autoOperationBinding.getOperationId()).append("]!").toString()); } else { throw new java.lang.IllegalStateException(new StringBuilder().append("Get opation type:").append(opType.value()).append( "find multi opation :").append(binds.size()).append(" Please check the datasource configuation .").toString()); } } } _cachedBindings.put(holder, autoOperationBinding); return autoOperationBinding; } /** * <B><li>ANNOTATE:</B> * <p> * This accessor method returns a reference to the {@link org.solmix.api.jaxb.TdataSource}, not a snapshot. * Therefore any modification you make to the returned list will be present inside the JAXB object. This is why * there is not a <CODE>set</CODE> method for the serviceOrReferenceListOrBean property. * <p> * For example, to add a new item, do as follows: * * <pre> * getTdataSource().getFields().setField(newItem); * </pre> * * @param fieldName * @return */ public Tfield getField(String fieldName) { if (getMapFields() == null || fieldName == null) return null; return getMapFields().get(fieldName); } /** * @return the tdataSource */ public TdataSource getTdataSource() { return tdataSource; } /** * find out the server implements type of datasource. * <p> * <B> * <li>ANNOTATE:</B> * <p> * This accessor method returns a reference to the {@link org.solmix.api.jaxb.TdataSource}, not a snapshot. * Therefore any modification you make to the returned list will be present inside the JAXB object. This is why * there is not a <CODE>set</CODE> method for the serviceOrReferenceListOrBean property. * <p> * For example, to add a new item, do as follows: * * <pre> * getTdataSource().setServerType(newItem); * </pre> * * @return */ public EserverType getServerType() { return tdataSource.getServerType(); } /** * @param tdataSource the tdataSource to set */ public void setTdataSource(TdataSource tdataSource) { this.tdataSource = tdataSource; } /** * <B><li>ANNOTATE:</B> This is DS runtime variable * * @return the configTimestamp */ public long getConfigTimestamp() { return configTimestamp; } /** * <B><li>ANNOTATE:</B> This is DS runtime variable * * @param configTimestamp the configTimestamp to set */ public void setConfigTimestamp(long configTimestamp) { this.configTimestamp = configTimestamp; } /** * <B><li>ANNOTATE:</B> This is DS runtime variable<p> * Used to dynamical load modified configure datasource file. * @return the configLastModified */ public long getConfigLastModified() { return configLastModified; } /** * <B><li>ANNOTATE:</B> This is DS runtime variable * @return the dsConfigFile */ public String getUrlString() { return urlString; } /** * @param dsConfigFile the dsConfigFile to set */ public void setUrlString(String urlString) { this.urlString = urlString; } /** * @return the evaluator */ public IEvaluator getEvaluator() { return evaluator; } /** * @param evaluator the evaluator to set */ public void setEvaluator(IEvaluator evaluator) { this.evaluator = evaluator; } /** * @return */ public List<String> getFieldNames() { List<String> res = new ArrayList<String>(); for (Tfield field : getFields()) res.add(field.getName()); return res; } /** * @return */ public boolean isValidateRecords() { if (tdataSource != null) return DataUtils.booleanValue(tdataSource.isValidateRecords()); else return false; } @SuppressWarnings("rawtypes") public Map<String, Object> getExpandedDs2NativeFieldMap() { Map fieldMap = new LinkedHashMap(); if (this.getSuperDS() != null) { fieldMap = getSuperDS().getContext().getExpandedDs2NativeFieldMap(); } DataUtils.mapMerge(ds2NativeFieldMap, fieldMap); return fieldMap; } public Map<String, Object> getValueMaps() { return getValueMaps(getFieldNames()); } public Map<String, Object> getValueMaps(List<String> fieldNames) { Map<String, Object> valueMaps = new HashMap<String, Object>(); if (fieldNames == null || fieldNames.size() == 0) return valueMaps; for (Object o : fieldNames) { String fieldName = (String) o; Tfield field = this.getField(fieldName); if (field != null && field.getValueMap() != null) valueMaps.put(fieldName, field.getValueMap().getValue()); } return valueMaps; } /** * @param columnName * @return */ public Object getColumnName(String fieldName) { return ds2NativeFieldMap.get(fieldName); } public static String getCustomSQL(ToperationBinding bind) { String _return = null; if (bind != null && bind.getQueryClauses() != null) { _return = bind.getQueryClauses().getCustomSQL(); } return _return == null ? null : _return.trim(); } public static String getValuesClause(ToperationBinding bind) { String _return = null; if (bind != null && bind.getQueryClauses() != null) { _return = bind.getQueryClauses().getValuesClause(); } return _return == null ? null : _return.trim(); } public static String getSelectClause(ToperationBinding bind) { String _return = null; if (bind != null && bind.getQueryClauses() != null) { _return = bind.getQueryClauses().getSelectClause(); } return _return == null ? null : _return.trim(); } public static String getTableClause(ToperationBinding bind) { String _return = null; if (bind != null && bind.getQueryClauses() != null) { _return = bind.getQueryClauses().getTableClause(); } return _return == null ? null : _return.trim(); } public static String getWhereClause(ToperationBinding bind) { String _return = null; if (bind != null && bind.getQueryClauses() != null) { _return = bind.getQueryClauses().getWhereClause(); } return _return == null ? null : _return.trim(); } /** * @param key * @return */ public Object getAttribute(String key) { if (otherAttributes != null) return otherAttributes.get(key); return null; } public void setAttribute(String key, Object value) { if (otherAttributes == null) otherAttributes = new HashMap<Object, Object>(); otherAttributes.put(key, value); } public void removeAttribute(String key) { if (otherAttributes != null) otherAttributes.remove(key); } class OpBindHolder{ final Eoperation type; final String id; OpBindHolder(Eoperation opType, String opId){ type=opType; id=opId; } @Override public boolean equals(Object obj) { if (!(obj instanceof OpBindHolder)) { return false; } OpBindHolder other = (OpBindHolder)obj; return type.equals(other.type) && ((id==other.id||id!=null&&id.equals(other.id))); } @Override public int hashCode(){ int hash = type.hashCode(); return (hash ^ (hash >>> 32)*31)+(this.id!=null?this.id.hashCode():0); } } /** * @param name */ public void setRepositoryId(String name) { this.repositoryId=name; } /** * @return the repositoryId */ public String getRepositoryId() { return repositoryId; } }