001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/profile/tags/sakai_2-4-1/common-composite-component/src/java/org/sakaiproject/component/common/edu/person/SakaiPersonManagerImpl.java $
003: * $Id: SakaiPersonManagerImpl.java 20966 2007-02-02 19:14:47Z jholtzman@berkeley.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.component.common.edu.person;
021:
022: import java.sql.SQLException;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029: import java.util.Set;
030:
031: import org.hibernate.Criteria;
032: import org.hibernate.Hibernate;
033: import org.hibernate.HibernateException;
034: import org.hibernate.Query;
035: import org.hibernate.Session;
036: import org.hibernate.criterion.Example;
037: import org.hibernate.criterion.Expression;
038: import org.hibernate.criterion.Order;
039:
040: import org.apache.commons.logging.Log;
041: import org.apache.commons.logging.LogFactory;
042: import org.sakaiproject.api.common.edu.person.SakaiPerson;
043: import org.sakaiproject.api.common.edu.person.SakaiPersonManager;
044: import org.sakaiproject.api.common.type.Type;
045: import org.sakaiproject.api.common.type.TypeManager;
046: import org.sakaiproject.authz.cover.SecurityService;
047: import org.sakaiproject.component.common.manager.PersistableHelper;
048: import org.sakaiproject.id.cover.IdManager;
049: import org.sakaiproject.tool.cover.SessionManager;
050: import org.sakaiproject.user.api.User;
051: import org.sakaiproject.user.api.UserNotDefinedException;
052: import org.sakaiproject.user.cover.UserDirectoryService;
053: import org.springframework.orm.hibernate3.HibernateCallback;
054: import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
055:
056: /**
057: * @author <a href="mailto:lance@indiana.edu">Lance Speelmon</a>
058: */
059: public class SakaiPersonManagerImpl extends HibernateDaoSupport
060: implements SakaiPersonManager {
061: private static final Log LOG = LogFactory
062: .getLog(SakaiPersonManagerImpl.class);
063:
064: private static final String PERCENT_SIGN = "%";
065:
066: private static final String SURNAME = "surname";
067:
068: private static final String GIVENNAME = "givenName";
069:
070: private static final String UID = "uid";
071:
072: private static final String TYPE_UUID = "typeUuid";
073:
074: private static final String AGENT_UUID = "agentUuid";
075:
076: private static final String AGENT_UUID_COLLECTION = "agentUuidCollection";
077:
078: private static final String FERPA_ENABLED = "ferpaEnabled";
079:
080: private static final String HQL_FIND_SAKAI_PERSON_BY_AGENT_AND_TYPE = "findEduPersonByAgentAndType";
081:
082: private static final String HQL_FIND_SAKAI_PERSONS_BY_AGENTS_AND_TYPE = "findEduPersonsByAgentsAndType";
083:
084: private static final String HQL_FIND_SAKAI_PERSON_BY_UID = "findSakaiPersonByUid";
085:
086: private static final int MAX_QUERY_COLLECTION_SIZE = 1000;
087:
088: private TypeManager typeManager; // dep inj
089:
090: private PersistableHelper persistableHelper; // dep inj
091:
092: // SakaiPerson record types
093: private Type systemMutableType; // oba constant
094:
095: private Type userMutableType; // oba constant
096:
097: // hibernate cannot cache BLOB data types - rshastri
098: // private boolean cacheFindSakaiPersonString = true;
099: // private boolean cacheFindSakaiPersonStringType = true;
100: // private boolean cacheFindSakaiPersonSakaiPerson = true;
101: // private boolean cacheFindSakaiPersonByUid = true;
102:
103: private static final String[] SYSTEM_MUTBALE_PRIMITIVES = {
104: "org.sakaiproject", "api.common.edu.person",
105: "SakaiPerson.recordType.systemMutable",
106: "System Mutable SakaiPerson", "System Mutable SakaiPerson", };
107:
108: private static final String[] USER_MUTBALE_PRIMITIVES = {
109: "org.sakaiproject", "api.common.edu.person",
110: "SakaiPerson.recordType.userMutable",
111: "User Mutable SakaiPerson", "User Mutable SakaiPerson", };
112:
113: public void init() {
114: LOG.debug("init()");
115:
116: LOG.debug("// init systemMutableType");
117: systemMutableType = typeManager.getType(
118: SYSTEM_MUTBALE_PRIMITIVES[0],
119: SYSTEM_MUTBALE_PRIMITIVES[1],
120: SYSTEM_MUTBALE_PRIMITIVES[2]);
121: if (systemMutableType == null) {
122: systemMutableType = typeManager.createType(
123: SYSTEM_MUTBALE_PRIMITIVES[0],
124: SYSTEM_MUTBALE_PRIMITIVES[1],
125: SYSTEM_MUTBALE_PRIMITIVES[2],
126: SYSTEM_MUTBALE_PRIMITIVES[3],
127: SYSTEM_MUTBALE_PRIMITIVES[4]);
128: }
129: if (systemMutableType == null)
130: throw new IllegalStateException("systemMutableType == null");
131:
132: LOG.debug("// init userMutableType");
133: userMutableType = typeManager.getType(
134: USER_MUTBALE_PRIMITIVES[0], USER_MUTBALE_PRIMITIVES[1],
135: USER_MUTBALE_PRIMITIVES[2]);
136: if (userMutableType == null) {
137: userMutableType = typeManager.createType(
138: USER_MUTBALE_PRIMITIVES[0],
139: USER_MUTBALE_PRIMITIVES[1],
140: USER_MUTBALE_PRIMITIVES[2],
141: USER_MUTBALE_PRIMITIVES[3],
142: USER_MUTBALE_PRIMITIVES[4]);
143: }
144: if (userMutableType == null)
145: throw new IllegalStateException("userMutableType == null");
146:
147: LOG.debug("init() has completed successfully");
148: }
149:
150: /**
151: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#create(java.lang.String, java.lang.String, org.sakaiproject.api.common.type.Type)
152: */
153: public SakaiPerson create(String userId, Type recordType) {
154: if (LOG.isDebugEnabled()) {
155: LOG.debug("create(String " + userId + ", Type "
156: + recordType + ")");
157: }
158: if (userId == null || userId.length() < 1)
159: throw new IllegalArgumentException(
160: "Illegal agentUuid argument passed!");
161: ; // a null uid is valid
162: if (!isSupportedType(recordType))
163: throw new IllegalArgumentException(
164: "Illegal recordType argument passed!");
165:
166: SakaiPersonImpl spi = new SakaiPersonImpl();
167: persistableHelper.createPersistableFields(spi);
168: spi.setUuid(IdManager.createUuid());
169: spi.setAgentUuid(userId);
170: spi.setUid(userId);
171: spi.setTypeUuid(recordType.getUuid());
172: this .getHibernateTemplate().save(spi);
173:
174: LOG.debug("return spi;");
175: return spi;
176: }
177:
178: /**
179: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#getSakaiPerson(org.sakaiproject.api.common.type.Type)
180: */
181: public SakaiPerson getSakaiPerson(Type recordType) {
182: if (LOG.isDebugEnabled()) {
183: LOG.debug("getSakaiPerson(Type " + recordType + ")");
184: }
185: ; // no validation required; method is delegated.
186:
187: LOG
188: .debug("return findSakaiPerson(agent.getUuid(), recordType);");
189: return getSakaiPerson(SessionManager.getCurrentSessionUserId(),
190: recordType);
191: }
192:
193: /**
194: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#getPrototype()
195: */
196: public SakaiPerson getPrototype() {
197: LOG.debug("getPrototype()");
198:
199: return new SakaiPersonImpl();
200: }
201:
202: /**
203: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#findSakaiPersonByUid(java.lang.String)
204: */
205: public List findSakaiPersonByUid(final String uid) {
206: if (LOG.isDebugEnabled()) {
207: LOG.debug("findSakaiPersonByUid(String " + uid + ")");
208: }
209: if (uid == null || uid.length() < 1)
210: throw new IllegalArgumentException(
211: "Illegal uid argument passed!");
212:
213: final HibernateCallback hcb = new HibernateCallback() {
214: public Object doInHibernate(Session session)
215: throws HibernateException, SQLException {
216: final Query q = session
217: .getNamedQuery(HQL_FIND_SAKAI_PERSON_BY_UID);
218: q.setParameter(UID, uid, Hibernate.STRING);
219: // q.setCacheable(cacheFindSakaiPersonByUid);
220: return q.list();
221: }
222: };
223:
224: LOG.debug("return getHibernateTemplate().executeFind(hcb);");
225: return getHibernateTemplate().executeFind(hcb);
226: }
227:
228: /**
229: * @see SakaiPersonManager#save(SakaiPerson)
230: */
231: public void save(SakaiPerson sakaiPerson) {
232: if (LOG.isDebugEnabled()) {
233: LOG.debug("save(SakaiPerson " + sakaiPerson + ")");
234: }
235: if (sakaiPerson == null)
236: throw new IllegalArgumentException(
237: "Illegal sakaiPerson argument passed!");
238: if (!isSupportedType(sakaiPerson.getTypeUuid()))
239: throw new IllegalArgumentException(
240: "The sakaiPerson argument contains an invalid Type!");
241:
242: // AuthZ
243: // Only superusers can update system records
244: if (getSystemMutableType().getUuid().equals(
245: sakaiPerson.getTypeUuid())
246: && !SecurityService.isSuperUser()) {
247: throw new IllegalAccessError(
248: "System mutable records cannot be updated.");
249: }
250:
251: // if it is a user mutable record, enusre the user is updating their own record
252: if (getUserMutableType().getUuid().equals(
253: sakaiPerson.getTypeUuid())) {
254: // AuthZ - Ensure the current user is updating their own record
255: if (!SessionManager.getCurrentSessionUserId().equals(
256: sakaiPerson.getAgentUuid()))
257: throw new IllegalAccessError(
258: "You do not have permissions to update this record!");
259: }
260:
261: // store record
262: if (!(sakaiPerson instanceof SakaiPersonImpl)) {
263: // TODO support alternate implementations of SakaiPerson
264: // copy bean properties into new SakaiPersonImpl with beanutils?
265: throw new UnsupportedOperationException(
266: "Unknown SakaiPerson implementation found!");
267: } else {
268: // update lastModifiedDate
269: SakaiPersonImpl spi = (SakaiPersonImpl) sakaiPerson;
270: persistableHelper.modifyPersistableFields(spi);
271: // use update(..) method to ensure someone does not try to insert a
272: // prototype.
273: getHibernateTemplate().update(spi);
274: }
275: }
276:
277: /**
278: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#findSakaiPerson(java.lang.String, org.sakaiproject.api.common.type.Type)
279: */
280: public SakaiPerson getSakaiPerson(final String agentUuid,
281: final Type recordType) {
282: if (LOG.isDebugEnabled()) {
283: LOG.debug("getSakaiPerson(String " + agentUuid + ", Type "
284: + recordType + ")");
285: }
286: if (agentUuid == null || agentUuid.length() < 1)
287: throw new IllegalArgumentException(
288: "Illegal agentUuid argument passed!");
289: if (recordType == null || !isSupportedType(recordType))
290: throw new IllegalArgumentException(
291: "Illegal recordType argument passed!");
292:
293: final HibernateCallback hcb = new HibernateCallback() {
294: public Object doInHibernate(Session session)
295: throws HibernateException, SQLException {
296: Query q = session
297: .getNamedQuery(HQL_FIND_SAKAI_PERSON_BY_AGENT_AND_TYPE);
298: q.setParameter(AGENT_UUID, agentUuid, Hibernate.STRING);
299: q.setParameter(TYPE_UUID, recordType.getUuid(),
300: Hibernate.STRING);
301: // q.setCacheable(false);
302: return q.uniqueResult();
303: }
304: };
305:
306: LOG
307: .debug("return (SakaiPerson) getHibernateTemplate().execute(hcb);");
308: return (SakaiPerson) getHibernateTemplate().execute(hcb);
309: }
310:
311: public Map<String, SakaiPerson> getSakaiPersons(
312: final Set<String> userIds, final Type recordType) {
313: if (LOG.isDebugEnabled()) {
314: LOG.debug("getSakaiPersons(Collection size "
315: + userIds.size() + ", Type " + recordType + ")");
316: }
317: if (userIds == null || userIds.size() == 0)
318: throw new IllegalArgumentException(
319: "Illegal agentUuid argument passed!");
320: if (recordType == null || !isSupportedType(recordType))
321: throw new IllegalArgumentException(
322: "Illegal recordType argument passed!");
323:
324: int collectionSize = userIds.size();
325:
326: // Keep an ordered list of userIds
327: List<String> userIdList = new ArrayList<String>(userIds);
328:
329: // The map we're return
330: Map<String, SakaiPerson> map = new HashMap<String, SakaiPerson>(
331: collectionSize);
332:
333: // Oracle (maybe others, too) can only take up to 1000 parameters total, so chunk the list if necessary
334: if (collectionSize > MAX_QUERY_COLLECTION_SIZE) {
335: int offset = 0;
336: List<String> userListChunk = new ArrayList<String>();
337: while (offset < collectionSize) {
338: if (offset > 0
339: && offset % MAX_QUERY_COLLECTION_SIZE == 0) {
340: // Our chunk is full, so process the list, clear it, and continue
341: List<SakaiPerson> personListChunk = listSakaiPersons(
342: userListChunk, recordType);
343: addSakaiPersonsToMap(personListChunk, map);
344: userListChunk.clear();
345: }
346: userListChunk.add(userIdList.get(offset));
347: offset++;
348: }
349: // We may (and probably do) have remaining users that haven't been queried
350: if (!userListChunk.isEmpty()) {
351: List<SakaiPerson> lastChunk = listSakaiPersons(
352: userListChunk, recordType);
353: addSakaiPersonsToMap(lastChunk, map);
354: }
355: } else {
356: addSakaiPersonsToMap(listSakaiPersons(userIds, recordType),
357: map);
358: }
359: return map;
360: }
361:
362: private void addSakaiPersonsToMap(List<SakaiPerson> sakaiPersons,
363: Map<String, SakaiPerson> map) {
364: for (Iterator<SakaiPerson> iter = sakaiPersons.iterator(); iter
365: .hasNext();) {
366: SakaiPerson person = iter.next();
367: map.put(person.getAgentUuid(), person);
368: }
369: }
370:
371: private List<SakaiPerson> listSakaiPersons(
372: final Collection<String> userIds, final Type recordType) {
373: final HibernateCallback hcb = new HibernateCallback() {
374: public Object doInHibernate(Session session)
375: throws HibernateException, SQLException {
376: Query q = session
377: .getNamedQuery(HQL_FIND_SAKAI_PERSONS_BY_AGENTS_AND_TYPE);
378: q.setParameterList(AGENT_UUID_COLLECTION, userIds);
379: q.setParameter(TYPE_UUID, recordType.getUuid(),
380: Hibernate.STRING);
381: // q.setCacheable(false);
382: return q.list();
383: }
384: };
385: return getHibernateTemplate().executeFind(hcb);
386: }
387:
388: /**
389: * @see SakaiPersonManager#findSakaiPerson(String)
390: */
391: public List findSakaiPerson(final String simpleSearchCriteria) {
392: if (LOG.isDebugEnabled()) {
393: LOG.debug("findSakaiPerson(String " + simpleSearchCriteria
394: + ")");
395: }
396: if (simpleSearchCriteria == null
397: || simpleSearchCriteria.length() < 1)
398: throw new IllegalArgumentException(
399: "Illegal simpleSearchCriteria argument passed!");
400:
401: final String match = PERCENT_SIGN + simpleSearchCriteria
402: + PERCENT_SIGN;
403: final HibernateCallback hcb = new HibernateCallback() {
404: public Object doInHibernate(Session session)
405: throws HibernateException, SQLException {
406: final Criteria c = session
407: .createCriteria(SakaiPersonImpl.class);
408: c.add(Expression.disjunction().add(
409: Expression.ilike(UID, match)).add(
410: Expression.ilike(GIVENNAME, match)).add(
411: Expression.ilike(SURNAME, match)));
412: c.addOrder(Order.asc(SURNAME));
413: // c.setCacheable(cacheFindSakaiPersonString);
414: return c.list();
415: }
416: };
417:
418: LOG.debug("return getHibernateTemplate().executeFind(hcb);");
419: return getHibernateTemplate().executeFind(hcb);
420: }
421:
422: /**
423: * @param typeManager
424: * The typeManager to set.
425: */
426: public void setTypeManager(TypeManager typeManager) {
427: if (LOG.isDebugEnabled()) {
428: LOG
429: .debug("setTypeManager(TypeManager " + typeManager
430: + ")");
431: }
432:
433: this .typeManager = typeManager;
434: }
435:
436: /**
437: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#getUserMutableType()
438: */
439: public Type getUserMutableType() {
440: LOG.debug("getUserMutableType()");
441:
442: return userMutableType;
443: }
444:
445: /**
446: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#getSystemMutableType()
447: */
448: public Type getSystemMutableType() {
449: LOG.debug("getSystemMutableType()");
450:
451: return systemMutableType;
452: }
453:
454: /**
455: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#findSakaiPerson(org.sakaiproject.api.common.edu.person.SakaiPerson)
456: */
457: public List findSakaiPerson(final SakaiPerson queryByExample) {
458: if (LOG.isDebugEnabled()) {
459: LOG.debug("findSakaiPerson(SakaiPerson " + queryByExample
460: + ")");
461: }
462: if (queryByExample == null)
463: throw new IllegalArgumentException(
464: "Illegal queryByExample argument passed!");
465:
466: final HibernateCallback hcb = new HibernateCallback() {
467: public Object doInHibernate(Session session)
468: throws HibernateException, SQLException {
469: Criteria criteria = session
470: .createCriteria(queryByExample.getClass());
471: criteria.add(Example.create(queryByExample));
472: // criteria.setCacheable(cacheFindSakaiPersonSakaiPerson);
473: return criteria.list();
474: }
475: };
476:
477: LOG.debug("return getHibernateTemplate().executeFind(hcb);");
478: return getHibernateTemplate().executeFind(hcb);
479: }
480:
481: /**
482: * @see org.sakaiproject.api.common.edu.person.SakaiPersonManager#delete(org.sakaiproject.api.common.edu.person.SakaiPerson)
483: */
484: public void delete(final SakaiPerson sakaiPerson) {
485: if (LOG.isDebugEnabled()) {
486: LOG.debug("delete(SakaiPerson " + sakaiPerson + ")");
487: }
488: if (sakaiPerson == null)
489: throw new IllegalArgumentException(
490: "Illegal sakaiPerson argument passed!");
491:
492: LOG.debug("getHibernateTemplate().delete(sakaiPerson);");
493: getHibernateTemplate().delete(sakaiPerson);
494: }
495:
496: private boolean isSupportedType(Type recordType) {
497: if (LOG.isDebugEnabled()) {
498: LOG.debug("isSupportedType(Type " + recordType + ")");
499: }
500:
501: if (recordType == null)
502: return false;
503: if (this .getUserMutableType().equals(recordType))
504: return true;
505: if (this .getSystemMutableType().equals(recordType))
506: return true;
507: return false;
508: }
509:
510: private boolean isSupportedType(String typeUuid) {
511: if (LOG.isDebugEnabled()) {
512: LOG.debug("isSupportedType(String " + typeUuid + ")");
513: }
514:
515: if (typeUuid == null)
516: return false;
517: if (this .getUserMutableType().getUuid().equals(typeUuid))
518: return true;
519: if (this .getSystemMutableType().getUuid().equals(typeUuid))
520: return true;
521: return false;
522: }
523:
524: /**
525: * @param cacheFindSakaiPersonStringType
526: * The cacheFindSakaiPersonStringType to set.
527: */
528: // public void setCacheFindSakaiPersonStringType(
529: // boolean cacheFindSakaiPersonStringType)
530: // {
531: // this.cacheFindSakaiPersonStringType = cacheFindSakaiPersonStringType;
532: // }
533: /**
534: * @param cacheFindSakaiPersonString
535: * The cacheFindSakaiPersonString to set.
536: */
537: // public void setCacheFindSakaiPersonString(boolean cacheFindSakaiPersonString)
538: // {
539: // this.cacheFindSakaiPersonString = cacheFindSakaiPersonString;
540: // }
541: /**
542: * @param cacheFindSakaiPersonSakaiPerson
543: * The cacheFindSakaiPersonSakaiPerson to set.
544: */
545: // public void setCacheFindSakaiPersonSakaiPerson(
546: // boolean cacheFindSakaiPersonSakaiPerson)
547: // {
548: // this.cacheFindSakaiPersonSakaiPerson = cacheFindSakaiPersonSakaiPerson;
549: // }
550: /**
551: * @param cacheFindSakaiPersonByUid
552: * The cacheFindSakaiPersonByUid to set.
553: */
554: // public void setCacheFindSakaiPersonByUid(boolean cacheFindSakaiPersonByUid)
555: // {
556: // this.cacheFindSakaiPersonByUid = cacheFindSakaiPersonByUid;
557: // }
558: /**
559: * @param persistableHelper
560: * The persistableHelper to set.
561: */
562: public void setPersistableHelper(PersistableHelper persistableHelper) {
563: this .persistableHelper = persistableHelper;
564: }
565:
566: public List isFerpaEnabled(final Collection agentUuids) {
567: if (LOG.isDebugEnabled()) {
568: LOG.debug("isFerpaEnabled(Set " + agentUuids + ")");
569: }
570: if (agentUuids == null || agentUuids.isEmpty()) {
571: throw new IllegalArgumentException(
572: "Illegal Set agentUuids argument!");
573: }
574:
575: final HibernateCallback hcb = new HibernateCallback() {
576: public Object doInHibernate(Session session)
577: throws HibernateException, SQLException {
578: final Criteria c = session
579: .createCriteria(SakaiPersonImpl.class);
580: c.add(Expression.in(AGENT_UUID, agentUuids));
581: c.add(Expression.eq(FERPA_ENABLED, Boolean.TRUE));
582: return c.list();
583: }
584: };
585: return getHibernateTemplate().executeFind(hcb);
586: }
587:
588: public List findAllFerpaEnabled() {
589: LOG.debug("findAllFerpaEnabled()");
590:
591: final HibernateCallback hcb = new HibernateCallback() {
592: public Object doInHibernate(Session session)
593: throws HibernateException, SQLException {
594: final Criteria c = session
595: .createCriteria(SakaiPersonImpl.class);
596: c.add(Expression.eq(FERPA_ENABLED, Boolean.TRUE));
597: return c.list();
598: }
599: };
600: return getHibernateTemplate().executeFind(hcb);
601: }
602:
603: }
|