de.tsl2.nano.bean.BeanContainer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tsl2.nano.descriptor Show documentation
Show all versions of tsl2.nano.descriptor Show documentation
TSL2 Framework Descriptor (currency-handling, generic formatter, descriptors for beans, collections, actions and values)
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Thomas Schneider
* created on: Feb 4, 2010
*
* Copyright: (c) Thomas Schneider 2010, all rights reserved
*/
package de.tsl2.nano.bean;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import de.tsl2.nano.action.CommonAction;
import de.tsl2.nano.action.IAction;
import de.tsl2.nano.bean.def.Bean;
import de.tsl2.nano.bean.def.BeanDefinition;
import de.tsl2.nano.bean.def.BeanValue;
import de.tsl2.nano.bean.def.IAttributeDefinition;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.Messages;
import de.tsl2.nano.core.cls.BeanAttribute;
import de.tsl2.nano.core.cls.BeanClass;
import de.tsl2.nano.core.cls.IAttribute;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.core.util.DateUtil;
import de.tsl2.nano.core.util.ListSet;
import de.tsl2.nano.core.util.NumberUtil;
import de.tsl2.nano.core.util.StringUtil;
/**
* class to be used as simple one instance bean container. initialize it with your service actions (through calls to
* specific service factories). it is a delegating class to avoid depending to a service factory and its services.
*
* @author Thomas Schneider
* @version $Revision$
*/
@SuppressWarnings({ "serial", "rawtypes", "unchecked" })
public class BeanContainer implements IBeanContainer {
/** we provide a thread invariant and a thread variant singelton instance - this is not secure but practicable. */
private static BeanContainer self = null;
private static ThreadLocal selfThread = new ThreadLocal();
private static final Log LOG = LogFactory.getLog(BeanContainer.class);
/** on initializing with {@link #initEmtpyServiceActions()} it is true */
private boolean emptyServices = false;
protected IAction> idFinderAction = null;
protected IAction> typeFinderAction = null;
protected IAction> exampleFinderAction = null;
protected IAction> betweenFinderAction = null;
protected IAction> queryAction = null;
protected IAction> queryMapAction = null;
protected IAction> lazyrelationInstantiateAction = null;
protected IAction> saveAction = null;
protected IAction> deleteAction = null;
protected IAction attrdefAction = null;
protected IAction> permissionAction = null;
protected IAction> persistableAction = null;
protected IAction executeAction = null;
private BeanContainer() {
super();
}
/**
* you must have called {@link #initServiceActions(IAction, IAction, IAction)} before you can use the instance.
*
* @return singelton instance
*/
public static final BeanContainer instance() {
assert self != null : "beancontainer not initialized! call initServiceActions(...) before!";
return self();
}
private static BeanContainer self() {
return selfThread.get() != null ? selfThread.get() : self;
}
/**
* isInitialized
*
* @return true, if {@link #initEmtpyServiceActions()} or
* {@link #initServiceActions(IAction, IAction, IAction, IAction, IAction, IAction, IAction, IAction, IAction, IAction)}
* was called before.
*/
public static final boolean isInitialized() {
return self() != null;
}
/**
* call this method only on initializing your new application.
*
* @param relationFinder action to find beans through a service
* @param lazyRelationResolver to fetch all lazy relations
* @param saveAction action to save bean through service
* @param deleteAction action to delete bean through service
* @param exampleFinder action to find beans through a service
* @param betweenFinder action to find beans through a service
* @param attrdefAction action to evaluate attribute definitions
* @param permissionAction action to evaluate the permission (through roles)
* @param persistableAction asks, if the given bean is persistable
*/
public static final void initServiceActions(IAction> idFinder,
IAction> relationFinder,
IAction> lazyRelationResolver,
IAction> saveAction,
IAction> deleteAction,
IAction> exampleFinder,
IAction> betweenFinder,
IAction> queryFinder,
IAction> queryMapFinder,
IAction attrdefAction,
IAction permissionAction,
IAction persistableAction,
IAction executeAction) {
self = new BeanContainer();
selfThread.set(self);
self.idFinderAction = idFinder;
self.typeFinderAction = relationFinder;
self.lazyrelationInstantiateAction = lazyRelationResolver;
self.saveAction = saveAction;
self.deleteAction = deleteAction;
self.exampleFinderAction = exampleFinder;
self.betweenFinderAction = betweenFinder;
self.queryAction = queryFinder;
self.queryMapAction = queryMapFinder;
self.attrdefAction = attrdefAction;
self.permissionAction = permissionAction;
self.persistableAction = persistableAction;
self.executeAction = executeAction;
}
/**
* will call {@link #initServiceActions(IAction, IAction, IAction)} with simple actions, doing nothing. useful for
* testing.
*/
public static final void initEmtpyServiceActions() {
final Collection> EMPTY_LIST = new ListSet();
final IAction idFinder = new CommonAction("empty.service.idFinder") {
@Override
public Object action() {
return null;
}
};
final IAction> relationFinder = new CommonAction>("empty.service.relationFinder") {
@Override
public Collection> action() {
LOG.debug("calling empty-services relationFinder");
return EMPTY_LIST;
}
};
final IAction> exampleFinder = new CommonAction>("empty.service.exampleFinder") {
@Override
public Collection> action() {
LOG.debug("calling empty-services exampleFinder");
return EMPTY_LIST;
}
};
final IAction> betweenFinder = new CommonAction>("empty.service.betweenFinder") {
@Override
public Collection> action() {
LOG.debug("calling empty-services betweenFinder");
return EMPTY_LIST;
}
};
final IAction> queryFinder = new CommonAction>("empty.service.queryFinder") {
@Override
public Collection> action() {
LOG.debug("calling empty-services queryFinder");
return EMPTY_LIST;
}
};
final IAction> queryMapFinder = new CommonAction>("empty.service.queryMapFinder") {
@Override
public Collection> action() {
LOG.debug("calling empty-services queryMapFinder");
return EMPTY_LIST;
}
};
final IAction lazyRelationResolver = new CommonAction("empty.service.lazyRelationResolver") {
@Override
public Object action() {
//do nothing, return the instance itself
return getParameter()[0];
}
};
final IAction saveAction = new CommonAction("empty.service.saveAction") {
@Override
public Object action() {
LOG.debug("calling empty-services saveAction");
//do nothing, return the instance itself
return getParameter()[0];
}
};
final IAction deleteAction = new CommonAction("empty.service.deleteAction") {
@Override
public Object action() {
LOG.debug("calling empty-services deleteAction");
//do nothing
return null;
}
};
final IAction attrAction = new CommonAction("empty.service.attrAction") {
@Override
public IAttributeDef action() {
//do nothing
return null;
}
};
final IAction permissionAction = new CommonAction("empty.service.permissionAction") {
@Override
public Object action() {
//do nothing
return true;
}
};
final IAction persistableAction = new CommonAction("empty.service.persistableAction") {
@Override
public Object action() {
return Serializable.class.isAssignableFrom((Class>) getParameter()[0]);
}
};
final IAction executeAction = new CommonAction("empty.service.executeAction") {
@Override
public Integer action() {
return null;
}
};
BeanContainer.initServiceActions(idFinder,
relationFinder,
lazyRelationResolver,
saveAction,
deleteAction,
exampleFinder,
betweenFinder,
queryFinder,
queryMapFinder,
attrAction,
permissionAction,
persistableAction,
executeAction);
self.emptyServices = true;
}
/**
* @return see {@link #emptyServices}
*/
public static boolean isConnected() {
return isInitialized() && !self().emptyServices;
}
/**
* {@inheritDoc}
*/
@Override
public T createBean(Class type) {
return createBeanInstance(type);
}
/**
* createBeanInstance
*
* @param
* @param type
* @return
*/
public static T createBeanInstance(Class type) {
try {
T bean;
if (type.isInterface()) {
bean = BeanProxy.createBeanImplementation(type, null, null, Thread.currentThread()
.getContextClassLoader());
} else {
bean = BeanClass.createInstance(type);
}
initDefaults(bean);
return bean;
} catch (final Exception e) {
ManagedException.forward(e);
return null;
}
}
/**
* initializes collections not to be null.
* @param bean new created bean.
*/
public static void initDefaults(T bean) {
//to fulfill bindings on onetomany, we bind empty lists
final BeanClass clazz = BeanClass.getBeanClass(bean.getClass());
final Collection multiValueAttributes = clazz.getMultiValueAttributes();
for (final BeanAttribute beanAttribute : multiValueAttributes) {
if (Set.class.isAssignableFrom(beanAttribute.getType())) {
beanAttribute.setValue(bean, new LinkedHashSet());
} else if (Collection.class.isAssignableFrom(beanAttribute.getType())) {
beanAttribute.setValue(bean, new LinkedList());
} else if (Properties.class.isAssignableFrom(beanAttribute.getType())) {
beanAttribute.setValue(bean, new Properties());
} else if (Map.class.isAssignableFrom(beanAttribute.getType())) {
beanAttribute.setValue(bean, new LinkedHashMap());
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void delete(T bean) {
deleteAction.setParameter(new Object[] { bean });
deleteAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public T save(T bean) {
saveAction.setParameter(new Object[] { bean });
return (T) saveAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public T getByID(Class type, Object id) {
idFinderAction.setParameter(new Object[] { type, id });
return (T) idFinderAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public Collection getBeans(Class type, int startIndex, int maxResult) {
typeFinderAction.setParameter(new Object[] { type, startIndex, maxResult });
return (Collection) typeFinderAction.activate();
}
@Override
public Collection getBeans(BeanFindParameters parameters) {
typeFinderAction.setParameter(new Object[] { parameters });
return (Collection) typeFinderAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public Collection getBeansByExample(T exampleBean) {
return getBeansByExample(exampleBean, false, 0, Integer.MAX_VALUE);
}
/**
* {@inheritDoc}
*/
@Override
public Collection getBeansByExample(T exampleBean, Boolean useLike, int startIndex, int maxResult) {
exampleFinderAction.setParameter(new Object[] { exampleBean, useLike, startIndex, maxResult });
return (Collection) exampleFinderAction.activate();
}
@Override
public Collection getBeansByExample(T exampleBean, Boolean useLike, BeanFindParameters parameters) {
exampleFinderAction.setParameter(new Object[] { exampleBean, useLike, parameters });
return (Collection) exampleFinderAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public Collection getBeansBetween(T firstExampleBean, T secondExampleBean, int startIndex, int maxResult) {
betweenFinderAction.setParameter(new Object[] { firstExampleBean, secondExampleBean, startIndex, maxResult });
return (Collection) betweenFinderAction.activate();
}
@Override
public Collection getBeansBetween(T firstBean, T secondBean, BeanFindParameters parameters) {
betweenFinderAction.setParameter(new Object[] { firstBean, secondBean, parameters });
return (Collection) betweenFinderAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public T resolveLazyRelations(T bean) {
lazyrelationInstantiateAction.setParameter(new Object[] { bean });
return (T) lazyrelationInstantiateAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public IAttributeDef getAttributeDef(Object bean, String attributeName) {
attrdefAction.setParameter(new Object[] { bean, attributeName });
return attrdefAction.activate();
}
@Override
public Boolean hasPermission(String roleName, String action) {
permissionAction.setParameter(new Object[] { roleName, action });
return (Boolean) permissionAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPersistable(Class> beanClass) {
persistableAction.setParameter(new Object[] { beanClass });
return (Boolean) persistableAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public Collection getBeansByQuery(String query, Boolean nativeQuery, Object[] args, Class... lazyRelations) {
queryAction.setParameter(new Object[] { query, nativeQuery, args, lazyRelations });
return (Collection) queryAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public Collection getBeansByQuery(String query,
Boolean nativeQuery,
Map par,
Class... lazyRelations) {
queryMapAction.setParameter(new Object[] { query, nativeQuery, par, lazyRelations });
return (Collection) queryMapAction.activate();
}
/**
* {@inheritDoc}
*/
@Override
public Integer executeStmt(String query, Boolean nativeQuery, Object[] args) {
executeAction.setParameter(new Object[] { query, nativeQuery, args });
return executeAction.activate();
}
/**
* isConstraintError
*
* @param e server exception
* @return true, if error-cause contains a constraint violation
*/
public static boolean isConstraintError(Exception e) {
//etwas unsauber!
Throwable cause = e;
Throwable c = cause;
while (c != null) {
cause = c;
c = c.getCause();
}
if (cause.toString().contains("onstraint")) {
return true;
}
return false;
}
/**
* convenience method to ask for a user role depending on a bean presenters action.
*
* @param beanType bean class. this should be a specific class like an entity - no collection of entities!
* @param packedInList true, if the bean type is packed into a collection. e.g. in
* BeanContainerView/BeanContainerDialog.
* @param actionName translated action name.
* @return true, if user has role for that action, false otherwise
*/
public static final boolean hasPermission(Class> beanType, boolean packedInList, String actionName) {
return instance().hasPermission(getActionId(beanType, packedInList, actionName), null);
}
/**
* data rows for the given type
*
* @param beanType bean type
* @return count(*) for type
*/
public static final long getCount(Class> beanType) {
return ((Number) instance()
.getBeansByQuery("select count(*) from " + beanType.getSimpleName(), true, (Object[]) null).iterator()
.next()).longValue();
}
/**
* getActionId
*
* @param beanType presenters bean type to create an action for
* @param packedInList whether it is a bean instance or a bean container
* @param actionName simple action name
* @return full action id
*/
public static final String getActionId(Class> beanType, boolean packedInList, String actionName) {
final String type =
BeanClass.getName(beanType).toLowerCase() + (packedInList ? Messages.getString("tsl2nano.list")
.toLowerCase()
: "");
return type + "." + actionName.toLowerCase();
}
/**
* will search for the actionId in the resource bundle - then in tsl2nano base bundle - if not found, the action
* simple name will be returned
*
* @param actionId full action id
* @param actionSimpleName simple action name
* @param if true, the tooltip for the action will be evaluated
* @return text to show on action
*/
public static final String getActionText(String actionId, boolean tooltip) {
final String tooltip_postfix = (tooltip ? Messages.POSTFIX_TOOLTIP : "");
final String actionSimpleName = actionId.substring(actionId.lastIndexOf(".") + 1);
String text = Messages.getString(actionId + tooltip_postfix);
if (Messages.unknown(text)) {
text = Messages.getString("tsl2nano." + actionSimpleName + tooltip_postfix);
if (Messages.unknown(text)) {
return StringUtil.toFirstUpper(actionSimpleName);
} else if (tooltip) {
return "";
} else {
return text;
}
}
return text;
}
/**
* evaluates the given entities bean id. don't call that method, if the bean is not an ejb entity!
*
* @param beanOrClass entity bean or type to evaluate
* @return bean id attribute or null, if not existent
*/
public static BeanAttribute getIdAttribute(Object beanOrClass) {
if (!isInitialized()) {
return null;
}
Class cls =
(Class) (beanOrClass == null || beanOrClass instanceof Class ? beanOrClass : beanOrClass.getClass());
if (cls == null || !instance().isPersistable(cls))
return null;
final BeanClass bc = BeanClass.getBeanClass(cls);
final Collection attributes = bc.getSingleValueAttributes();
for (final BeanAttribute attr : attributes) {
final IAttributeDef attributeDef = BeanContainer.instance().getAttributeDef(cls, attr.getName());
if (attributeDef != null && attributeDef.id()) {
return attr;
}
}
return null;
}
/**
* see {@link #getIdAttribute(Object)}
*
* @param bean instance
* @return true, if bean is persistable and id attribute is null
*/
public boolean isTransient(Object bean) {
final BeanAttribute idAttribute = getIdAttribute(bean);
Object id;
if (idAttribute != null && (id = idAttribute.getValue(bean)) != null) {
Class type = idAttribute.getDeclaringClass();
return !isPersistable(type) || getByID(type, id) == null;
} else {
return true;
}
}
//TODO: implement getValueBetween using new action
// public static final Collection getValueBetween(String attributeName, ClassattributeType, T first, T second) {
// ServiceUtil.
// BeanContainer.instance().getBeansBetween(first, second, -1);
// Collection result;
//
// }
public static void reset() {
self = null;
selfThread.remove(); //how to remove all other thread-values?
}
/**
* reloads all referenced entities (having only an id)
*
* @param obj transient (example) entity holding attached entities as relations
* @return the obj itself
*/
public static Object attachEntities(Object obj) {
List> attributes = Bean.getBean(obj).getBeanAttributes();
BeanValue bv;
for (IAttributeDefinition> a : attributes) {
if (a.isRelation() && (bv = (BeanValue) a).getValue() != null) {
Bean b = Bean.getBean(bv.getValue());
Object n = BeanContainer.instance().getByID(bv.getType(), b.getId());
bv.setValue(n);
}
}
return obj;
}
/**
* replaces attached entities with copies holding only the id
*
* @param obj transient (example) entity holding attached entities as relations
* @return the obj itself
*/
public static Object detachEntities(Object obj) {
List> attributes = Bean.getBean(obj).getBeanAttributes();
BeanValue bv;
for (IAttributeDefinition> a : attributes) {
if (a.isRelation() && (bv = (BeanValue) a).getValue() != null) {
Bean b = Bean.getBean(bv.getValue());
Object n = BeanClass.createInstance(a.getType());
Bean.getBean(n).getIdAttribute().setValue(n, b.getId());
bv.setValue(n);
}
}
return obj;
}
/**
* creates and sets a generated value for the id. if jpa annotation @GenerateValue is present, it will overwrite this id.
*
* @param newItem
*/
public static void createId(Object newItem) {
if (ENV.get("value.id.fill.uuid", true)) {
final BeanAttribute idAttribute = BeanContainer.getIdAttribute(newItem);
if (idAttribute != null) {
Object value = null;
if (String.class.isAssignableFrom(idAttribute.getType())) {
IAttributeDef def = ENV.get(IBeanContainer.class).getAttributeDef(newItem,
idAttribute.getName());
//TODO: through string cut, the uuid may not be unique
value =
StringUtil.fixString(BeanUtil.createUUID(), (def.length() > -1 ? def.length() : 0), ' ', true);
} else if (NumberUtil.isNumber(idAttribute.getType())) {
//subtract the years from 1970 to 2015 to be castable to an int
//TODO: use a more unique value
if (ENV.get("value.id.use.timestamp", false)) {
value = System.currentTimeMillis();
if (NumberUtil.isInteger(idAttribute.getType())) {
value = DateUtil.getMillisWithoutYear((Long) value);
}
} else {
value = ENV.counter("collector.new.id.number.counter.start", 1);
}
} else if (ENV.get("container.try.generate.multi.primary.key", true)
&& (idAttribute.getType().getPackage() == null || idAttribute.getType().getPackage().equals(newItem.getClass().getPackage()))) {
value = createCompositeKey(newItem, idAttribute);
if (value == null)
LOG.warn("the id-attribute " + idAttribute + " can't be created for "
+ idAttribute.getType());
} else {
LOG.warn("the id-attribute " + idAttribute + " can't be assigned to a generated value of type "
+ idAttribute.getType());
}
idAttribute.setValue(newItem, value);
return;
}
}
LOG.debug("no id will be generated for new item " + newItem);
}
/**
* tries to create the
* @param newItem
* @param idAttribute
* @return
*/
private static Object createCompositeKey(Object newItem, BeanAttribute idAttribute) {
Object value = BeanClass.createInstance(idAttribute.getType());
//TODO: implement going through all item attributes, get their idAttributes and try to set them to idValue
Bean