package com.idega.block.process.business;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import javax.ejb.FinderException;
import javax.faces.component.UIComponent;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import com.idega.block.process.IWBundleStarter;
import com.idega.block.process.data.Case;
import com.idega.block.process.data.CaseCode;
import com.idega.block.process.data.CaseHome;
import com.idega.block.process.data.CaseStatus;
import com.idega.block.process.presentation.UserCases;
import com.idega.block.process.presentation.beans.CasePresentation;
import com.idega.block.process.presentation.beans.CasesSearchCriteriaBean;
import com.idega.business.IBOLookup;
import com.idega.business.IBOLookupException;
import com.idega.business.IBORuntimeException;
import com.idega.core.business.DefaultSpringBean;
import com.idega.core.cache.IWCacheManager2;
import com.idega.data.IDOLookup;
import com.idega.idegaweb.IWMainApplication;
import com.idega.presentation.IWContext;
import com.idega.presentation.paging.PagedDataCollection;
import com.idega.presentation.text.Link;
import com.idega.user.data.Group;
import com.idega.user.data.User;
import com.idega.util.ListUtil;
import com.idega.util.StringUtil;
import com.idega.util.datastructures.map.MapUtil;
/**
* Default implementation of CaseManager.
*
* @author donatas
*
*/
@Scope(BeanDefinition.SCOPE_SINGLETON)
@Service(CasesRetrievalManagerImpl.beanIdentifier)
public class CasesRetrievalManagerImpl extends DefaultSpringBean implements CasesRetrievalManager {
private ReentrantLock lock = new ReentrantLock();
public static final String beanIdentifier = "defaultCaseHandler";
public static final String caseHandlerType = "CasesDefault";
protected static final String CASES_LIST_IDS_CACHE = "casesListIdsCache";
@Override
public String getBeanIdentifier() {
return beanIdentifier;
}
@Override
public String getType() {
return caseHandlerType;
}
@Override
public List<Long> getAllCaseProcessDefinitions() {
return new ArrayList<Long>();
}
@Override
public Map<Long, String> getAllCaseProcessDefinitionsWithName() {
throw new UnsupportedOperationException("Not implemented");
}
public List<Link> getCaseLinks(Case theCase, String componentType) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
@SuppressWarnings("unchecked")
public PagedDataCollection<CasePresentation> getCases(User user, String type, Locale locale, List<String> caseCodes, List<String> caseStatusesToHide,
List<String> caseStatusesToShow, int startIndex, int count, boolean onlySubscribedCases, boolean showAllCases) {
CaseBusiness caseBusiness = getCaseBusiness();
try {
CaseCode[] codes = caseBusiness.getCaseCodesForUserCasesList();
Collection<Case> cases = caseBusiness.getAllCasesForUserExceptCodes(user, codes, startIndex, count);
Collection<Case> casesToShow = null;
if (onlySubscribedCases) {
casesToShow = new ArrayList<Case>();
for (Case theCase : cases) {
Collection<User> subscribers = theCase.getSubscribers();
if (!ListUtil.isEmpty(subscribers) && subscribers.contains(user))
casesToShow.add(theCase);
}
} else {
casesToShow = cases;
}
int caseCount = caseBusiness.getNumberOfCasesForUserExceptCodes(user, codes);
return new PagedDataCollection<CasePresentation>(convertToPresentationBeans(casesToShow, locale), caseCount);
} catch (RemoteException e) {
e.printStackTrace();
} catch (FinderException e) {
e.printStackTrace();
}
return new PagedDataCollection<CasePresentation>(new ArrayList<CasePresentation>());
}
@Override
public List<Integer> getCaseIds(User user, String type, List<String> caseCodes, List<String> statusesToHide, List<String> statusesToShow,
boolean onlySubscribedCases, boolean showAllCases) throws Exception {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public List<Integer> getCasePrimaryKeys(User user, String type, List<String> caseCodes, List<String> statusesToHide, List<String> statusesToShow,
boolean onlySubscribedCases, boolean showAllCases, List<Long> procInstIds, Collection<Long> handlerCategoryIDs) throws Exception {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public Long getLatestProcessDefinitionIdByProcessName(String name) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public Long getProcessDefinitionId(Case theCase) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public String getProcessDefinitionName(Case theCase) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public String getProcessIdentifier(Case theCase) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public Long getProcessInstanceId(Case theCase) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public String getProcessName(String processName, Locale locale) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public UIComponent getView(IWContext iwc, Integer caseId, String caseProcessorType, String caseManagerType) {
return null;
}
@Override
public PagedDataCollection<CasePresentation> getCasesByIds(List<Integer> ids, Locale locale) {
Collection<Case> cases = getCaseBusiness().getCasesByIds(ids);
return getCasesByEntities(cases, locale);
}
@Override
public PagedDataCollection<CasePresentation> getCasesByEntities(Collection<Case> cases, Locale locale) {
return new PagedDataCollection<CasePresentation>(convertToPresentationBeans(cases, locale), cases.size());
}
@Override
public Long getProcessInstanceIdByCaseId(Object id) {
throw new UnsupportedOperationException("Not implemented");
}
/**
* Converts Case entities to presentation beans.
*
* @param cases Collection of Case entities.
* @param locale Current locale.
* @return List of CasePresentation beans.
*/
protected List<CasePresentation> convertToPresentationBeans(Collection<? extends Case> cases, Locale locale) {
if (ListUtil.isEmpty(cases))
return new ArrayList<CasePresentation>(0);
List<CasePresentation> beans = new ArrayList<CasePresentation>(cases.size());
for (Iterator<? extends Case> iterator = cases.iterator(); iterator.hasNext();) {
Case caze = iterator.next();
CasePresentation bean = null;
try {
bean = convertToPresentation(caze, null, locale);
} catch (Exception e) {
getLogger().log(Level.WARNING, "Error while converting case " + caze + " to " + CasePresentation.class, e);
}
if (bean != null)
beans.add(bean);
}
return beans;
}
protected CasePresentation convertToPresentation(Case theCase, CasePresentation bean, Locale locale) {
if (bean == null)
bean = new CasePresentation();
CaseCode code = theCase.getCaseCode();
CaseBusiness business;
try {
business = CaseCodeManager.getInstance().getCaseBusinessOrDefault(code, IWMainApplication.getDefaultIWApplicationContext());
} catch (IBOLookupException ile) {
business = getCaseBusiness();
}
bean.setPrimaryKey(theCase.getPrimaryKey() instanceof Integer ?
(Integer) theCase.getPrimaryKey() :
Integer.valueOf(theCase.getPrimaryKey().toString()));
bean.setId(theCase.getId());
bean.setUrl(theCase.getUrl());
bean.setCaseManagerType(theCase.getCaseManagerType());
bean.setOwner(theCase.getOwner());
bean.setExternalId(theCase.getExternalId());
bean.setCaseIdentifier(theCase.getCaseIdentifier());
try {
bean.setSubject(business.getCaseSubject(theCase, locale));
} catch (RemoteException e) {
e.printStackTrace();
}
try {
bean.setCaseStatus(getCaseBusiness().getCaseStatus(theCase.getStatus()));
} catch (Exception e) {
bean.setCaseStatus(theCase.getCaseStatus());
}
if (bean.getCaseStatus() != null) {
bean.setLocalizedStatus(getLocalizedStatus(theCase, bean.getCaseStatus(), business, locale));
}
bean.setCreated(theCase.getCreated());
bean.setCode(theCase.getCode());
return bean;
}
protected String getLocalizedStatus(Case theCase, CaseStatus status, Locale locale) {
return getLocalizedStatus(theCase, status, getCaseBusiness(), locale);
}
protected String getLocalizedStatus(Case theCase, CaseStatus status, CaseBusiness business, Locale locale) {
String statusKey = status.getStatus();
String localization = null;
try {
localization = business.getLocalizedCaseStatusDescription(theCase, status, locale);
} catch (RemoteException e) {
e.printStackTrace();
}
if (StringUtil.isEmpty(localization) || localization.equals(statusKey)) {
try {
localization = business.getLocalizedCaseStatusDescription(theCase, status, locale, "is.idega.idegaweb.egov.cases");
} catch (RemoteException e) {
e.printStackTrace();
}
}
if (StringUtil.isEmpty(localization) || localization.equals(statusKey)) {
try {
localization = business.getLocalizedCaseStatusDescription(theCase, status, locale, IWBundleStarter.IW_BUNDLE_IDENTIFIER);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return StringUtil.isEmpty(localization) ? statusKey : localization;
}
@Override
public PagedDataCollection<CasePresentation> getClosedCases(Collection<Group> groups) {
return new PagedDataCollection<CasePresentation>(new ArrayList<CasePresentation>());
}
@Override
public PagedDataCollection<CasePresentation> getMyCases(User user) {
return new PagedDataCollection<CasePresentation>(new ArrayList<CasePresentation>());
}
protected CaseBusiness getCaseBusiness() {
try {
return IBOLookup.getServiceInstance(IWMainApplication.getDefaultIWApplicationContext(), CaseBusiness.class);
}
catch (IBOLookupException ile) {
throw new IBORuntimeException(ile);
}
}
@Override
public Long getTaskInstanceIdForTask(Case theCase, String taskName) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public List<Long> getCasesIdsByProcessDefinitionName(String processDefinitionName) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public String resolveCaseId(IWContext iwc) {
if (iwc == null)
return null;
return iwc.getParameter(UserCases.PARAMETER_CASE_PK);
}
@Override
public User getCaseOwner(Object entityId) {
if (entityId == null || entityId instanceof Long)
return null;
try {
CaseHome caseHome = (CaseHome) IDOLookup.getHome(Case.class);
Case theCase = caseHome.findByPrimaryKey(entityId);
return theCase == null ? null : theCase.getOwner();
} catch(FinderException e) {
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Collection<CasePresentation> getReLoadedCases(CasesSearchCriteriaBean criterias) {
throw new UnsupportedOperationException("Not implemented");
}
private int lastUsedCacheSize = 75;
private Map<CasesCacheCriteria, Map<Integer, Boolean>> getCache() {
int cacheSize = Integer.valueOf(getApplication().getSettings().getProperty("cases_cache_size", String.valueOf(75)));
if (cacheSize == lastUsedCacheSize)
return getCache(CASES_LIST_IDS_CACHE, 86400, cacheSize);
IWCacheManager2.getInstance(getApplication()).invalidate(CASES_LIST_IDS_CACHE);
lastUsedCacheSize = cacheSize;
return getCache();
}
/**
*
* <p>Creates new search criteria for cache by:</p>
* @param user
* @param type
* @param caseCodes
* @param statusesToHide is {@link Collection} of {@link Case#getStatus()},
* which should be hidden;
* @param statusesToShow is {@link Collection} of {@link Case#getStatus()},
* which should be shown;
* @param onlySubscribedCases shows only those {@link Case}s where given
* {@link User} is in {@link Case#getSubscribers()};
* @param roles of BPM processes, which {@link User} can access. More info
* in bpm_actors table;
* @param groups
* @param codes
* @param showAllCases
* @param procInstIds is id's of BPM process instances;
* @param handlerCategoryIDs is ID's of groups, which has {@link User}s, who
* is in {@link Case#getSubscribers()} list;
* @return criteria bean for querying cache;
*/
protected CasesCacheCriteria getCacheKey(
User user,
String type,
Collection<String> caseCodes,
Collection<String> statusesToHide,
Collection<String> statusesToShow,
boolean onlySubscribedCases,
Collection<String> roles,
Collection<Integer> groups,
Collection<String> codes,
boolean showAllCases,
Collection<Long> procInstIds,
Collection<Long> handlerCategoryIDs) {
return new CasesCacheCriteria(
user == null ? -1 : Integer.valueOf(user.getId()),
type, caseCodes, statusesToHide, statusesToShow, roles, groups,
codes, procInstIds, handlerCategoryIDs, onlySubscribedCases,
showAllCases);
}
protected void clearCache() throws Exception {
assert !lock.isHeldByCurrentThread();
lock.lock();
try {
getCache().clear();
} catch (Exception e) {
} finally {
lock.unlock();
}
}
protected void removeFromCache(CasesCacheCriteria key) throws Exception {
assert !lock.isHeldByCurrentThread();
lock.lock();
try {
getCache().remove(key);
} catch (Exception e) {
} finally {
lock.unlock();
}
}
protected void removeElementFromCache(CasesCacheCriteria key, Integer id) throws Exception {
assert !lock.isHeldByCurrentThread();
lock.lock();
try {
Map<Integer, Boolean> ids = getCache().get(key);
if (ids != null) {
ids.remove(id);
}
} catch (Exception e) {
} finally {
lock.unlock();
}
}
protected boolean containsElement(CasesCacheCriteria key, Integer id) throws Exception {
assert !lock.isHeldByCurrentThread();
lock.lock();
try {
Map<Integer, Boolean> ids = getCache().get(key);
return !MapUtil.isEmpty(ids) && ids.containsKey(id);
} finally {
lock.unlock();
}
}
protected void addElementToCache(CasesCacheCriteria key, Integer id) throws Exception {
assert !lock.isHeldByCurrentThread();
lock.lock();
try {
Map<CasesCacheCriteria, Map<Integer, Boolean>> cache = getCache();
Map<Integer, Boolean> ids = getCache().get(key);
if (ids == null) {
ids = new LinkedHashMap<Integer, Boolean>();
cache.put(key, ids);
}
ids.put(id, Boolean.TRUE);
} finally {
lock.unlock();
}
}
protected Collection<CasesCacheCriteria> getCacheKeySet() throws Exception {
assert !lock.isHeldByCurrentThread();
lock.lock();
try {
return new ArrayList<CasesCacheCriteria>(getCache().keySet());
} catch (Exception e) {
} finally {
lock.unlock();
}
return Collections.emptyList();
}
/**
*
* <p>Searches {@link Case#getPrimaryKey()}s in cache {@link Map}.</p>
* @param user
* @param type
* @param caseCodes
* @param caseStatusesToHide is {@link Collection} of {@link Case#getStatus()},
* which should be hidden;
* @param caseStatusesToShow is {@link Collection} of {@link Case#getStatus()},
* which should be shown;
* @param onlySubscribedCases shows only those {@link Case}s where given
* {@link User} is in {@link Case#getSubscribers()};
* @param roles of BPM processes, which {@link User} can access. More info
* in bpm_actors table;
* @param groups
* @param codes
* @param showAllCases
* @param procInstIds is id's of BPM process instances;
* @param handlerCategoryIDs is ID's of groups, which has {@link User}s, who
* is in {@link Case#getSubscribers()} list;
* @return cached {@link Case#getPrimaryKey()}s or {@link Collections#emptyList()}
* on failure;
* @author <a href="mailto:martynas@idega.is">Martynas StakÄ—</a>
*/
protected List<Integer> getCachedIds(
User user,
String type,
Collection<String> caseCodes,
Collection<String> caseStatusesToHide,
Collection<String> caseStatusesToShow,
boolean onlySubscribedCases,
Collection<String> roles,
Collection<Integer> groups,
Collection<String> codes,
boolean showAllCases,
Collection<Long> procInstIds,
Collection<Long> handlerCategoryIDs) {
Map<CasesCacheCriteria, Map<Integer, Boolean>> cache = getCache();
if (cache == null)
return Collections.emptyList();
/* Creating key */
CasesCacheCriteria key = getCacheKey(user, type, caseCodes,
caseStatusesToHide, caseStatusesToShow, onlySubscribedCases,
roles, groups, codes, showAllCases, procInstIds,
handlerCategoryIDs);
/* Querying */
Map<Integer, Boolean> ids = cache.get(key);
if (MapUtil.isEmpty(ids))
return Collections.emptyList();
/* Inverting sort order */
List<Integer> cachedIds = new ArrayList<Integer>(ids.keySet());
Collections.sort(cachedIds, new Comparator<Integer>() {
@Override
public int compare(Integer id1, Integer id2) {
return -1 * (id1.compareTo(id2));
}
});
return cachedIds;
}
/**
*
* <p>Puts {@link Case#getPrimaryKey()}s to {@link Map}</p>
* @param ids is {@link Case#getPrimaryKey()}s to put, not <code>null</code>;
* @param user
* @param type
* @param caseCodes
* @param caseStatusesToHide is {@link Collection} of {@link Case#getStatus()},
* which should be hidden;
* @param caseStatusesToShow is {@link Collection} of {@link Case#getStatus()},
* which should be shown;
* @param onlySubscribedCases shows only those {@link Case}s where given
* {@link User} is in {@link Case#getSubscribers()};
* @param roles of BPM processes, which {@link User} can access. More info
* in bpm_actors table;
* @param groups
* @param codes
* @param showAllCases
* @param procInstIds is id's of BPM process instances;
* @param handlerCategoryIDs is ID's of groups, which has {@link User}s, who
* is in {@link Case#getSubscribers()} list;
* @author <a href="mailto:martynas@idega.is">Martynas StakÄ—</a>
*/
protected void putIdsToCache(
Collection<Integer> ids,
User user,
String type,
Collection<String> caseCodes,
Collection<String> caseStatusesToHide,
Collection<String> caseStatusesToShow,
boolean onlySubscribedCases,
Collection<String> roles,
Collection<Integer> groups,
Collection<String> codes,
boolean showAllCases,
Collection<Long> procInstIds,
Collection<Long> handlerCategoryIDs) {
if (ListUtil.isEmpty(ids))
return;
Map<CasesCacheCriteria, Map<Integer, Boolean>> cache = getCache();
if (cache == null)
return;
/* Creating key */
CasesCacheCriteria key = getCacheKey(user, type, caseCodes,
caseStatusesToHide, caseStatusesToShow, onlySubscribedCases,
roles, groups, codes, showAllCases, procInstIds,
handlerCategoryIDs);
/* Getting id's, that already cached by given criteria */
Map<Integer, Boolean> cachedIds = cache.get(key);
if (cachedIds == null) {
cachedIds = new LinkedHashMap<Integer, Boolean>();
cache.put(key, cachedIds);
}
/* Putting to cache */
for (Integer id: ids)
cachedIds.put(id, Boolean.TRUE);
}
protected List<Integer> getCaseIds(User user, String type, List<String> caseCodes, List<String> caseStatusesToHide, List<String> caseStatusesToShow,
boolean onlySubscribedCases, boolean showAllCases, Integer caseId, List<Long> procInstIds, Collection<Long> handlerCategoryIDs) throws Exception {
throw new UnsupportedOperationException("This method is not implemented");
}
@Override
public CasePresentation getCaseByIdLazily(Integer caseId) {
if (caseId == null)
return null;
Case theCase = null;
try {
theCase = getCaseBusiness().getCase(caseId);
} catch (RemoteException e) {
e.printStackTrace();
} catch (FinderException e) {
}
if (theCase == null)
return null;
CasePresentation casePresentation = new CasePresentation(theCase);
casePresentation.setLocalizedStatus(getLocalizedStatus(theCase, theCase.getCaseStatus(), getCurrentLocale()));
return casePresentation;
}
}