Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.tsl2.nano.service.util.GenericServiceBean Maven / Gradle / Ivy
Go to download
TSL2 JEE Service Access (Generic Services for Entity Access, JEE File-System-Connector, Generic Featuring, Job-Scheduling, BeanContainer, Batch, Comfortable Bean Query Definitions, JAAS, Authentification, Authorization, )
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: TS, Thomas Schneider
* created on: Jan 11, 2010
*
* Copyright: (c) Thomas Schneider 2010, all rights reserved
*/
package de.tsl2.nano.service.util;
import static de.tsl2.nano.service.util.ServiceUtil.*;
import static de.tsl2.nano.service.util.ServiceUtil.createBetweenStatement;
import static de.tsl2.nano.service.util.ServiceUtil.createExampleStatement;
import static de.tsl2.nano.service.util.ServiceUtil.createStatement;
import static de.tsl2.nano.service.util.ServiceUtil.getId;
import static de.tsl2.nano.service.util.ServiceUtil.getIdName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.Query;
import javax.security.auth.Subject;
import de.tsl2.nano.bean.BeanFindParameters;
import de.tsl2.nano.bean.def.Diff;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.cls.BeanAttribute;
import de.tsl2.nano.core.cls.BeanClass;
import de.tsl2.nano.core.util.MapUtil;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.service.util.batch.Part;
import de.tsl2.nano.service.util.finder.AbstractFinder;
import de.tsl2.nano.service.util.finder.Finder;
import de.tsl2.nano.serviceaccess.aas.principal.UserPrincipal;
/**
* provides some common and batch service access methods to work with beans.
*
* Five base services are used by all others:
* {@link #findByQuery(String, boolean, int, int, Object[], Map, Class...)}
* {@link #findById(Class, Object, Class...)}
* {@link #instantiateLazyRelationship(Class, Object, String[], List)}
* {@link #persistNoTransaction(Object, boolean, boolean, Class...)}
* {@link #remove(Object)}
* {@link #executeQuery(String, boolean, Object[])}
*
* pro
*
* @author TS
*
*/
@Stateless
@SuppressWarnings({ "rawtypes", "unchecked" })
public class GenericServiceBean extends NamedQueryServiceBean implements IGenericService, IGenericLocalService {
// private Map, Object> beanIdCache = new Hashtable, Object>();
/** {@inheritDoc} */
@Override
public Collection findAll(Class beanType, Class... lazyRelations) {
return findAll(new BeanFindParameters(beanType, 0, -1, null, null, lazyRelations));
}
@Override
public Collection findAll(Class beanType, int startIndex, int maxResult, Class... lazyRelations) {
return findAll(new BeanFindParameters(beanType, startIndex, maxResult, lazyRelations));
}
/** {@inheritDoc} */
@Override
public Collection findAll(BeanFindParameters p) {
checkContextSecurity();
Class beanType = p.getBeanType();
if (isVirtualEntity(beanType)) {
if (isNamedQuery(beanType)) {
return findByNamedQuery(beanType, getNamedQueryByArguments(beanType), p.getMaxResult());
}
}
final StringBuffer qStr = createStatement(beanType);
qStr.append(addOrderBy(p.getOrderBy()));
Map hints = MapUtil.asMap("org.hibernate.cacheable",
Boolean.TRUE,
"org.hibernate.readOnly",
Boolean.TRUE);
if (p.getHints() != null) {
hints.putAll(p.getHints());
}
//a findAll should only be done on 'configuration' tables
//QUESTION: why does the query perform poor on activated cache????
return (Collection) findByQuery(qStr.toString(), false, p.getStartIndex(), p.getMaxResult(), null, hints, p.getLazyRelations());
}
/** {@inheritDoc} */
@Override
public Collection findMembers(H holder, Class beanType, String attributeName, Class... lazyRelations) {
checkContextSecurity();
final StringBuffer qStr = createStatement(beanType);
addMemberExpression(qStr, holder, beanType, attributeName);
return (Collection) findByQuery(qStr.toString(),
false,
0,
-1,
new Object[] { getId(holder) },
null,
lazyRelations);
}
/** {@inheritDoc} */
@Override
public Collection findHolders(T member, Class holderType, String attributeName, Class... lazyRelations) {
checkContextSecurity();
final StringBuffer qStr = createStatement(holderType);
final String idAttribute = getIdName(member);
qStr.append(", " + member.getClass().getSimpleName()
+ " t1\n where t1."
+ idAttribute
+ " = ? and t1 member of t."
+ attributeName);
return (Collection) findByQuery(qStr.toString(),
false,
0,
-1,
new Object[] { idAttribute },
null,
lazyRelations);
}
/** {@inheritDoc} */
@Override
public T findById(Class beanType, Object id, Class... lazyRelations) {
checkContextSecurity();
if (isVirtualEntity(beanType)) {
if (isNamedQuery(beanType)) {
final Collection result = findByNamedQuery(beanType, getNamedQueryByArguments(beanType, id), -1, id);
if (result.size() > 1) {
LOG.warn("findById (" + beanType
+ ", "
+ id
+ ") found more than one unbound entity:"
+ StringUtil.toString(result, 100));
}
return result.size() > 0 ? result.iterator().next() : null;
}
}
final T bean = connection().find(beanType, id);
if (bean == null) {
return null;
}
return fillTree(Arrays.asList(bean), lazyRelations).iterator().next();
}
/**
* helper for all finders
*
* @param bean type
* @param result instance
* @param lazyRelations relation types to fill
* @return perhaps filled object tree
*/
protected final Collection fillTree(Collection result, Class... lazyRelations) {
if (lazyRelations != null && lazyRelations.length > 0) {
List relations = Arrays.asList(lazyRelations);
for (T bean : result) {
//if an object array is returned we do it for all objects
Object[] b = (Object[]) (bean instanceof Object[] ? bean : new Object[] { bean });
for (int i = 0; i < b.length; i++) {
resetTreeVars(b[i]);
fillTree(b[i], null, relations);
finishTree(b[i], lazyRelations);
}
}
}
return result;
}
/**
* because fillTree() is recursive, we use this method by every caller to clean the data.
*
* @param bean entity
* @param lazyRelations relations to be filled
*/
private final void finishTree(Object bean, Class... lazyRelations) {
if (recurseLevel > 0 || instantiatedEntities.size() > 0) {
LOG.debug("==> fillTree finished on bean:" + bean
+ ", recursionLevel="
+ recurseLevel
+ ", instantiated entities:"
+ instantiatedEntities.size());
}
// recurseLevel = 0;
// instantiatedEntities.clear();
}
/**
* fills recursive all given attribute-relations of type fillType.
*
* @param bean bean, that must not already be serialized.
* @param attributes relations. if null, all attributes will be filled
* @param fillTypes (optional) relation types to fill. if null, all types will be filled.
*/
private int recurseLevel = 0;
private final int MAX_RECURSLEVEL = 10;
private final Collection accessedBeanAttributes = new LinkedHashSet();
private final Collection instantiatedEntities = new LinkedHashSet();
private void fillTree(Object bean, String[] attributes, List fillTypes) {
final Class> clazz = bean.getClass();
Object relation;
/*
* do we use to instantiate non-collections? we do it to fulfill any mapping strategy.
* the checkTypesOnly is used, if no attribute names were given
*/
boolean checkTypesOnly = false;
if (attributes == null) {
attributes =
/*isLazyLoadingOnlyOnOneToMany() ? new BeanClass(clazz).getMultiValueAttributes() : */BeanClass
.getBeanClass(clazz).getAttributeNames();
if (fillTypes != null && fillTypes.size() > 0) {
checkTypesOnly = true;
}
}
try {
BeanAttribute beanAttribute;
for (final String attribute : attributes) {
beanAttribute = BeanAttribute.getBeanAttribute(clazz, attribute);
// Class> relationType;
/*
* for performance aspects, ask, if attribute-type is contained in given relation types!
* for collections, the above solution is available, as it is now possible to evaluate the content type
* of a collection on runtime without loading the collection (eager).
*/
Class> relationType = beanAttribute.getType();
if (Collection.class.isAssignableFrom(relationType)) {
relationType = beanAttribute.getGenericType();
}
if (checkTypesOnly && !fillTypes.contains(relationType)) {
continue;
}
/*
* now, check for eager loading
*/
relation = beanAttribute.getValue(bean);
if (relation != null) {
// call any method to instantiate the indirect object
Collection> relationSet;
if (relation instanceof Collection) {
relationSet = (Collection>) relation;
/*
* fill it through size call. it is not possible to evaluate the collections
* content type on runtime without eager-loading the collection
*/
if (relationSet.size() == 0 || fillTypes == null) {
continue;
}
relationType = relationSet.iterator().next().getClass();
} else {//simulate a collection to use the same code
relationSet = Arrays.asList(relation);
//fill it through object method call
relation.hashCode();
relationType = beanAttribute.getType();
}
if ((fillTypes != null && fillTypes.contains(relationType))
&& BeanContainerUtil.isPersistable(relationType)) {
for (final Object item : relationSet) {
if (instantiatedEntities.contains(item)) {
continue;
}
instantiatedEntities.add(item);
if (!accessedBeanAttributes.contains(beanAttribute)) {
accessedBeanAttributes.add(beanAttribute);
//on model cycles or to many fillTypes, we do a break on MAX_RECURSLEVEL!
LOG.debug("==> fillTree: attribute=" + beanAttribute.getName()
+ ", recursionLevel="
+ recurseLevel
+ ", instantiated entities:"
+ instantiatedEntities.size());
if (recurseLevel++ > getMaxRecursionLevel()) {
throw new ManagedException("instantiateLazyRelationship: max recurs level "
+ MAX_RECURSLEVEL
+ " exceeded evaluating attribute "
+ beanAttribute.getName()
+ ". Please check datamodel for cycles!\ninstantiated entities:\n"
+ StringUtil.toFormattedString(instantiatedEntities, 20, true));
}
}
fillTree(item, null, fillTypes);
}
}
}
}
} catch (final Exception e) {
LOG.error(e);
ManagedException.forward(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public T instantiateLazyRelationship(T bean) {
final Object id = getId(bean);
if (id != null) {
return (T) instantiateLazyRelationship(bean.getClass(), id, null, null);
} else {
LOG.warn("no bean id found ==> nothing to do!");
return bean;
}
}
/** {@inheritDoc} */
@Override
public T instantiateLazyRelationship(Class clazz, Object beanId, String[] attributes) {
return instantiateLazyRelationship(clazz, beanId, attributes, null);
}
/** {@inheritDoc} */
@Override
public T instantiateLazyRelationship(Class clazz, Object beanId, List fillTypes) {
return instantiateLazyRelationship(clazz, beanId, null, fillTypes);
}
/** {@inheritDoc} */
protected T instantiateLazyRelationship(Class clazz,
Object beanId,
String[] attributes,
List fillTypes) {
LOG.info("instantiating lazy relation on " + clazz
+ " with id:"
+ beanId
+ "--> (special-attributeNames)"
+ attributes);
final T bean = findById(clazz, beanId);
if (bean == null) {
throw new ManagedException("couldn''t find bean of type " + clazz
+ " with id: "
+ beanId
+ ".\nPossible causes are: null values on not-nullable attributes of that bean - or a relation!");
}
return instantiateLazyRelationship(bean, attributes, fillTypes);
}
/**
* fills recursive all attribute-relations of type fillType.
*
* @param clazz type of bean to get through beanId
* @param beanId bean id to load
* @param attributes (optional) relations
* @param fillTypes (optional) relation types to fill. if null, all types will be filled.
*/
@Override
public T instantiateLazyRelationship(T bean, String[] attributes, List fillTypes) {
resetTreeVars(bean);
fillTree(bean, attributes, fillTypes);
if (fillTypes != null) {
finishTree(bean, fillTypes.toArray(new Class[0]));
}
return bean;
}
/**
* resetTreeVars
*
* @param
* @param bean
*/
private void resetTreeVars(T bean) {
recurseLevel = 0;
accessedBeanAttributes.clear();
instantiatedEntities.clear();
instantiatedEntities.add(bean);
}
/** {@inheritDoc} */
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public T persist(T bean, Class... lazyRelations) {
return persist(bean, true, true, lazyRelations);
}
/** {@inheritDoc} */
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public T persist(T bean, boolean refreshBean, boolean flush, Class... lazyRelations) {
return persistNoTransaction(bean, refreshBean, flush, lazyRelations);
}
/** {@inheritDoc} */
@Override
public T persistNoTransaction(T bean, boolean refreshBean, boolean flush, Class... lazyRelations) {
checkContextSecurity();
final Class beanType = (Class) (bean != null ? bean.getClass() : null);
if (isVirtualEntity(beanType)) {
if (isNamedQuery(beanType)) {
return persistByNamedQuery(bean);
}
}
// try {
/*
* The merge operation is clever enough to automatically detect whether
* the merging of the detached instance has to result in an insert or
* update. In other words, you don't have to worry about passing a new
* instance (and not a detached instance) to merge(), the entity manager
* will figure this out for you.
*/
//WORKAOURND (for TopLink!): on new items with new relations it doesn't work
// if (getId(bean) == null)
// connection().persist(bean);
// else
T newbean = connection().merge(bean);
if (flush) {
connection().flush(); // force the SQL insert and triggers to run
}
if (refreshBean) {
connection().refresh(newbean); //re-read the state (after the trigger executes)
}
if (LOG.isDebugEnabled()) {
LOG.debug(new Diff(bean, newbean).toString());
}
return fillTree(Arrays.asList(newbean), lazyRelations).iterator().next();
// } catch (Exception ex) {
// //catch it and throw a new one. otherwise, the server (toplink) will
// //catch it prints only a warning. the client would only see a
// transaction exception
// throw new EJBException(ex);
// }
}
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public Collection persistCollection(Collection beans, Class... lazyRelations) {
final Collection newBeans = new ArrayList(beans.size());
for (final T bean : beans) {
newBeans.add(persistNoTransaction(bean, false, false, lazyRelations));
}
connection().flush();
return newBeans;
}
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public Object[] persistAll(Object... beans) {
final Object[] newBeans = new Object[beans.length];
for (int i = 0; i < newBeans.length; i++) {
newBeans[i] = persistNoTransaction(beans[i], false, false);
}
connection().flush();
return newBeans;
}
/** {@inheritDoc} */
@Override
// @Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void remove(Object bean) {
checkContextSecurity();
final Class> beanType = (bean != null ? bean.getClass() : null);
if (isVirtualEntity(beanType)) {
if (isNamedQuery(beanType)) {
removeByNamedQuery(bean);
}
return;
}
// try {
/*
* first: refresh the bean. perhaps it is loaded in a transaction that was marked as rollbackonly
*/
bean = refresh(bean);
// bean = connection().merge(bean);
connection().remove(bean);
connection().flush(); // force the SQL insert and triggers to run
// } catch (Exception ex) {
// //catch it and throw a new one. otherwise, the server (toplink) will
// //catch it prints only a warning. the client would only see a
// transaction exception
// throw new ManagedException(ex);
// }
}
/** {@inheritDoc} */
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void removeCollection(Collection beans) {
if (beans != null) {
for (Object bean : beans) {
remove(bean);
}
}
}
/** {@inheritDoc} */
// @Override
@Override
public T refresh(T bean) {
//the following works only, if bean was loaded in the current transaction
//connection().refresh(bean);
/*
* reloads the bean - to be loaded in the current transaction!
*/
return (T) (connection().contains(bean) ? connection().find(BeanClass.getDefiningClass(bean), getId(bean)) : null);
}
/** {@inheritDoc} */
@Override
public Collection findByExample(T exampleBean, boolean caseInsensitive, Class... lazyRelations) {
return findByExample(exampleBean, caseInsensitive, false, new BeanFindParameters<>(null, lazyRelations));
}
@Override
public Collection findByExampleLike(T exampleBean,
boolean caseInsensitive,
int startIndex,
int maxResult,
Class... lazyRelations) {
return findByExampleLike(exampleBean, caseInsensitive, new BeanFindParameters((Class)null, startIndex, maxResult, lazyRelations));
}
/** {@inheritDoc} */
@Override
public Collection findByExampleLike(T exampleBean,
boolean caseInsensitive,
BeanFindParameters findPars) {
return findByExample(exampleBean, caseInsensitive, true, findPars);
}
/**
* {@inheritDoc}
*/
@Override
public T findByExample(T exampleBean, Class... lazyRelations) {
Collection collection = findByExample(exampleBean, false);
if (collection.size() > 1) {
throw new ManagedException("tsl2nano.multiple.items", new Object[] { exampleBean });
}
collection = fillTree(collection, lazyRelations);
return collection.size() > 0 ? collection.iterator().next() : null;
}
/** {@inheritDoc} */
public Collection findByExample(T exampleBean,
boolean caseInsensitive,
boolean useLike,
BeanFindParameters p) {
checkContextSecurity();
StringBuffer qStr = new StringBuffer();
final Collection> parameter = createExampleStatement(qStr, exampleBean, useLike, caseInsensitive);
qStr.append(addOrderBy(p.getOrderBy()));
return (Collection) findByQuery(qStr.toString(), false, p.getStartIndex(), p.getMaxResult(), parameter.toArray(), p.getHints(),
p.getLazyRelations());
}
/** {@inheritDoc} */
@Override
public Collection findBetween(T firstBean, T secondBean, Class... lazyRelations) {
return findBetween(firstBean, secondBean, true, new BeanFindParameters(null, lazyRelations));
}
@Override
public Collection findBetween(T firstBean,
T secondBean,
boolean caseInsensitive,
int startIndex,
int maxResult,
Class... lazyRelations) {
return findBetween(firstBean, secondBean, caseInsensitive, new BeanFindParameters<>((Class)null, startIndex, maxResult, lazyRelations));
}
/** {@inheritDoc} */
@Override
public Collection findBetween(T firstBean,
T secondBean,
boolean caseInsensitive,
BeanFindParameters p) {
checkContextSecurity();
final Class beanType = (Class) (firstBean != null ? firstBean.getClass()
: secondBean != null ? secondBean.getClass() : null);
if (isVirtualEntity(beanType)) {
if (isNamedQuery(beanType)) {
return findByNamedQuery(beanType, getNamedQueryByArguments(beanType), p.getMaxResult());
}
}
StringBuffer qStr = new StringBuffer();
Collection> parameter = createBetweenStatement(qStr, firstBean, secondBean, caseInsensitive);
qStr.append(addOrderBy(p.getOrderBy()));
return (Collection) findByQuery(qStr.toString(),
false,
p.getStartIndex(),
p.getMaxResult(),
parameter.toArray(),
null,
p.getLazyRelations());
}
/** {@inheritDoc} */
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public int executeQuery(String queryString, boolean nativeQuery, Object[] args) {
Query query = createQuery(queryString, nativeQuery, 0, -1, null, args);
return query.executeUpdate();
}
/** {@inheritDoc} */
@Override
public Collection> findByQuery(String queryString, boolean nativeQuery, Object[] args, Class... lazyRelations) {
return findByQuery(queryString, nativeQuery, 0, -1, args, null, lazyRelations);
}
/** {@inheritDoc} */
@Override
public Collection> findByQuery(String queryString,
boolean nativeQuery,
int startIndex,
int maxResult,
Object[] args,
Map hints,
Class... lazyRelations) {
Query query = createQuery(queryString, nativeQuery, startIndex, maxResult, hints, args);
return fillTree(query.getResultList(), lazyRelations);
}
/**
* createQuery
*
* @param queryString
* @param nativeQuery
* @param startIndex
* @param maxResult
* @param hints
* @return
*/
protected Query createQuery(String queryString,
boolean nativeQuery,
int startIndex,
int maxResult,
Map hints,
Object... args) {
checkContextSecurity();
LOG.debug(queryString);
Query query;
if (nativeQuery) {
query = connection().createNativeQuery(queryString);
} else {
query = connection().createQuery(queryString);
}
query = query.setFirstResult(startIndex != -1 ? startIndex : 0);
query = query.setMaxResults(maxResult != -1 ? maxResult : getMaxResult());
query = ServiceUtil.setHints(query, hints);
if (args != null && args.length > 0) {
boolean mappedParameter = false;
if (args[0] instanceof Map) { //<- Linkedhashmap to be ordered. TODO: is that the right place?
if (ServiceUtil.useNamedParameters(queryString)) {
mappedParameter = true;
final Query fquery = query;
((Map)args[0]).forEach( (k, v) -> fquery.setParameter(k, v));
} else {
args = ((Map)args[0]).values().toArray();
}
}
if (!mappedParameter) {
if (ServiceUtil.useNamedParameters(queryString)) {
query = ServiceUtil.setNamedParameters(query, args);
} else {
query = ServiceUtil.setParameters(query, args);
}
}
}
logTrace(query);
return query;
}
/**
* {@inheritDoc}
*/
@Override
public Object findValueByQuery(String queryString, boolean nativeQuery, Object... args) {
Query query = createQuery(queryString, nativeQuery, 0, -1, null, args);
return query.getSingleResult();
}
/**
* {@inheritDoc}
*/
@Override
public Object findItemByQuery(String queryString, boolean nativeQuery, Object[] args, Class... lazyRelations) {
Collection> collection = findByQuery(queryString, nativeQuery, args, lazyRelations);
if (collection.size() > 1) {
throw new ManagedException("tsl2nano.multiple.items", new Object[] { StringUtil.fixString(queryString,
25,
' ',
true) });
}
collection = fillTree(collection, lazyRelations);
return collection.size() > 0 ? collection.iterator().next() : null;
}
/** {@inheritDoc} */
@Override
public Collection> findByQuery(String queryString,
boolean nativeQuery,
Map args,
Class... lazyRelations) {
Query query = createQuery(queryString, nativeQuery, 0, -1, null, args);
return fillTree(query.getResultList(), lazyRelations);
}
/**
* {@inheritDoc}
*/
@Override
public , T> java.util.Collection find(FINDER... finder) {
LinkedList parameter = new LinkedList();
LinkedList> lazyRelations = new LinkedList>();
String qStr = Finder.createQuery(parameter, lazyRelations, finder);
return (Collection) findByQuery(qStr,
false,
0,
-1,
parameter.toArray(),
null,
lazyRelations.toArray(new Class[0]));
}
/**
* {@inheritDoc}
*/
@Override
public Part[] findBatch(Part... batchParts) {
for (int i = 0; i < batchParts.length; i++) {
batchParts[i].setResult(find(batchParts[i].getFinders()));
}
return batchParts;
}
/**
* {@inheritDoc}
*/
@Override
public T getUser(Subject subject, Class userEntity, String userIdAttribute) {
final Set principals = subject.getPrincipals(UserPrincipal.class);
final UserPrincipal userPrincipal = principals.iterator().next();
T transUser = null;
try {
transUser = userEntity.newInstance();
} catch (final Exception e) {
ManagedException.forward(e);
}
BeanAttribute.getBeanAttribute(userEntity, userIdAttribute).setValue(transUser, userPrincipal.getName());
return findByExample(transUser);
}
}