Java tutorial
/* * Copyright 2003 - 2016 The eFaps Team * * 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 org.efaps.admin.datamodel; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.UUID; import org.apache.commons.lang3.builder.ToStringBuilder; import org.efaps.admin.event.EventDefinition; import org.efaps.admin.event.EventType; import org.efaps.admin.event.Parameter.ParameterValues; import org.efaps.admin.event.Return; import org.efaps.admin.event.Return.ReturnValues; import org.efaps.ci.CIAdminDataModel; import org.efaps.ci.CIAdminUser; import org.efaps.db.Context; import org.efaps.db.databases.information.ColumnInformation; import org.efaps.db.transaction.ConnectionResource; import org.efaps.db.wrapper.SQLInsert; import org.efaps.db.wrapper.SQLPart; import org.efaps.db.wrapper.SQLSelect; import org.efaps.db.wrapper.SQLUpdate; import org.efaps.util.EFapsException; import org.efaps.util.cache.CacheLogListener; import org.efaps.util.cache.CacheReloadException; import org.efaps.util.cache.InfinispanCache; import org.infinispan.Cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * This is the class for the attribute description. The type description holds * information about creation of a new instance of a attribute with default * values. * * @author The eFaps Team * */ public class Attribute extends AbstractDataModelObject { /** * ENUM used to access the different attribute types. */ public enum AttributeTypeDef { /** Attribute type Link. */ ATTRTYPE_LINK("440f472f-7be2-41d3-baec-4a2f0e4e5b31"), /** Attribute type Link with Ranges. */ ATTRTYPE_LINK_WITH_RANGES("9d6b2e3e-68ce-4509-a5f0-eae42323a696"), /** Attribute type PersonLink. */ ATTRTYPE_GROUP_LINK("a48538dd-5d9b-468f-a84f-bf42791eed66"), /** Attribute type PersonLink. */ ATTRTYPE_PERSON_LINK("7b8f98de-1967-44e0-b174-027349868a61"), /** Attribute type Creator Link. */ ATTRTYPE_CREATOR_LINK("76122fe9-8fde-4dd4-a229-e48af0fb4083"), /** Attribute type Modifier Link. */ ATTRTYPE_MODIFIER_LINK("447a7c87-8395-48c4-b2ed-d4e96d46332c"), /** Attribute type Multi Line Array. */ ATTRTYPE_MULTILINEARRAY("adb13c3d-9506-4da2-8d75-b54c76779c6c"), /** Attribute type Status. */ ATTRTYPE_STATUS("0161bcdb-45e9-4839-a709-3a1c56f8a76a"), /** Attribute type Enum. */ ATTRTYPE_ENUM("b7c6a324-5dec-425f-b778-fa8fabf80202"), /** Attribute type BitEnum. */ ATTRTYPE_BITENUM("a9b1abde-d58d-4aea-8cdc-f2870111f1cd"), /** Attribute type BitEnum. */ ATTRTYPE_JAXB("58817bd8-db76-4b40-8acd-18112fe96170"); /** * Stored the UUID for the given type. */ private final UUID uuid; /** * Private Constructor. * * @param _uuid UUID to set */ AttributeTypeDef(final String _uuid) { this.uuid = UUID.fromString(_uuid); } /** * @return the uuid */ public UUID getUuid() { return this.uuid; } } /** * Needed for serialization. */ private static final long serialVersionUID = 1L; /** * Logging instance used in this class. */ private static final Logger LOG = LoggerFactory.getLogger(Attribute.class); /** * SQL Statement to get attributes for at type. */ private static final String SQL_TYPE = new SQLSelect().column("ID").column("NAME").column("TYPEID") .column("DMTABLE").column("DMATTRIBUTETYPE").column("DMTYPELINK").column("PARENTSET") .column("SQLCOLUMN").column("DEFAULTVAL").column("DIMENSION").column("CLASSNAME") .from("V_ADMINATTRIBUTE", 0).addPart(SQLPart.WHERE).addColumnPart(0, "DMTYPE").addPart(SQLPart.EQUAL) .addValuePart("?").toString(); /** * SQL Statement to get an attribute. */ private static final String SQL_ATTR = new SQLSelect().column("DMTYPE").from("V_ADMINATTRIBUTE", 0) .addPart(SQLPart.WHERE).addColumnPart(0, "ID").addPart(SQLPart.EQUAL).addValuePart("?").toString(); /** * Name of the Cache by Name. */ private static String NAMECACHE = Attribute.class.getName() + ".Name"; /** * Name of the Cache by ID. */ private static String IDCACHE = Attribute.class.getName() + ".ID"; /** * This is the instance variable for the table, where attribute is stored. * * @see #getTable */ private final SQLTable sqlTable; /** * Instance variable for the link to another type id.. * * @see #getLink * @see #setLink */ private Long link = null; /** * Instance variable for the parent type id. * * @see #getParent * @see #setParent */ private final Long parent; /** * This instance variable stores the sql column name. * * @see #getSqlColName * @see #setSqlColName */ private final ArrayList<String> sqlColNames = new ArrayList<String>(); /** * The instance variable stores the attribute type for this attribute. * * @see #getAttributeType */ private final AttributeType attributeType; /** * The String holds the default value as string for this Attribute. * * @see #getDefaultValue */ private final String defaultValue; /** * Is the attribute required? This means at minimum one part of the * attribute is not allowed to be a null value. * * @see #isRequired */ private final boolean required; /** * The parent this attribute belongs to. */ private AttributeSet parentSet; /** * Size of the attribute (for string). Precision of the attribute (for * decimal). */ private final int size; /** * Scale of the attribute (for decimal). */ private final int scale; /** * UUID of the dimension belonging to this attribute. */ private final String dimensionUUID; /** * Holds the Attributes this Attribute depend on. A TreeMap is used to have * a fixed position of each attribute. (Needed e.g for printquery) */ private Map<String, Attribute> dependencies; /** * Key of this Attribute. Consist of name of the Parent Type and name of the Attribute itself */ private String key; /** * ClassName of this Attribute. Used only in case of * {@link org.efaps.admin.datamodel.attributetype.EnumType} * and * {@link org.efaps.admin.datamodel.attributetype.BitEnumType}. */ private String className; /** * This is the constructor for class {@link Attribute}. Every instance of * class {@link Attribute} must have a name (parameter <i>_name</i>) and an * identifier (parameter <i>_id</i>). * * @param _id id of the attribute * @param _parentId id of the parent type * @param _name name of the instance * @param _sqlColNames name of the SQL columns * @param _sqlTable table of this attribute * @param _attributeType type of this attribute * @param _defaultValue default value for this attribute * @param _dimensionUUID UUID of the Dimension * @throws EFapsException on error while retrieving column information from * database */ //CHECKSTYLE:OFF protected Attribute(final long _id, final long _parentId, final String _name, final String _sqlColNames, final SQLTable _sqlTable, final AttributeType _attributeType, final String _defaultValue, final String _dimensionUUID) //CHECKSTYLE:ON throws EFapsException { super(_id, null, _name); this.sqlTable = _sqlTable; this.parent = _parentId; this.attributeType = _attributeType; this.defaultValue = _defaultValue != null ? _defaultValue.trim() : null; this.dimensionUUID = _dimensionUUID != null ? _dimensionUUID.trim() : null; // add SQL columns and evaluate if attribute is required boolean req = false; int sizeTemp = 0; int scaleTemp = 0; final StringTokenizer tok = new StringTokenizer(_sqlColNames.trim(), ","); while (tok.hasMoreTokens()) { final String colName = tok.nextToken().trim(); getSqlColNames().add(colName); final ColumnInformation columInfo = this.sqlTable.getTableInformation().getColInfo(colName); if (columInfo == null) { throw new EFapsException(Attribute.class, "Attribute", _id, _name, _sqlTable, colName); } req |= !columInfo.isNullable(); sizeTemp = columInfo.getSize(); scaleTemp = columInfo.getScale(); } this.size = sizeTemp; this.scale = scaleTemp; this.required = req; } /** * This is the constructor for class {@link Attribute}. Every instance of * class {@link Attribute} must have a name (parameter <i>_name</i>) and an * identifier (parameter <i>_id</i>).<br/> * This constructor is used for the copy method (clone of an attribute * instance). * * @see #copy * @param _id id of the attribute * @param _name name of the instance * @param _sqlTable table of this attribute * @param _attributeType typer of this attribute * @param _defaultValue default value for this attribute * @param _dimensionUUID uuid of the dimension belnging to this attribute * @param _required is it required * @param _size Size * @param _scale Scale */ // CHECKSTYLE:OFF private Attribute(final long _id, final long _parentId, final String _name, final SQLTable _sqlTable, final AttributeType _attributeType, final String _defaultValue, final String _dimensionUUID, final boolean _required, final int _size, final int _scale) { // CHECKSTYLE:ON super(_id, null, _name); this.parent = _parentId; this.sqlTable = _sqlTable; this.attributeType = _attributeType; this.defaultValue = _defaultValue != null ? _defaultValue.trim() : null; this.required = _required; this.size = _size; this.scale = _scale; this.dimensionUUID = _dimensionUUID; } /** * This method returns <i>true</i> if a link exists. This is made with a * test of the return value of method {@link #getLink} on null. * * @return <i>true</i> if this attribute has a link, otherwise <i>false</i> */ public boolean hasLink() { return this.link != null; } /** * The method makes a clone of the current attribute instance. * @param _parentId if of the parent type * @return clone of current attribute instance */ protected Attribute copy(final long _parentId) { final Attribute ret = new Attribute(getId(), _parentId, getName(), this.sqlTable, this.attributeType, this.defaultValue, this.dimensionUUID, this.required, this.size, this.scale); ret.getSqlColNames().addAll(getSqlColNames()); ret.setLink(this.link); ret.setClassName(getClassName()); ret.getProperties().putAll(getProperties()); return ret; } /** * {@inheritDoc} */ @Override public void addEvent(final EventType _eventtype, final EventDefinition _eventdef) throws CacheReloadException { super.addEvent(_eventtype, _eventdef); for (final Type child : getParent().getChildTypes()) { final Attribute childAttr = child.getAttribute(getName()); if (childAttr != null) { childAttr.addEvent(_eventtype, _eventdef); } } } /** * This is the getter method for instance variable {@link #sqlTable}. * * @return value of instance variable {@link #sqlTable} * @see #sqlTable */ public SQLTable getTable() { return this.sqlTable; } /** * This is the setter method for instance variable {@link #link}. * * @param _link new instance of class {@link Long} to set for link * @see #link * @see #getLink */ protected void setLink(final Long _link) { this.link = _link; } /** * This is the getter method for instance variable {@link #link}. * * @return value of instance variable {@link #link} * @throws CacheReloadException on erorr */ public Type getLink() throws CacheReloadException { if (this.link == null) { LOG.error("Access on Attribute Link without parent defintion: {}", this); } return Type.get(this.link); } /** * Getter method for the instance variable {@link #dependencies}. * * @return value of instance variable {@link #dependencies} * @throws CacheReloadException on error */ public Map<String, Attribute> getDependencies() throws CacheReloadException { if (this.dependencies == null) { this.dependencies = new TreeMap<String, Attribute>(); // in case of a rate attribute the dependencies to the currencies // must be given if (getProperties().containsKey("CurrencyAttribute4Rate")) { this.dependencies.put("CurrencyAttribute4Rate", getParent().getAttribute(getProperties().get("CurrencyAttribute4Rate"))); this.dependencies.put("TargetCurrencyAttribute4Rate", getParent().getAttribute(getProperties().get("TargetCurrencyAttribute4Rate"))); } } return this.dependencies; } /** * This is the getter method for instance variable {@link #parent}. * * @return value of instance variable {@link #parent} * @throws CacheReloadException on error */ public Type getParent() throws CacheReloadException { return Type.get(this.parent); } /** * @return the parent Type id */ public Long getParentId() { return this.parent; } /** * This is the getter method for instance variable {@link #parentSet}. * * @return value of instance variable {@link #parentSet} * */ public AttributeSet getParentSet() { return this.parentSet; } /** * This is the setter method for instance variable {@link #parentSet}. * * @param _parentSet new instance of class {@link AttributeSet} to set */ private void setParentSet(final AttributeSet _parentSet) { this.parentSet = _parentSet; } /** * This is the getter method for instance variable {@link #sqlColNames}. * * @return value of instance variable {@link #sqlColNames} * @see #sqlColNames */ public ArrayList<String> getSqlColNames() { return this.sqlColNames; } /** * This is the getter method for instance variable {@link #attributeType}. * * @return value of instance variable {@link #attributeType} * @see #attributeType */ public AttributeType getAttributeType() { return this.attributeType; } /** * This is the getter method for instance variable {@link #defaultValue}. * * @return value of instance variable {@link #defaultValue} * @see #defaultValue */ public String getDefaultValue() { return this.defaultValue; } /** * This is the getter method for instance variable {@link #required}. * * @return value of instance variable {@link #required} * @see #required */ public boolean isRequired() { return this.required; } /** * Getter method for instance variable {@link #size}. * * @return value of instance variable {@link #size} */ public int getSize() { return this.size; } /** * Getter method for instance variable {@link #scale}. * * @return value of instance variable {@link #scale} */ public int getScale() { return this.scale; } /** * Method to get the dimension related to this attribute. * * @return Dimension */ public Dimension getDimension() { Dimension ret = null; try { ret = Dimension.get(UUID.fromString(this.dimensionUUID)); } catch (final CacheReloadException e) { LOG.error("Catched CacheReloadException", e); } return ret; } /** * Has this attribute an UoM. * * @return true id dimensionUUId!=null, else false */ public boolean hasUoM() { return this.dimensionUUID != null; } /** * Prepares for given <code>_values</code> depending on this attribute the * <code>_insert</code> into the database. * * @param _insert SQL insert statement for related {@link #sqlTable} * @param _values values to insert * @throws SQLException if values could not be inserted */ public void prepareDBInsert(final SQLInsert _insert, final Object... _values) throws SQLException { Object[] tmp = _values; try { final List<Return> returns = executeEvents(EventType.UPDATE_VALUE, ParameterValues.CLASS, this, ParameterValues.OTHERS, _values); for (final Return aRet : returns) { if (aRet.contains(ReturnValues.VALUES)) { tmp = (Object[]) aRet.get(ReturnValues.VALUES); } } } catch (final EFapsException e) { throw new SQLException(e); } this.attributeType.getDbAttrType().prepareInsert(_insert, this, tmp); } /** * Prepares for given <code>_values</code> depending on this attribute the * <code>_update</code> into the database. * * @param _update SQL update statement for related {@link #sqlTable} * @param _values values to update * @throws SQLException if values could not be inserted */ public void prepareDBUpdate(final SQLUpdate _update, final Object... _values) throws SQLException { Object[] tmp = _values; try { final List<Return> returns = executeEvents(EventType.UPDATE_VALUE, ParameterValues.CLASS, this, ParameterValues.OTHERS, _values); for (final Return aRet : returns) { if (aRet.contains(ReturnValues.VALUES)) { tmp = (Object[]) aRet.get(ReturnValues.VALUES); } } } catch (final EFapsException e) { throw new SQLException(e); } this.attributeType.getDbAttrType().prepareUpdate(_update, this, tmp); } /** * * @param _objectList object list from the database * @return found value * @throws EFapsException if values could not be read from the * <code>_objectList</code> */ public Object readDBValue(final List<Object> _objectList) throws EFapsException { Object ret = this.attributeType.getDbAttrType().readValue(this, _objectList); final List<Return> returns = executeEvents(EventType.READ_VALUE, ParameterValues.CLASS, this, ParameterValues.OTHERS, ret); for (final Return aRet : returns) { if (aRet.contains(ReturnValues.VALUES)) { ret = aRet.get(ReturnValues.VALUES); } } return ret; } /** * @return the key for the DBProperties value */ public String getLabelKey() { return getKey() + ".Label"; } /** * @return the key for the DBProperties value */ public String getKey() { if (this.key == null) { try { this.key = getParent().getName() + "/" + getName(); } catch (final CacheReloadException e) { Attribute.LOG.error("Problems during reading of key for Attribute: {}", this); } } return this.key; } /** * Getter method for the instance variable {@link #className}. * * @return value of instance variable {@link #className} */ public String getClassName() { return this.className; } /** * Setter method for instance variable {@link #className}. * * @param _className value for instance variable {@link #className} */ protected void setClassName(final String _className) { this.className = _className; } /** * Method to initialize this Cache. * * @param _class clas that called this method * @throws CacheReloadException on error */ public static void initialize(final Class<?> _class) throws CacheReloadException { if (InfinispanCache.get().exists(Attribute.NAMECACHE)) { InfinispanCache.get().<String, Attribute>getCache(Attribute.NAMECACHE).clear(); } else { InfinispanCache.get().<String, Attribute>getCache(Attribute.NAMECACHE) .addListener(new CacheLogListener(Attribute.LOG)); } if (InfinispanCache.get().exists(Attribute.IDCACHE)) { InfinispanCache.get().<Long, Attribute>getCache(Attribute.IDCACHE).clear(); } else { InfinispanCache.get().<Long, Attribute>getCache(Attribute.IDCACHE) .addListener(new CacheLogListener(Attribute.LOG)); } } /** * Method to initialize the Cache of this CacheObjectInterface. * * @throws CacheReloadException on error */ public static void initialize() throws CacheReloadException { Attribute.initialize(Attribute.class); } /** * Returns for given parameter <i>_id</i> the instance of class * {@link Attribute}. * * @param _id id to search in the cache * @return instance of class {@link Attribute} * @throws CacheReloadException on error * @see #getCache */ public static Attribute get(final long _id) throws CacheReloadException { final Cache<Long, Attribute> cache = InfinispanCache.get().<Long, Attribute>getCache(Attribute.IDCACHE); if (!cache.containsKey(_id)) { Type.get(Attribute.getTypeID(_id)); } return cache.get(_id); } /** * Returns for given parameter <i>_name</i> the instance of class * {@link Attribute}. * * @param _name name to search in the cache * @return instance of class {@link Attribute} * @throws CacheReloadException on error * @see #getCache */ @SuppressFBWarnings("RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORE") public static Attribute get(final String _name) throws CacheReloadException { final Cache<String, Attribute> cache = InfinispanCache.get() .<String, Attribute>getCache(Attribute.NAMECACHE); if (!cache.containsKey(_name)) { final String[] nameParts = _name.split("/"); if (nameParts != null && nameParts.length == 2) { Type.get(nameParts[0]); } } return cache.get(_name); } /** * @param _attr Attribute to be cached * @param _type Parent Type */ private static void cacheAttribute(final Attribute _attr, final Type _type) { final Cache<String, Attribute> nameCache = InfinispanCache.get() .<String, Attribute>getIgnReCache(Attribute.NAMECACHE); if (_type != null) { nameCache.putIfAbsent(_type.getName() + "/" + _attr.getName(), _attr); } else { nameCache.putIfAbsent(_attr.getKey(), _attr); } final Cache<Long, Attribute> idCache = InfinispanCache.get() .<Long, Attribute>getIgnReCache(Attribute.IDCACHE); idCache.putIfAbsent(_attr.getId(), _attr); } /** * The instance method returns the string representation of this attribute. * The string representation of this attribute is the name of the type plus * slash plus name of this attribute. (Must not contain getKey()!!) * * @return String representation */ @Override public String toString() { return new ToStringBuilder(this).appendSuper(super.toString()) .append("attributetype", getAttributeType().toString()).append("required", this.required) .toString(); } @Override public boolean equals(final Object _obj) { final boolean ret; if (_obj instanceof Attribute) { ret = ((Attribute) _obj).getId() == getId(); } else { ret = super.equals(_obj); } return ret; } @Override public int hashCode() { return Long.valueOf(getId()).intValue(); } /** * @param _attrId id of an attribute * @return the id of a Type * @throws CacheReloadException on error */ protected static long getTypeID(final long _attrId) throws CacheReloadException { long ret = 0; ConnectionResource con = null; try { con = Context.getThreadContext().getConnectionResource(); PreparedStatement stmt = null; try { stmt = con.getConnection().prepareStatement(Attribute.SQL_ATTR); stmt.setObject(1, _attrId); final ResultSet rs = stmt.executeQuery(); while (rs.next()) { ret = rs.getLong(1); } rs.close(); } finally { if (stmt != null) { stmt.close(); } } con.commit(); } catch (final SQLException e) { throw new CacheReloadException("Cannot read a type for an attribute.", e); } catch (final EFapsException e) { throw new CacheReloadException("Cannot read a type for an attribute.", e); } finally { if (con != null && con.isOpened()) { try { con.abort(); } catch (final EFapsException e) { throw new CacheReloadException("Cannot read a type for an attribute.", e); } } } return ret; } /** * @param _type Type the attributes are wanted for * @throws EFapsException on error */ protected static void add4Type(final Type _type) throws EFapsException { ConnectionResource con = null; try { con = Context.getThreadContext().getConnectionResource(); PreparedStatement stmt = null; final List<Object[]> values = new ArrayList<Object[]>(); try { stmt = con.getConnection().prepareStatement(Attribute.SQL_TYPE); stmt.setObject(1, _type.getId()); final ResultSet rs = stmt.executeQuery(); while (rs.next()) { values.add(new Object[] { rs.getLong(1), rs.getString(2).trim(), rs.getLong(3), rs.getLong(4), rs.getLong(5), rs.getLong(6), rs.getLong(7), rs.getString(8), rs.getString(9), rs.getString(10), rs.getString(11) }); } rs.close(); } finally { if (stmt != null) { stmt.close(); } } con.commit(); final Map<Long, AttributeSet> id2Set = new HashMap<Long, AttributeSet>(); final Map<Attribute, Long> attribute2setId = new HashMap<Attribute, Long>(); final List<Attribute> attributes = new ArrayList<Attribute>(); for (final Object[] row : values) { final long id = (Long) row[0]; final String name = (String) row[1]; final long typeAttrId = (Long) row[2]; final long tableId = (Long) row[3]; final long attrTypeId = (Long) row[4]; final long typeLinkId = (Long) row[5]; final long parentSetId = (Long) row[6]; final String sqlCol = (String) row[7]; final String defaultval = (String) row[8]; final String dimensionUUID = (String) row[9]; final String className = (String) row[10]; Attribute.LOG.debug("read attribute '{}/{}' (id = {})", _type.getName(), name, id); if (Type.check4Type(typeAttrId, CIAdminDataModel.AttributeSet.uuid)) { final AttributeSet set = new AttributeSet(id, _type, name, AttributeType.get(attrTypeId), sqlCol, tableId, typeLinkId, dimensionUUID); id2Set.put(id, set); } else { final Attribute attr = new Attribute(id, _type.getId(), name, sqlCol, SQLTable.get(tableId), AttributeType.get(attrTypeId), defaultval, dimensionUUID); final UUID uuid = attr.getAttributeType().getUUID(); if (uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_LINK.getUuid()) || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_LINK_WITH_RANGES.getUuid()) || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_STATUS.getUuid())) { attr.setLink(typeLinkId); // in case of a PersonLink, CreatorLink or ModifierLink a link to Admin_User_Person // must be set } else if (uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_CREATOR_LINK.getUuid()) || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_MODIFIER_LINK.getUuid()) || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_PERSON_LINK.getUuid())) { attr.setLink(Type.getId4UUID(CIAdminUser.Person.uuid)); // in case of a GroupLink, a link to Admin_User_Group must be set } else if (uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_GROUP_LINK.getUuid())) { attr.setLink(Type.getId4UUID(CIAdminUser.Group.uuid)); // in case of a Enum and BitEnum the className must be set } else if (uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_ENUM.getUuid()) || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_BITENUM.getUuid()) || uuid.equals(Attribute.AttributeTypeDef.ATTRTYPE_JAXB.getUuid())) { if (className == null || className != null && className.isEmpty()) { Attribute.LOG.error( "An Attribute of Type Enum, BitEnum, Jaxb must have a className: {}", attr); } attr.setClassName(className.trim()); } attr.readFromDB4Properties(); if (Type.check4Type(typeAttrId, CIAdminDataModel.AttributeSetAttribute.uuid)) { attribute2setId.put(attr, parentSetId); } else { attributes.add(attr); Attribute.cacheAttribute(attr, _type); } } } // make connection between set and attributes for (final Entry<Attribute, Long> entry : attribute2setId.entrySet()) { final AttributeSet parentset = id2Set.get(entry.getValue()); final Attribute childAttr = entry.getKey(); parentset.addAttributes(false, childAttr); childAttr.setParentSet(parentset); // needed due to cluster serialization that does not update automatically Attribute.cacheAttribute(childAttr, parentset); } for (final AttributeSet set : id2Set.values()) { Type.cacheType(set); } _type.addAttributes(false, attributes.toArray(new Attribute[attributes.size()])); } catch (final SQLException e) { throw new CacheReloadException("Cannot read attributes.", e); } finally { if (con != null && con.isOpened()) { con.abort(); } } } }