001: /**********************************************************************************
002:
003: * $URL: https://source.sakaiproject.org/svn/osp/tags/sakai_2-4-1/jsf/example/src/java/migration/RepositoryUpgrader.java $
004:
005: * $Id: RepositoryUpgrader.java 10835 2006-06-17 03:25:03Z lance@indiana.edu $
006:
007: ***********************************************************************************
008:
009: *
010:
011: * Copyright (c) 2005, 2006 The Sakai Foundation.
012:
013: *
014:
015: * Licensed under the Educational Community License, Version 1.0 (the "License");
016:
017: * you may not use this file except in compliance with the License.
018:
019: * You may obtain a copy of the License at
020:
021: *
022:
023: * http://www.opensource.org/licenses/ecl1.php
024:
025: *
026:
027: * Unless required by applicable law or agreed to in writing, software
028:
029: * distributed under the License is distributed on an "AS IS" BASIS,
030:
031: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
032:
033: * See the License for the specific language governing permissions and
034:
035: * limitations under the License.
036:
037: *
038:
039: **********************************************************************************/package migration;
040:
041: import java.sql.Connection;
042: import java.sql.ResultSet;
043: import java.sql.SQLException;
044: import java.util.Date;
045: import java.util.GregorianCalendar;
046: import java.util.Iterator;
047: import java.util.List;
048:
049: import org.apache.commons.logging.Log;
050: import org.sakaiproject.component.cover.ComponentManager;
051: import org.sakaiproject.content.api.ContentCollectionEdit;
052: import org.sakaiproject.content.api.ContentHostingService;
053: import org.sakaiproject.content.api.ContentResourceEdit;
054: import org.sakaiproject.db.api.SqlReader;
055: import org.sakaiproject.db.cover.SqlService;
056: import org.sakaiproject.entity.api.Entity;
057: import org.sakaiproject.entity.api.ResourceProperties;
058: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
059: import org.sakaiproject.exception.IdInvalidException;
060: import org.sakaiproject.exception.IdUnusedException;
061: import org.sakaiproject.exception.IdUsedException;
062: import org.sakaiproject.exception.InconsistentException;
063: import org.sakaiproject.exception.OverQuotaException;
064: import org.sakaiproject.exception.PermissionException;
065: import org.sakaiproject.exception.ServerOverloadException;
066: import org.sakaiproject.exception.TypeException;
067: import org.sakaiproject.time.api.Time;
068: import org.sakaiproject.time.cover.TimeService;
069: import org.sakaiproject.tool.api.Session;
070: import org.sakaiproject.tool.cover.SessionManager;
071:
072: /**
073:
074: * "/files" is the base of the repository.
075:
076: * Resources doesn't have a base.
077:
078: *
079:
080: * "/files/users/*" => "/user/*"
081:
082: * "/files/worksites/*" => "/group/*"
083:
084: * respository worksites are ids without dashes and resources worksites have dashes
085:
086: * how is that being converted from 1.5=>2.1 in sakai?
087:
088: *
089:
090: * When deleting a folder, it doesn't actually remove the record. It just adds 2000 years to the modification date.
091:
092: * This is how we can retain all the info and add our "processed" tag without changing the database.
093:
094: *
095:
096: * @author andersjb
097:
098: *
099:
100: */
101:
102: public class RepositoryUpgrader {
103:
104: private final static Log logger = org.apache.commons.logging.LogFactory
105: .getLog(RepositoryUpgrader.class);
106:
107: private ContentHostingService contentHostingService;
108:
109: /**
110:
111: * This moves the files from repository to resources. The process is interruptible.
112:
113: * This method builds the tree structure
114:
115: *
116:
117: */
118:
119: public void upgrade()
120:
121: {
122:
123: contentHostingService = (ContentHostingService) ComponentManager
124: .get(ContentHostingService.APPLICATION_ID);
125:
126: // move the folder tree over to the resources
127:
128: try {
129:
130: moveTree();
131:
132: } catch (Exception e) {
133:
134: logger.fatal(e);
135:
136: }
137:
138: }
139:
140: /**
141:
142: * all artifacts have a parent directory
143:
144: *
145:
146: */
147:
148: protected void moveTree() throws SQLException,
149:
150: InconsistentException, PermissionException, IdUsedException,
151: IdInvalidException, TypeException
152:
153: {
154:
155: moveFolder(null, false);
156:
157: }
158:
159: /**
160:
161: * get all the children folders and create them, then recurse those children.
162:
163: * @param parentId String
164:
165: */
166:
167: protected void moveFolder(ReFolder parentFolder, boolean move)
168: throws SQLException,
169:
170: InconsistentException, PermissionException,
171: IdUsedException, IdInvalidException
172:
173: {
174:
175: List children = getChildren(parentFolder);
176:
177: upgradeEntityPaths(children);
178:
179: for (Iterator i = children.iterator(); i.hasNext();) {
180:
181: RepositoryEntity ent = (RepositoryEntity) i.next();
182:
183: ent.transition(move);
184:
185: }
186:
187: }
188:
189: /**
190:
191: * this pulls in the file from the old location and places it in the resources
192:
193: * @param parentId String
194:
195: */
196:
197: protected void moveFile(ReFile file) throws SQLException,
198:
199: InconsistentException, PermissionException, IdUsedException,
200: IdInvalidException
201:
202: {
203:
204: // set the user information into the current session
205:
206: Session sakaiSession = SessionManager.getCurrentSession();
207:
208: String uid, eid;
209:
210: uid = sakaiSession.getUserId();
211:
212: eid = sakaiSession.getUserEid();
213:
214: sakaiSession.setUserId(file.getOwnerId());
215:
216: sakaiSession.setUserEid(file.getOwnerId());
217:
218: ResourcePropertiesEdit resourceProperties = contentHostingService
219: .newResourceProperties();
220:
221: resourceProperties.addProperty(
222: ResourceProperties.PROP_DISPLAY_NAME, file.getTitle());
223:
224: resourceProperties.addProperty(
225: ResourceProperties.PROP_DESCRIPTION, file.getTitle());
226:
227: resourceProperties.addProperty(
228: ResourceProperties.PROP_CREATION_DATE,
229:
230: dateToTime(file.getCreationDate()).toString());
231:
232: resourceProperties.addProperty(
233: ResourceProperties.PROP_MODIFIED_DATE,
234:
235: dateToTime(file.getLastModifiedDate()).toString());
236:
237: resourceProperties.addProperty(ResourceProperties.PROP_CREATOR,
238: file.getOwnerId());
239:
240: resourceProperties.addProperty(
241: ResourceProperties.PROP_MODIFIED_BY, file.getOwnerId());
242:
243: try {
244:
245: //We can't just add a collection the normal way because we want to override the live properties
246:
247: ContentResourceEdit cc = contentHostingService
248: .addResource(file.getUri());
249:
250: addProperties(cc.getPropertiesEdit(), resourceProperties);
251:
252: cc.setContent("".getBytes());
253:
254: cc.setContentType("");
255:
256: contentHostingService.commitResource(cc);
257:
258: contentHostingService.setUuid(file.getUri(), file
259: .getArtifactId());
260:
261: } catch (ServerOverloadException e) {
262:
263: throw new RuntimeException(e);
264:
265: } catch (OverQuotaException e) {
266:
267: throw new RuntimeException(e);
268:
269: }
270:
271: sakaiSession.setUserId(uid);
272:
273: sakaiSession.setUserEid(eid);
274:
275: }
276:
277: /**
278:
279: * This should be ordered by uri, always. the folder always comes first
280:
281: * @param parentId String of the parent folder structure id
282:
283: * @return List of class Folder
284:
285: * @throws SQLException
286:
287: */
288:
289: protected List getChildren(ReFolder parentFolder)
290: throws SQLException
291:
292: {
293:
294: final Connection connection = SqlService.borrowConnection();
295:
296: boolean wasCommit = connection.getAutoCommit();
297:
298: connection.setAutoCommit(false);
299:
300: String sql = "select row_id, parent, osp_tree_node.id, osp_tree_node.name, uri, "
301: +
302:
303: "creation, last_modified, owner_id, worksiteId, typeId "
304: +
305:
306: "from osp_tree_node join osp_node_metadata on osp_tree_node.id=osp_node_metadata.id "
307: +
308:
309: "where parent ";
310:
311: Object[] fields = null;
312:
313: if (parentFolder != null) {
314:
315: sql += "=? ";
316:
317: fields = new Object[1];
318:
319: fields[0] = parentFolder.getFolderStructureId();
320:
321: } else
322:
323: sql += "is null ";
324:
325: sql += "order by uri";
326:
327: List children = SqlService.dbRead(connection, sql, fields,
328: new SqlReader() {
329:
330: public Object readSqlResultRecord(ResultSet result)
331:
332: {
333:
334: try
335:
336: {
337:
338: RepositoryEntity ent = null;
339:
340: String type = result.getString(10);
341:
342: if (type.equals("folder")) {
343:
344: ent = new ReFolder();
345:
346: } else if (type.equals("fileArtifact")) {
347:
348: ent = new ReFile();
349:
350: } else {
351:
352: ent = new ReForm(type);
353:
354: }
355:
356: ent.setFolderStructureId(result
357: .getString(1));
358:
359: ent.setParentFolderId(result.getString(2));
360:
361: ent.setArtifactId(result.getString(3));
362:
363: ent.setTitle(result.getString(4));
364:
365: ent.setUri(result.getString(5));
366:
367: ent.setCreationDate(result.getDate(6));
368:
369: ent.setLastModifiedDate(result.getDate(7));
370:
371: ent.setOwnerId(result.getString(8));
372:
373: ent.setWorksiteId(result.getString(9));
374:
375: return ent;
376:
377: } catch (SQLException ignore) {
378:
379: return null;
380:
381: }
382:
383: }
384:
385: });
386:
387: connection.commit();
388:
389: connection.setAutoCommit(wasCommit);
390:
391: SqlService.returnConnection(connection);
392:
393: for (Iterator i = children.iterator(); i.hasNext();) {
394:
395: RepositoryEntity ent = (RepositoryEntity) i.next();
396:
397: ent.setParentFolder(parentFolder);
398:
399: }
400:
401: return children;
402:
403: }
404:
405: protected Time dateToTime(Date in)
406:
407: {
408:
409: GregorianCalendar gc = new GregorianCalendar();
410:
411: gc.setTime(in);
412:
413: return TimeService.newTime(gc);
414:
415: }
416:
417: protected String idToGuid(String str)
418: throws java.lang.IllegalArgumentException
419:
420: {
421:
422: if (str == null)
423: return null;
424:
425: int length = str.length();
426:
427: if (length == 36 || length == 0)
428:
429: return str;
430:
431: if (length == 32)
432:
433: return str.substring(0, 8) + "-" + str.substring(8, 12)
434: + "-" + str.substring(12, 16)
435:
436: + "-" + str.substring(16, 20) + "-"
437: + str.substring(20, 32);
438:
439: throw new IllegalArgumentException("");
440:
441: }
442:
443: /**
444:
445: * This method will create a folder in resources if it doesn't exist.
446:
447: * @param folder
448:
449: * @throws InconsistentException
450:
451: * @throws PermissionException
452:
453: * @throws IdUsedException
454:
455: * @throws IdInvalidException
456:
457: * @throws TypeException
458:
459: */
460:
461: protected void createNewResourceFolder(ReFolder folder)
462:
463: throws InconsistentException, PermissionException, IdUsedException,
464: IdInvalidException {
465:
466: try {
467:
468: contentHostingService.getCollection(folder.getUri());
469:
470: } catch (PermissionException e) {
471:
472: logger.error(e);
473:
474: } catch (TypeException e) {
475:
476: logger.error(e);
477:
478: } catch (IdUnusedException e) {
479:
480: // wasn't found, so we need to create it
481:
482: // set the user information into the current session
483:
484: Session sakaiSession = SessionManager.getCurrentSession();
485:
486: String uid, eid;
487:
488: uid = sakaiSession.getUserId();
489:
490: eid = sakaiSession.getUserEid();
491:
492: sakaiSession.setUserId(folder.getOwnerId());
493:
494: sakaiSession.setUserEid(folder.getOwnerId());
495:
496: ResourcePropertiesEdit resourceProperties = contentHostingService
497: .newResourceProperties();
498:
499: resourceProperties.addProperty(
500: ResourceProperties.PROP_DISPLAY_NAME, folder
501: .getTitle());
502:
503: resourceProperties.addProperty(
504: ResourceProperties.PROP_DESCRIPTION, folder
505: .getTitle());
506:
507: resourceProperties.addProperty(
508: ResourceProperties.PROP_CREATION_DATE,
509:
510: dateToTime(folder.getCreationDate()).toString());
511:
512: resourceProperties
513: .addProperty(ResourceProperties.PROP_MODIFIED_DATE,
514:
515: dateToTime(folder.getLastModifiedDate()).toString());
516:
517: resourceProperties.addProperty(
518: ResourceProperties.PROP_CREATOR, folder
519: .getOwnerId());
520:
521: resourceProperties.addProperty(
522: ResourceProperties.PROP_MODIFIED_BY, folder
523: .getOwnerId());
524:
525: //We can't just add a collection the normal way because we want to override the live properties
526:
527: ContentCollectionEdit cc = contentHostingService
528: .addCollection(folder.getUri());
529:
530: addProperties(cc.getPropertiesEdit(), resourceProperties);
531:
532: contentHostingService.commitCollection(cc);
533:
534: sakaiSession.setUserId(uid);
535:
536: sakaiSession.setUserEid(eid);
537:
538: }
539:
540: }
541:
542: /**
543:
544: * strips "/files", transitions "/users" => "/user"
545:
546: * @param folders
547:
548: */
549:
550: protected void upgradeEntityPaths(List folders)
551:
552: {
553:
554: for (Iterator i = folders.iterator(); i.hasNext();) {
555:
556: RepositoryEntity ent = (RepositoryEntity) i.next();
557:
558: String uri = ent.getUri();
559:
560: if (uri.startsWith(Entity.SEPARATOR + "files"))
561:
562: uri = uri.substring((Entity.SEPARATOR + "files")
563: .length());
564:
565: if (uri.startsWith(Entity.SEPARATOR + "users")) {
566:
567: uri = uri.substring((Entity.SEPARATOR + "users")
568: .length());
569:
570: if (uri.length() == 0)
571:
572: ent.setTitle("user");
573:
574: uri = Entity.SEPARATOR + "user" + uri;
575:
576: } else if (uri.startsWith(Entity.SEPARATOR + "worksites")) {
577:
578: uri = uri.substring((Entity.SEPARATOR + "worksites")
579: .length());
580:
581: if (uri.length() == 0)
582:
583: ent.setTitle("group");
584:
585: uri = Entity.SEPARATOR + "group" + uri;
586:
587: }
588:
589: // all folders need to have an end separator
590:
591: if (ent.isFolder() && !uri.endsWith(Entity.SEPARATOR))
592:
593: uri += Entity.SEPARATOR;
594:
595: ent.setUri(uri);
596:
597: ent.setArtifactId(idToGuid(ent.getArtifactId()));
598:
599: }
600:
601: }
602:
603: /**
604:
605: * Add properties for a resource.
606:
607: *
608:
609: * @param r
610:
611: * The resource.
612:
613: * @param props
614:
615: * The properties.
616:
617: */
618:
619: protected void addProperties(ResourcePropertiesEdit p,
620: ResourceProperties props)
621:
622: {
623:
624: if (props == null)
625: return;
626:
627: Iterator it = props.getPropertyNames();
628:
629: while (it.hasNext())
630:
631: {
632:
633: String name = (String) it.next();
634:
635: p.addProperty(name, props.getProperty(name));
636:
637: }
638:
639: } // addProperties
640:
641: public ContentHostingService getContentHostingService() {
642:
643: return contentHostingService;
644:
645: }
646:
647: public void setContentHostingService(
648: ContentHostingService contentHostingService) {
649:
650: this .contentHostingService = contentHostingService;
651:
652: }
653:
654: private abstract class RepositoryEntity {
655:
656: protected String folderStructureId, parentFolderId, artifactId,
657: title, uri, ownerId, worksiteId, type;
658:
659: protected ReFolder parentFolder;
660:
661: protected Date creationDate, lastModifiedDate;
662:
663: public String getArtifactId() {
664: return artifactId;
665: }
666:
667: public void setArtifactId(String artifactId) {
668: this .artifactId = artifactId;
669: }
670:
671: public String getFolderStructureId() {
672: return folderStructureId;
673: }
674:
675: public void setFolderStructureId(String folderStructureId) {
676:
677: this .folderStructureId = folderStructureId;
678: }
679:
680: public String getParentFolderId() {
681: return parentFolderId;
682: }
683:
684: public void setParentFolderId(String parentFolderId) {
685:
686: this .parentFolderId = parentFolderId;
687: }
688:
689: public String getTitle() {
690: return title;
691: }
692:
693: public void setTitle(String title) {
694: this .title = title;
695: }
696:
697: public ReFolder getParentFolder() {
698: return parentFolder;
699: }
700:
701: public void setParentFolder(ReFolder parentFolder) {
702: this .parentFolder = parentFolder;
703: }
704:
705: public String getUri() {
706: return uri;
707: }
708:
709: public void setUri(String uri) {
710: this .uri = uri;
711: }
712:
713: public Date getCreationDate() {
714: return creationDate;
715: }
716:
717: public void setCreationDate(Date creationDate) {
718: this .creationDate = creationDate;
719: }
720:
721: public Date getLastModifiedDate() {
722: return lastModifiedDate;
723: }
724:
725: public void setLastModifiedDate(Date lastModifiedDate) {
726: this .lastModifiedDate = lastModifiedDate;
727: }
728:
729: public String getOwnerId() {
730: return ownerId;
731: }
732:
733: public void setOwnerId(String ownerId) {
734: this .ownerId = ownerId;
735: }
736:
737: public String getWorksiteId() {
738: return worksiteId;
739: }
740:
741: public void setWorksiteId(String worksiteId) {
742: this .worksiteId = worksiteId;
743: }
744:
745: public String getType() {
746: return type;
747: }
748:
749: //public void setType(String type) {this.type = type;}
750:
751: public abstract void transition(boolean move)
752: throws SQLException,
753:
754: InconsistentException, PermissionException,
755: IdUsedException, IdInvalidException;
756:
757: public boolean isFolder() {
758: return false;
759: }
760:
761: public boolean isFile() {
762: return false;
763: }
764:
765: public boolean isForm() {
766: return false;
767: }
768:
769: }
770:
771: private class ReFolder extends RepositoryEntity {
772:
773: public ReFolder() {
774: type = "folder";
775: }
776:
777: public boolean isFolder() {
778: return true;
779: }
780:
781: public void transition(boolean move) throws SQLException,
782:
783: InconsistentException, PermissionException, IdUsedException,
784: IdInvalidException
785:
786: {
787:
788: // create the folder
789:
790: if (move)
791:
792: createNewResourceFolder(this );
793:
794: // move all the children nodes
795:
796: moveFolder(this , true);
797:
798: // delete this folder
799:
800: // markEntityProcessed(this);
801:
802: }
803:
804: }
805:
806: private class ReFile extends RepositoryEntity {
807:
808: public ReFile() {
809: type = "fileArtifact";
810: }
811:
812: public boolean isFile() {
813: return true;
814: }
815:
816: public void transition(boolean move) throws SQLException,
817:
818: InconsistentException, PermissionException, IdUsedException,
819: IdInvalidException
820:
821: {
822:
823: moveFile(this );
824:
825: // delete this file
826:
827: // markEntityProcessed(this);
828:
829: }
830:
831: }
832:
833: private class ReForm extends RepositoryEntity {
834:
835: public ReForm(String type) {
836: this .type = type;
837: }
838:
839: public boolean isForm() {
840: return true;
841: }
842:
843: public void transition(boolean move) throws SQLException,
844:
845: InconsistentException, PermissionException, IdUsedException,
846: IdInvalidException
847:
848: {
849:
850: // moveForm(this);
851:
852: // delete this file
853:
854: // markEntityProcessed(this);
855:
856: }
857:
858: }
859:
860: }
|