/********************************************************************************** * $URL: https://source.sakaiproject.org/svn/osp/trunk/jsf/example/src/java/migration/RepositoryUpgrader.java $ * $Id: RepositoryUpgrader.java 105079 2012-02-24 23:08:11Z ottenhoff@longsight.com $ *********************************************************************************** * * Copyright (c) 2006, 2008 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package migration; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.sakaiproject.component.cover.ComponentManager; import org.sakaiproject.content.api.ContentCollectionEdit; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.content.api.ContentResourceEdit; import org.sakaiproject.db.api.SqlReader; import org.sakaiproject.db.cover.SqlService; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.entity.api.ResourcePropertiesEdit; import org.sakaiproject.exception.IdInvalidException; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.IdUsedException; import org.sakaiproject.exception.InconsistentException; import org.sakaiproject.exception.OverQuotaException; import org.sakaiproject.exception.PermissionException; import org.sakaiproject.exception.ServerOverloadException; import org.sakaiproject.exception.TypeException; import org.sakaiproject.time.api.Time; import org.sakaiproject.time.cover.TimeService; import org.sakaiproject.tool.api.Session; import org.sakaiproject.tool.cover.SessionManager; /** * "/files" is the base of the repository. * Resources doesn't have a base. * * "/files/users/*" => "/user/*" * "/files/worksites/*" => "/group/*" * respository worksites are ids without dashes and resources worksites have dashes * how is that being converted from 1.5=>2.1 in sakai? * * When deleting a folder, it doesn't actually remove the record. It just adds 2000 years to the modification date. * This is how we can retain all the info and add our "processed" tag without changing the database. * * @author andersjb * */ public class RepositoryUpgrader { private final static Log logger = org.apache.commons.logging.LogFactory.getLog(RepositoryUpgrader.class); private ContentHostingService contentHostingService; /** * This moves the files from repository to resources. The process is interruptible. * This method builds the tree structure * */ public void upgrade() { contentHostingService = (ContentHostingService) ComponentManager.get(ContentHostingService.APPLICATION_ID); // move the folder tree over to the resources try { moveTree(); } catch(Exception e) { logger.fatal(e); } } /** * all artifacts have a parent directory * */ protected void moveTree() throws SQLException, InconsistentException, PermissionException, IdUsedException, IdInvalidException, TypeException { moveFolder(null, false); } /** * get all the children folders and create them, then recurse those children. * @param parentId String */ protected void moveFolder(ReFolder parentFolder, boolean move) throws SQLException, InconsistentException, PermissionException, IdUsedException, IdInvalidException { List children = getChildren(parentFolder); upgradeEntityPaths(children); for(Iterator i = children.iterator(); i.hasNext(); ) { RepositoryEntity ent = (RepositoryEntity)i.next(); ent.transition(move); } } /** * this pulls in the file from the old location and places it in the resources * @param parentId String */ protected void moveFile(ReFile file) throws SQLException, InconsistentException, PermissionException, IdUsedException, IdInvalidException { // set the user information into the current session Session sakaiSession = SessionManager.getCurrentSession(); String uid, eid; uid = sakaiSession.getUserId(); eid = sakaiSession.getUserEid(); sakaiSession.setUserId(file.getOwnerId()); sakaiSession.setUserEid(file.getOwnerId()); ResourcePropertiesEdit resourceProperties = contentHostingService.newResourceProperties(); resourceProperties.addProperty (ResourceProperties.PROP_DISPLAY_NAME, file.getTitle()); resourceProperties.addProperty (ResourceProperties.PROP_DESCRIPTION, file.getTitle()); resourceProperties.addProperty (ResourceProperties.PROP_CREATION_DATE, dateToTime(file.getCreationDate()).toString()); resourceProperties.addProperty (ResourceProperties.PROP_MODIFIED_DATE, dateToTime(file.getLastModifiedDate()).toString()); resourceProperties.addProperty (ResourceProperties.PROP_CREATOR, file.getOwnerId()); resourceProperties.addProperty (ResourceProperties.PROP_MODIFIED_BY, file.getOwnerId()); try { //We can't just add a collection the normal way because we want to override the live properties ContentResourceEdit cc = contentHostingService.addResource(file.getUri()); addProperties(cc.getPropertiesEdit(), resourceProperties); cc.setContent("".getBytes()); cc.setContentType(""); contentHostingService.commitResource(cc); contentHostingService.setUuid(file.getUri(), file.getArtifactId()); } catch(ServerOverloadException e) { throw new RuntimeException(e); } catch(OverQuotaException e) { throw new RuntimeException(e); } sakaiSession.setUserId(uid); sakaiSession.setUserEid(eid); } /** * This should be ordered by uri, always. the folder always comes first * @param parentId String of the parent folder structure id * @return List of class Folder * @throws SQLException */ protected List getChildren(ReFolder parentFolder) //throws SQLException { Connection connection = null; boolean wasCommit = false; try { connection = SqlService.borrowConnection(); wasCommit = connection.getAutoCommit(); connection.setAutoCommit(false); String sql = "select row_id, parent, osp_tree_node.id, osp_tree_node.name, uri, " + "creation, last_modified, owner_id, worksiteId, typeId " + "from osp_tree_node join osp_node_metadata on osp_tree_node.id=osp_node_metadata.id " + "where parent "; Object[] fields = null; if(parentFolder != null) { sql += "=? "; fields = new Object[1]; fields[0] = parentFolder.getFolderStructureId(); } else sql += "is null "; sql += "order by uri"; List children = SqlService.dbRead(connection, sql, fields, new SqlReader() { public Object readSqlResultRecord(ResultSet result) { try { RepositoryEntity ent = null; String type = result.getString(10); if(type.equals("folder")) { ent = new ReFolder(); } else if(type.equals("fileArtifact")) { ent = new ReFile(); } else { ent = new ReForm(type); } ent.setFolderStructureId(result.getString(1)); ent.setParentFolderId(result.getString(2)); ent.setArtifactId(result.getString(3)); ent.setTitle(result.getString(4)); ent.setUri(result.getString(5)); ent.setCreationDate(result.getDate(6)); ent.setLastModifiedDate(result.getDate(7)); ent.setOwnerId(result.getString(8)); ent.setWorksiteId(result.getString(9)); return ent; } catch (SQLException ignore) { return null; } } }); for(Iterator i = children.iterator(); i.hasNext(); ) { RepositoryEntity ent = (RepositoryEntity)i.next(); ent.setParentFolder(parentFolder); } return children; } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { connection.commit(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { connection.setAutoCommit(wasCommit); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } SqlService.returnConnection(connection); } return null; } protected Time dateToTime(Date in) { GregorianCalendar gc = new GregorianCalendar(); gc.setTime(in); return TimeService.newTime(gc); } protected String idToGuid(String str) throws java.lang.IllegalArgumentException { if(str == null) return null; int length = str.length(); if(length == 36 || length == 0) return str; if(length == 32) return str.substring(0, 8) + "-" + str.substring(8, 12) + "-" + str.substring(12, 16) + "-" + str.substring(16, 20) + "-" + str.substring(20, 32); throw new IllegalArgumentException(""); } /** * This method will create a folder in resources if it doesn't exist. * @param folder * @throws InconsistentException * @throws PermissionException * @throws IdUsedException * @throws IdInvalidException * @throws TypeException */ protected void createNewResourceFolder(ReFolder folder) throws InconsistentException, PermissionException, IdUsedException, IdInvalidException { try { contentHostingService.getCollection(folder.getUri()); } catch (PermissionException e) { logger.error(e); } catch (TypeException e) { logger.error(e); } catch (IdUnusedException e) { // wasn't found, so we need to create it // set the user information into the current session Session sakaiSession = SessionManager.getCurrentSession(); String uid, eid; uid = sakaiSession.getUserId(); eid = sakaiSession.getUserEid(); sakaiSession.setUserId(folder.getOwnerId()); sakaiSession.setUserEid(folder.getOwnerId()); ResourcePropertiesEdit resourceProperties = contentHostingService.newResourceProperties(); resourceProperties.addProperty (ResourceProperties.PROP_DISPLAY_NAME, folder.getTitle()); resourceProperties.addProperty (ResourceProperties.PROP_DESCRIPTION, folder.getTitle()); resourceProperties.addProperty (ResourceProperties.PROP_CREATION_DATE, dateToTime(folder.getCreationDate()).toString()); resourceProperties.addProperty (ResourceProperties.PROP_MODIFIED_DATE, dateToTime(folder.getLastModifiedDate()).toString()); resourceProperties.addProperty (ResourceProperties.PROP_CREATOR, folder.getOwnerId()); resourceProperties.addProperty (ResourceProperties.PROP_MODIFIED_BY, folder.getOwnerId()); //We can't just add a collection the normal way because we want to override the live properties ContentCollectionEdit cc = contentHostingService.addCollection(folder.getUri()); addProperties(cc.getPropertiesEdit(), resourceProperties); contentHostingService.commitCollection(cc); sakaiSession.setUserId(uid); sakaiSession.setUserEid(eid); } } /** * strips "/files", transitions "/users" => "/user" * @param folders */ protected void upgradeEntityPaths(List folders) { for(Iterator i = folders.iterator(); i.hasNext(); ) { RepositoryEntity ent = (RepositoryEntity)i.next(); String uri = ent.getUri(); if(uri.startsWith(Entity.SEPARATOR+"files")) uri = uri.substring((Entity.SEPARATOR+"files").length()); if(uri.startsWith(Entity.SEPARATOR+"users")) { uri = uri.substring((Entity.SEPARATOR+"users").length()); if(uri.length() == 0) ent.setTitle("user"); uri = Entity.SEPARATOR+"user" + uri; } else if(uri.startsWith(Entity.SEPARATOR+"worksites")) { uri = uri.substring((Entity.SEPARATOR+"worksites").length()); if(uri.length() == 0) ent.setTitle("group"); uri = Entity.SEPARATOR+"group" + uri; } // all folders need to have an end separator if(ent.isFolder() && !uri.endsWith(Entity.SEPARATOR)) uri += Entity.SEPARATOR; ent.setUri(uri); ent.setArtifactId(idToGuid(ent.getArtifactId())); } } /** * Add properties for a resource. * * @param r * The resource. * @param props * The properties. */ protected void addProperties(ResourcePropertiesEdit p, ResourceProperties props) { if (props == null) return; Iterator it = props.getPropertyNames(); while (it.hasNext()) { String name = (String) it.next(); p.addProperty(name, props.getProperty(name)); } } // addProperties public ContentHostingService getContentHostingService() { return contentHostingService; } public void setContentHostingService(ContentHostingService contentHostingService) { this.contentHostingService = contentHostingService; } private abstract class RepositoryEntity { protected String folderStructureId, parentFolderId, artifactId, title, uri, ownerId, worksiteId, type; protected ReFolder parentFolder; protected Date creationDate, lastModifiedDate; public String getArtifactId() {return artifactId;} public void setArtifactId(String artifactId) {this.artifactId = artifactId;} public String getFolderStructureId() {return folderStructureId;} public void setFolderStructureId(String folderStructureId) { this.folderStructureId = folderStructureId;} public String getParentFolderId() {return parentFolderId;} public void setParentFolderId(String parentFolderId) { this.parentFolderId = parentFolderId;} public String getTitle() {return title;} public void setTitle(String title) {this.title = title;} public ReFolder getParentFolder() {return parentFolder;} public void setParentFolder(ReFolder parentFolder) {this.parentFolder = parentFolder;} public String getUri() {return uri;} public void setUri(String uri) {this.uri = uri;} public Date getCreationDate() {return creationDate;} public void setCreationDate(Date creationDate) {this.creationDate = creationDate;} public Date getLastModifiedDate() {return lastModifiedDate;} public void setLastModifiedDate(Date lastModifiedDate) {this.lastModifiedDate = lastModifiedDate;} public String getOwnerId() {return ownerId;} public void setOwnerId(String ownerId) {this.ownerId = ownerId;} public String getWorksiteId() {return worksiteId;} public void setWorksiteId(String worksiteId) {this.worksiteId = worksiteId;} public String getType() {return type;} //public void setType(String type) {this.type = type;} public abstract void transition(boolean move) throws SQLException, InconsistentException, PermissionException, IdUsedException, IdInvalidException; public boolean isFolder() { return false; } public boolean isFile() { return false; } public boolean isForm() { return false; } } private class ReFolder extends RepositoryEntity { public ReFolder() {type = "folder";} public boolean isFolder() { return true; } public void transition(boolean move) throws SQLException, InconsistentException, PermissionException, IdUsedException, IdInvalidException { // create the folder if(move) createNewResourceFolder(this); // move all the children nodes moveFolder(this, true); // delete this folder // markEntityProcessed(this); } } private class ReFile extends RepositoryEntity { public ReFile() {type = "fileArtifact";} public boolean isFile() { return true; } public void transition(boolean move) throws SQLException, InconsistentException, PermissionException, IdUsedException, IdInvalidException { moveFile(this); // delete this file // markEntityProcessed(this); } } private class ReForm extends RepositoryEntity { public ReForm(String type) {this.type = type;} public boolean isForm() { return true; } public void transition(boolean move) throws SQLException, InconsistentException, PermissionException, IdUsedException, IdInvalidException { // moveForm(this); // delete this file // markEntityProcessed(this); } } }