001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/user/tags/sakai_2-4-1/user-impl/impl/src/java/org/sakaiproject/user/impl/DbUserService.java $
003: * $Id: DbUserService.java 11180 2006-06-26 02:45:23Z ggolden@umich.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.user.impl;
021:
022: import java.sql.ResultSet;
023: import java.sql.SQLException;
024: import java.util.Collection;
025: import java.util.List;
026: import java.util.Vector;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.sakaiproject.db.api.SqlReader;
031: import org.sakaiproject.db.api.SqlService;
032: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
033: import org.sakaiproject.time.api.Time;
034: import org.sakaiproject.user.api.UserEdit;
035: import org.sakaiproject.util.BaseDbFlatStorage;
036: import org.sakaiproject.util.StorageUser;
037: import org.sakaiproject.util.StringUtil;
038:
039: /**
040: * <p>
041: * DbCachedUserService is an extension of the BaseUserService with a database storage backed up by an in-memory cache.
042: * </p>
043: */
044: public abstract class DbUserService extends BaseUserDirectoryService {
045: /** Our log (commons). */
046: private static Log M_log = LogFactory.getLog(DbUserService.class);
047:
048: /** Table name for users. */
049: protected String m_tableName = "SAKAI_USER";
050:
051: /** Table name for properties. */
052: protected String m_propTableName = "SAKAI_USER_PROPERTY";
053:
054: /** ID field. */
055: protected String m_idFieldName = "USER_ID";
056:
057: /** SORT field 1. */
058: protected String m_sortField1 = "LAST_NAME";
059:
060: /** SORT field 2. */
061: protected String m_sortField2 = "FIRST_NAME";
062:
063: /** All fields. */
064: protected String[] m_fieldNames = { "USER_ID", "EMAIL", "EMAIL_LC",
065: "FIRST_NAME", "LAST_NAME", "TYPE", "PW", "CREATEDBY",
066: "MODIFIEDBY", "CREATEDON", "MODIFIEDON" };
067:
068: /**********************************************************************************************************************************************************************************************************************************************************
069: * Dependencies
070: *********************************************************************************************************************************************************************************************************************************************************/
071:
072: /**
073: * @return the MemoryService collaborator.
074: */
075: protected abstract SqlService sqlService();
076:
077: /**********************************************************************************************************************************************************************************************************************************************************
078: * Configuration
079: *********************************************************************************************************************************************************************************************************************************************************/
080:
081: /**
082: * Configuration: set the table name
083: *
084: * @param path
085: * The table name.
086: */
087: public void setTableName(String name) {
088: m_tableName = name;
089: }
090:
091: /** If true, we do our locks in the remote database, otherwise we do them here. */
092: protected boolean m_useExternalLocks = true;
093:
094: /**
095: * Configuration: set the external locks value.
096: *
097: * @param value
098: * The external locks value.
099: */
100: public void setExternalLocks(String value) {
101: m_useExternalLocks = new Boolean(value).booleanValue();
102: }
103:
104: /** Configuration: to run the ddl on init or not. */
105: protected boolean m_autoDdl = false;
106:
107: /**
108: * Configuration: to run the ddl on init or not.
109: *
110: * @param value
111: * the auto ddl value.
112: */
113: public void setAutoDdl(String value) {
114: m_autoDdl = new Boolean(value).booleanValue();
115: }
116:
117: /**********************************************************************************************************************************************************************************************************************************************************
118: * Init and Destroy
119: *********************************************************************************************************************************************************************************************************************************************************/
120:
121: /**
122: * Final initialization, once all dependencies are set.
123: */
124: public void init() {
125: try {
126: // if we are auto-creating our schema, check and create
127: if (m_autoDdl) {
128: sqlService().ddl(this .getClass().getClassLoader(),
129: "sakai_user");
130:
131: // load the 2.1.0.004 email_lc conversion
132: sqlService().ddl(this .getClass().getClassLoader(),
133: "sakai_user_2_1_0_004");
134:
135: // load the 2.1.0 postmaster password conversion
136: sqlService().ddl(this .getClass().getClassLoader(),
137: "sakai_user_2_1_0");
138:
139: // load the 2.2 id-eid map table conversion
140: sqlService().ddl(this .getClass().getClassLoader(),
141: "sakai_user_2_2_map");
142: }
143:
144: super .init();
145:
146: M_log.info("init(): table: " + m_tableName
147: + " external locks: " + m_useExternalLocks);
148:
149: } catch (Throwable t) {
150: M_log.warn("init(): ", t);
151: }
152: }
153:
154: /**********************************************************************************************************************************************************************************************************************************************************
155: * BaseUserService extensions
156: *********************************************************************************************************************************************************************************************************************************************************/
157:
158: /**
159: * Construct a Storage object.
160: *
161: * @return The new storage object.
162: */
163: protected Storage newStorage() {
164: return new DbStorage(this );
165: }
166:
167: /**********************************************************************************************************************************************************************************************************************************************************
168: * Storage implementation
169: *********************************************************************************************************************************************************************************************************************************************************/
170:
171: /**
172: * Covers for the BaseXmlFileStorage, providing User and UserEdit parameters
173: */
174: protected class DbStorage extends BaseDbFlatStorage implements
175: Storage, SqlReader {
176: /** A prior version's storage model. */
177: protected Storage m_oldStorage = null;
178:
179: /**
180: * Construct.
181: *
182: * @param user
183: * The StorageUser class to call back for creation of Resource and Edit objects.
184: */
185: public DbStorage(StorageUser user) {
186: super (m_tableName, m_idFieldName, m_fieldNames,
187: m_propTableName, m_useExternalLocks, null,
188: sqlService());
189: setSortField(m_sortField1, m_sortField2);
190:
191: m_reader = this ;
192: }
193:
194: public boolean check(String id) {
195: boolean rv = super .checkResource(id);
196:
197: return rv;
198: }
199:
200: public UserEdit getById(String id) {
201: UserEdit rv = (UserEdit) super .getResource(id);
202:
203: return rv;
204: }
205:
206: public UserEdit getByEid(String eid) {
207: // find id from mapping - if not found, we don't have the record
208: String id = checkMapForId(eid);
209: if (id == null)
210: return null;
211:
212: UserEdit rv = (UserEdit) super .getResource(id);
213:
214: return rv;
215: }
216:
217: public List getAll() {
218: // let the db do range selection
219: List all = super .getAllResources();
220: return all;
221: }
222:
223: public List getAll(int first, int last) {
224: // let the db do range selection
225: List all = super .getAllResources(first, last);
226: return all;
227: }
228:
229: public int count() {
230: return super .countAllResources();
231: }
232:
233: public UserEdit put(String id, String eid) {
234: // check for already exists
235: if (check(id))
236: return null;
237:
238: // assure mapping
239: if (!putMap(id, eid))
240: return null;
241:
242: BaseUserEdit rv = (BaseUserEdit) super .putResource(id,
243: fields(id, null, false));
244: if (rv != null)
245: rv.activate();
246: return rv;
247: }
248:
249: public UserEdit edit(String id) {
250: BaseUserEdit rv = (BaseUserEdit) super .editResource(id);
251:
252: if (rv != null)
253: rv.activate();
254: return rv;
255: }
256:
257: public boolean commit(UserEdit edit) {
258: // update the mapping - fail if that does not succeed
259: if (!updateMap(edit.getId(), edit.getEid()))
260: return false;
261:
262: super .commitResource(edit,
263: fields(edit.getId(), edit, true), edit
264: .getProperties());
265: return true;
266: }
267:
268: public void cancel(UserEdit edit) {
269: super .cancelResource(edit);
270: }
271:
272: public void remove(UserEdit edit) {
273: unMap(edit.getId());
274: super .removeResource(edit);
275: }
276:
277: public List search(String criteria, int first, int last) {
278: String search = "%" + criteria + "%";
279: Object[] fields = new Object[5];
280: fields[0] = criteria;
281: fields[1] = search;
282: fields[2] = search.toLowerCase();
283: fields[3] = search;
284: fields[4] = search;
285: List rv = super
286: .getSelectedResources(
287: "SAKAI_USER.USER_ID = SAKAI_USER_ID_MAP.USER_ID AND (SAKAI_USER.USER_ID = ? OR UPPER(EID) LIKE UPPER(?) OR EMAIL_LC LIKE ? OR UPPER(FIRST_NAME) LIKE UPPER(?) OR UPPER(LAST_NAME) LIKE UPPER(?))",
288: "SAKAI_USER_ID_MAP.EID", fields,
289: "SAKAI_USER_ID_MAP");
290:
291: return rv;
292: }
293:
294: public int countSearch(String criteria) {
295: String search = "%" + criteria + "%";
296: Object[] fields = new Object[5];
297: fields[0] = criteria;
298: fields[1] = search;
299: fields[2] = search.toLowerCase();
300: fields[3] = search;
301: fields[4] = search;
302: int rv = super
303: .countSelectedResources(
304: "SAKAI_USER.USER_ID = SAKAI_USER_ID_MAP.USER_ID AND (SAKAI_USER.USER_ID = ? OR UPPER(EID) LIKE UPPER(?) OR EMAIL_LC LIKE ? OR UPPER(FIRST_NAME) LIKE UPPER(?) OR UPPER(LAST_NAME) LIKE UPPER(?))",
305: fields, "SAKAI_USER_ID_MAP");
306:
307: return rv;
308: }
309:
310: /**
311: * {@inheritDoc}
312: */
313: public Collection findUsersByEmail(String email) {
314: Collection rv = new Vector();
315:
316: // search for it
317: Object[] fields = new Object[1];
318: fields[0] = email.toLowerCase();
319: List users = super .getSelectedResources("EMAIL_LC = ?",
320: fields);
321: if (users != null) {
322: rv.addAll(users);
323: }
324:
325: return rv;
326: }
327:
328: /**
329: * Read properties from storage into the edit's properties.
330: *
331: * @param edit
332: * The user to read properties for.
333: */
334: public void readProperties(UserEdit edit,
335: ResourcePropertiesEdit props) {
336: super .readProperties(edit, props);
337: }
338:
339: /**
340: * Get the fields for the database from the edit for this id, and the id again at the end if needed
341: *
342: * @param id
343: * The resource id
344: * @param edit
345: * The edit (may be null in a new)
346: * @param idAgain
347: * If true, include the id field again at the end, else don't.
348: * @return The fields for the database.
349: */
350: protected Object[] fields(String id, UserEdit edit,
351: boolean idAgain) {
352: Object[] rv = new Object[idAgain ? 12 : 11];
353: rv[0] = caseId(id);
354: if (idAgain) {
355: rv[11] = rv[0];
356: }
357:
358: if (edit == null) {
359: String attribUser = sessionManager()
360: .getCurrentSessionUserId();
361:
362: // if no current user, since we are working up a new user record, use the user id as creator...
363: if ((attribUser == null) || (attribUser.length() == 0))
364: attribUser = (String) rv[0];
365:
366: Time now = timeService().newTime();
367: rv[1] = "";
368: rv[2] = "";
369: rv[3] = "";
370: rv[4] = "";
371: rv[5] = "";
372: rv[6] = "";
373: rv[7] = attribUser;
374: rv[8] = attribUser;
375: rv[9] = now;
376: rv[10] = now;
377: }
378:
379: else {
380: rv[1] = StringUtil.trimToZero(edit.getEmail());
381: rv[2] = StringUtil.trimToZero(edit.getEmail()
382: .toLowerCase());
383: rv[3] = StringUtil.trimToZero(edit.getFirstName());
384: rv[4] = StringUtil.trimToZero(edit.getLastName());
385: rv[5] = StringUtil.trimToZero(edit.getType());
386: rv[6] = StringUtil
387: .trimToZero(((BaseUserEdit) edit).m_pw);
388:
389: // for creator and modified by, if null, make it the id
390: rv[7] = StringUtil
391: .trimToNull(((BaseUserEdit) edit).m_createdUserId);
392: if (rv[7] == null) {
393: rv[7] = rv[0];
394: }
395: rv[8] = StringUtil
396: .trimToNull(((BaseUserEdit) edit).m_lastModifiedUserId);
397: if (rv[8] == null) {
398: rv[8] = rv[0];
399: }
400:
401: rv[9] = edit.getCreatedTime();
402: rv[10] = edit.getModifiedTime();
403: }
404:
405: return rv;
406: }
407:
408: /**
409: * Read from the result one set of fields to create a Resource.
410: *
411: * @param result
412: * The Sql query result.
413: * @return The Resource object.
414: */
415: public Object readSqlResultRecord(ResultSet result) {
416: try {
417: String id = result.getString(1);
418: String email = result.getString(2);
419: String email_lc = result.getString(3);
420: String firstName = result.getString(4);
421: String lastName = result.getString(5);
422: String type = result.getString(6);
423: String pw = result.getString(7);
424: String createdBy = result.getString(8);
425: String modifiedBy = result.getString(9);
426: Time createdOn = timeService().newTime(
427: result.getTimestamp(10, sqlService().getCal())
428: .getTime());
429: Time modifiedOn = timeService().newTime(
430: result.getTimestamp(11, sqlService().getCal())
431: .getTime());
432:
433: // find the eid from the mapping
434: String eid = checkMapForEid(id);
435: if (eid == null) {
436: M_log.warn("readSqlResultRecord: null eid for id: "
437: + id);
438: }
439:
440: // create the Resource from these fields
441: return new BaseUserEdit(id, eid, email, firstName,
442: lastName, type, pw, createdBy, createdOn,
443: modifiedBy, modifiedOn);
444: } catch (SQLException e) {
445: M_log.warn("readSqlResultRecord: " + e);
446: return null;
447: }
448: }
449:
450: /**
451: * Create a mapping between the id and eid.
452: *
453: * @param id
454: * The user id.
455: * @param eid
456: * The user eid.
457: * @return true if successful, false if not (id or eid might be in use).
458: */
459: public boolean putMap(String id, String eid) {
460: // if we are not doing separate id/eid, do nothing
461: if (!m_separateIdEid)
462: return true;
463:
464: String statement = "insert into SAKAI_USER_ID_MAP (USER_ID, EID) values (?,?)";
465:
466: Object fields[] = new Object[2];
467: fields[0] = id;
468: fields[1] = eid;
469:
470: return m_sql.dbWrite(statement, fields);
471: }
472:
473: /**
474: * Update the mapping
475: *
476: * @param id
477: * The user id.
478: * @param eid
479: * The user eid.
480: * @return true if successful, false if not (id or eid might be in use).
481: */
482: protected boolean updateMap(String id, String eid) {
483: // if we are not doing separate id/eid, do nothing
484: if (!m_separateIdEid)
485: return true;
486:
487: // do we have this id mapped?
488: String eidAlready = checkMapForEid(id);
489:
490: // if not, add it
491: if (eidAlready == null) {
492: return putMap(id, eid);
493: }
494:
495: // we have a mapping, is it what we want?
496: if (eidAlready.equals(eid))
497: return true;
498:
499: // we have a mapping that needs to be updated
500: String statement = "update SAKAI_USER_ID_MAP set EID=? where USER_ID=?";
501:
502: Object fields[] = new Object[2];
503: fields[0] = eid;
504: fields[1] = id;
505:
506: return m_sql.dbWrite(statement, fields);
507: }
508:
509: /**
510: * Remove the mapping for this id
511: *
512: * @param id
513: * The user id.
514: */
515: protected void unMap(String id) {
516: // if we are not doing separate id/eid, do nothing
517: if (!m_separateIdEid)
518: return;
519:
520: String statement = "delete from SAKAI_USER_ID_MAP where USER_ID=?";
521:
522: Object fields[] = new Object[1];
523: fields[0] = id;
524:
525: m_sql.dbWrite(statement, fields);
526: }
527:
528: /**
529: * Check the id -> eid mapping: lookup this id and return the eid if found
530: *
531: * @param id
532: * The user id to lookup.
533: * @return The eid mapped to this id, or null if none.
534: */
535: public String checkMapForEid(String id) {
536: // if we are not doing separate id/eid, return the id
537: if (!m_separateIdEid)
538: return id;
539:
540: String statement = "select EID from SAKAI_USER_ID_MAP where USER_ID=?";
541: Object fields[] = new Object[1];
542: fields[0] = id;
543: List rv = sqlService().dbRead(statement, fields, null);
544:
545: if (rv.size() > 0) {
546: String eid = (String) rv.get(0);
547: return eid;
548: }
549:
550: return null;
551: }
552:
553: /**
554: * Check the id -> eid mapping: lookup this eid and return the id if found
555: *
556: * @param eid
557: * The user eid to lookup.
558: * @return The id mapped to this eid, or null if none.
559: */
560: public String checkMapForId(String eid) {
561: // if we are not doing separate id/eid, do nothing
562: if (!m_separateIdEid)
563: return eid;
564:
565: String statement = "select USER_ID from SAKAI_USER_ID_MAP where EID=?";
566: Object fields[] = new Object[1];
567: fields[0] = eid;
568: List rv = sqlService().dbRead(statement, fields, null);
569:
570: if (rv.size() > 0) {
571: String id = (String) rv.get(0);
572: return id;
573: }
574:
575: return null;
576: }
577: }
578: }
|