Index: lams_common/.classpath =================================================================== diff -u -rea6e9356bb158005dd1536639b0c43d80766a4e0 -rf17ef475206dfa5950b19434ec135b157201c987 --- lams_common/.classpath (.../.classpath) (revision ea6e9356bb158005dd1536639b0c43d80766a4e0) +++ lams_common/.classpath (.../.classpath) (revision f17ef475206dfa5950b19434ec135b157201c987) @@ -6,5 +6,6 @@ + Index: lams_common/src/java/org/lamsfoundation/lams/commonContext.xml =================================================================== diff -u -r2071fadc773d0ee55e6302e4700ab3f5ada403cb -rf17ef475206dfa5950b19434ec135b157201c987 --- lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision 2071fadc773d0ee55e6302e4700ab3f5ada403cb) +++ lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision f17ef475206dfa5950b19434ec135b157201c987) @@ -145,6 +145,7 @@ + @@ -160,7 +161,7 @@ PROPAGATION_REQUIRED PROPAGATION_REQUIRED - PROPAGATION_REQUIRED,readOnly + PROPAGATION_REQUIRED Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java =================================================================== diff -u -rbebb1babe03164359747ae3b7abac81d05ea7d6c -rf17ef475206dfa5950b19434ec135b157201c987 --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java (.../ExportToolContentService.java) (revision bebb1babe03164359747ae3b7abac81d05ea7d6c) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/ExportToolContentService.java (.../ExportToolContentService.java) (revision f17ef475206dfa5950b19434ec135b157201c987) @@ -28,19 +28,39 @@ import java.io.FileWriter; import java.io.IOException; import java.io.Writer; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.beanutils.BeanUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.math.NumberUtils; import org.apache.log4j.Logger; +import org.lamsfoundation.lams.contentrepository.ItemNotFoundException; +import org.lamsfoundation.lams.contentrepository.RepositoryCheckedException; +import org.lamsfoundation.lams.contentrepository.client.IToolContentHandler; import org.lamsfoundation.lams.learningdesign.dao.hibernate.ActivityDAO; +import org.lamsfoundation.lams.learningdesign.dto.AuthoringActivityDTO; import org.lamsfoundation.lams.learningdesign.dto.LearningDesignDTO; +import org.lamsfoundation.lams.tool.Tool; +import org.lamsfoundation.lams.tool.ToolContentManager; +import org.lamsfoundation.lams.tool.dao.hibernate.ToolDAO; +import org.lamsfoundation.lams.tool.exception.DataMissingException; +import org.lamsfoundation.lams.tool.exception.ToolException; import org.lamsfoundation.lams.util.FileUtil; import org.lamsfoundation.lams.util.FileUtilException; import org.lamsfoundation.lams.util.zipfile.ZipFileUtil; import org.lamsfoundation.lams.util.zipfile.ZipFileUtilException; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.Converter; /** * Export tool content service bean. * @author Steve.Ni @@ -51,42 +71,176 @@ public static final String LEARNING_DESIGN_SERVICE_BEAN_NAME = "learningDesignService"; //export tool content zip file prefix public static final String EXPORT_TOOLCONTNET_ZIP_PREFIX = "lams_toolcontent_"; - public static final String EXPORT_TOOLCONTNET_CONTENT_FOLDER = "content"; public static final String EXPORT_TOOLCONTNET_FOLDER_SUFFIX = "export_toolcontent"; public static final String EXPORT_TOOLCONTNET_ZIP_SUFFIX = ".zip"; public static final String LEARNING_DESIGN_FILE_NAME = "learning_design.xml"; + private static final String TOOL_FILE_NAME = "tool.xml"; private Logger log = Logger.getLogger(ExportToolContentService.class); private ApplicationContext applicationContext; + //save list of all tool file node class information. One tool may have over one file node, such as + //in share resource tool, it has contnent attachment and shared resource item attachement. + private List fileHandleClassList; + //spring injection properties private ActivityDAO activityDAO; + private ToolDAO toolDAO; /** - * @see org.lamsfoundation.lams.authoring.service.IExportToolContentService.exportToolContent(Long) + * Class of tool attachment file handler information container. */ - public String exportToolContent(Long learningDesignId) throws ExportToolContentException{ + private class FileHandleClassInfo{ + + //the Class instance according to className. + public Class handlerClass; + public String className; + public String uuidFieldName; + public String versionFieldName; + + public FileHandleClassInfo(String className, String uuidFieldName, String versionFieldName){ + this.className = className; + this.uuidFieldName = uuidFieldName; + this.versionFieldName = versionFieldName; + } + } + + + /** + * File node information container. + */ + private class FileNodeInfo{ + private Long fileUuid; + private Long fileVersionId; + + public FileNodeInfo(Long uuid, Long versionId){ + this.fileUuid = uuid; + this.fileVersionId = versionId; + } + public Long getFileUuid() { + return fileUuid; + } + public void setFileUuid(Long fileUuid) { + this.fileUuid = fileUuid; + } + public Long getFileVersionId() { + return fileVersionId; + } + public void setFileVersionId(Long fileVersionId) { + this.fileVersionId = fileVersionId; + } + + } + /** + * Proxy class for Default XStream converter. + * + */ + private class FileInvocationHandler implements InvocationHandler{ + + private Object obj; + private List fileNodes; + private List fileHandleClassList; + public FileInvocationHandler(Object obj){ + this.obj = obj; + this.fileNodes = new ArrayList(); + } + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object result; + try { + + if(StringUtils.equals(method.getName(),"marshal")){ + for(FileHandleClassInfo info:fileHandleClassList){ + if(args[0] != null && info.className.equals((args[0].getClass().getName()))){ + Long uuid = NumberUtils.createLong(BeanUtils.getProperty(args[0],info.uuidFieldName)); + Long version = NumberUtils.createLong(BeanUtils.getProperty(args[0],info.versionFieldName)); + log.debug("XStream get file node ["+uuid +"," + version +"]."); + fileNodes.add(ExportToolContentService.this.new FileNodeInfo(uuid,version)); + } + } + } + if(StringUtils.equals(method.getName(),"canConvert")){ + boolean flag = false; + for(FileHandleClassInfo info:fileHandleClassList){ + if(args[0] != null && info.className.equals(((Class)args[0]).getName())){ + log.debug("XStream will handle ["+info.className+"] as file node class."); + flag = true; + break; + } + } + return flag; + } + result = method.invoke(obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (Exception e) { + throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); + } + + return result; + } + + public List getFileNodes() { + return fileNodes; + } + public List getFileHandleClassList() { + return fileHandleClassList; + } + public void setFileHandleClassList(List fileHandleClassList) { + this.fileHandleClassList = fileHandleClassList; + + //initial class instance. + for(FileHandleClassInfo info:fileHandleClassList){ + try { + info.handlerClass = Class.forName(info.className); + } catch (ClassNotFoundException e) { + throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); + } + } + } + } + /** + * Default contructor method. + */ + public ExportToolContentService(){ + fileHandleClassList = new ArrayList(); + } + /** + * @see org.lamsfoundation.lams.authoring.service.IExportToolContentService.exportLearningDesign(Long) + */ + public String exportLearningDesign(Long learningDesignId) throws ExportToolContentException{ try { //root temp directory, put target zip file String rootDir = FileUtil.createTempDirectory(EXPORT_TOOLCONTNET_FOLDER_SUFFIX); - //content directory to put all toolContent, root file is learning design xml. - String contentDir = getFullPath(rootDir,EXPORT_TOOLCONTNET_CONTENT_FOLDER); - if(!(new File(contentDir).mkdir())) - throw new ExportToolContentException("Unable to create content directory."); + String contentDir = rootDir + File.separator + "content"; + FileUtil.createDirectory(contentDir); - String targetZipFileName = getFullPath(rootDir, EXPORT_TOOLCONTNET_ZIP_PREFIX - + learningDesignId + EXPORT_TOOLCONTNET_ZIP_SUFFIX); + //zip file name with full path + String targetZipFileName = EXPORT_TOOLCONTNET_ZIP_PREFIX + learningDesignId + EXPORT_TOOLCONTNET_ZIP_SUFFIX; + //learing design file name with full path String ldFileName = getFullPath(contentDir,LEARNING_DESIGN_FILE_NAME); Writer ldFile = new FileWriter(new File(ldFileName)); + //get learning desing and serialize it to XML file. ILearningDesignService service = getLearningDesignService(); LearningDesignDTO ldDto = service.getLearningDesignDTO(learningDesignId); - XStream xstream = new XStream(); - xstream.toXML(ldDto,ldFile); + XStream designXml = new XStream(); + designXml.toXML(ldDto,ldFile); + log.debug("Learning design xml export success"); - return ZipFileUtil.createZipFile(targetZipFileName, rootDir); + //iterator all activities in this learning design and export their content to given folder. + //The content will contain tool.xml and attachment files of tools from LAMS repository. + List activities = ldDto.getActivities(); + for(AuthoringActivityDTO activity : activities){ + ToolContentManager contentManager = (ToolContentManager) findToolService(toolDAO.getToolByID(activity.getToolID())); + log.debug("Tool export content : " + activity.getTitle() +" by contentID :" + activity.getToolContentID()); + contentManager.exportToolContent(activity.getToolContentID(),contentDir); + } + + log.debug("Create export content target zip file. File name is " + targetZipFileName); + //create zip file and return zip full file name + return ZipFileUtil.createZipFile(targetZipFileName, contentDir,rootDir); } catch (FileUtilException e) { log.error("FileUtilExcpetion:" + e.toString()); throw new ExportToolContentException(e); @@ -96,9 +250,63 @@ } catch (IOException e) { log.error("IOException:" + e.toString()); throw new ExportToolContentException(e); + } catch (DataMissingException e) { + log.error("DataMissingException:" + e.toString()); + throw new ExportToolContentException(e); + } catch (ToolException e) { + log.error("ToolException:" + e.toString()); + throw new ExportToolContentException(e); } } - + /** + * @throws ExportToolContentException + * + */ + public void exportToolContent(Long toolContentId, Object toolContentObj, IToolContentHandler toolContentHandler, String toPath) + throws ExportToolContentException { + try { + //create tool's save path + String toolPath = toPath+File.separator+toolContentId; + FileUtil.createDirectory(toolPath); + + //create tool xml file name : tool.xml + String toolFileName = getFullPath(toolPath,TOOL_FILE_NAME); + Writer toolFile = new FileWriter(new File(toolFileName)); + + //serialize tool xml into local file. + XStream toolXml = new XStream(); + Converter c = toolXml.getConverterLookup().defaultConverter(); + FileInvocationHandler handler = new FileInvocationHandler(c); + handler.setFileHandleClassList(fileHandleClassList); + Converter myc = (Converter) Proxy.newProxyInstance(c.getClass().getClassLoader(),new Class[]{Converter.class},handler); + toolXml.registerConverter(myc); + toolXml.toXML(toolContentObj,toolFile); + + //get out the fileNodes + List list = handler.getFileNodes(); + for(FileNodeInfo fileNode:list){ + log.debug("Tool attachement file is going to save : " + fileNode.getFileUuid()); + toolContentHandler.saveFile(fileNode.getFileUuid(),toolPath+File.separator+fileNode.getFileUuid()); + } + list.clear(); + } catch (ItemNotFoundException e) { + throw new ExportToolContentException(e); + } catch (RepositoryCheckedException e) { + throw new ExportToolContentException(e); + } catch (IOException e) { + throw new ExportToolContentException(e); + } catch (FileUtilException e) { + throw new ExportToolContentException(e); + } finally{ + if(fileHandleClassList != null) + fileHandleClassList.clear(); + } + + } + public void registerFileHandleClass(String fileNodeClassName,String fileUuidFieldName, String fileVersionFieldName){ + fileHandleClassList.add(this.new FileHandleClassInfo(fileNodeClassName,fileUuidFieldName,fileVersionFieldName)); + + } //****************************************************************** // ApplicationContextAware method implementation //****************************************************************** @@ -113,9 +321,15 @@ } private String getFullPath(String path, String file){ - return path + File.separator + file; + if(path.endsWith(File.separator)) + return path + file; + else + return path + File.separator + file; } - + private Object findToolService(Tool tool) throws NoSuchBeanDefinitionException + { + return applicationContext.getBean(tool.getServiceName()); + } //****************************************************************** // Spring injection properties set/get //****************************************************************** @@ -125,4 +339,12 @@ public void setActivityDAO(ActivityDAO activityDAO) { this.activityDAO = activityDAO; } + public ToolDAO getToolDAO() { + return toolDAO; + } + public void setToolDAO(ToolDAO toolDAO) { + this.toolDAO = toolDAO; + } + + } Index: lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/IExportToolContentService.java =================================================================== diff -u -rbebb1babe03164359747ae3b7abac81d05ea7d6c -rf17ef475206dfa5950b19434ec135b157201c987 --- lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/IExportToolContentService.java (.../IExportToolContentService.java) (revision bebb1babe03164359747ae3b7abac81d05ea7d6c) +++ lams_common/src/java/org/lamsfoundation/lams/learningdesign/service/IExportToolContentService.java (.../IExportToolContentService.java) (revision f17ef475206dfa5950b19434ec135b157201c987) @@ -24,14 +24,55 @@ /* $Id$ */ package org.lamsfoundation.lams.learningdesign.service; - +import org.lamsfoundation.lams.contentrepository.client.IToolContentHandler; +/** + * Export tool content service provides ability to export learning design and its relative activities' tool content. + * + * @author Steve.Ni + * @version $Revision$ + */ public interface IExportToolContentService { /** - * Export given learning design tool content. It includes all tools content in this learning design. + * Export given learning design tool content. It includes all tools content + * in this learning design. * * @param learningDesignId * @return * @throws ExportToolContentException */ - String exportToolContent(Long learningDesignId) throws ExportToolContentException; + String exportLearningDesign(Long learningDesignId) throws ExportToolContentException; + /** + * Export tool content. + * + * @param toolContentId the tool content ID. + * @param toolContentObj The POJO object to descript the tool content which need to export + * @param toolContentHandler need be used when this tool has any items in LAMS repository to be exported. + * @param toPath the target local file system directory to put tool.xml and its attached files or package. + * + * @throws ExportToolContentException + */ + void exportToolContent(Long toolContentId, Object toolContentObj, IToolContentHandler toolContentHandler, String toPath) + throws ExportToolContentException; + + /** + * Register access class and relative method for item in LAMS repository. For example, there is POJO to refer to + * a tool offline instruction files: + *
+	 * 
+	 * public class ShareResourceToolFile{
+	 * 	private Long fileUuid;
+	 * 	private Long fileVersionId;
+	 *  ...
+	 *  //set&get methods
+	 * }
+	 * 
+	 * 
+ * + * Tool must call this method to tell export service to get item by this fileUuid and fileVersionId properties. + * + * @param fileNodeClassName The POJO class name for repository item reference + * @param fileUuidFieldName The POJO properties name for get fileUuid. There must be a get method to access this property. + * @param fileVersionFieldName The POJO properties name for get fileVersion. There must be a get method to access this property. + */ + void registerFileHandleClass(String fileNodeClassName,String fileUuidFieldName, String fileVersionFieldName); } Index: lams_common/src/java/org/lamsfoundation/lams/tool/ToolContentManager.java =================================================================== diff -u -r6a4cc1cba568d6ed7af70248ad7f89b757e8f468 -rf17ef475206dfa5950b19434ec135b157201c987 --- lams_common/src/java/org/lamsfoundation/lams/tool/ToolContentManager.java (.../ToolContentManager.java) (revision 6a4cc1cba568d6ed7af70248ad7f89b757e8f468) +++ lams_common/src/java/org/lamsfoundation/lams/tool/ToolContentManager.java (.../ToolContentManager.java) (revision f17ef475206dfa5950b19434ec135b157201c987) @@ -103,15 +103,15 @@ * @throws DataMissingException if no tool content matches the toolSessionId * @throws ToolException if any other error occurs */ - public String exportToolContent(Long toolContentId) + public void exportToolContent(Long toolContentId, String toPath) throws DataMissingException, ToolException; /** * Import the XML fragment for the tool's content, along with any files needed * for the content. * @throws ToolException if any other error occurs */ - public String importToolContent(Long toolContentId, String reference, String directory) + public void importToolContent(Object toolContnetPOJO) throws ToolException; Index: lams_common/src/java/org/lamsfoundation/lams/util/FileUtil.java =================================================================== diff -u -r34b959260a0f8f8285793a4481a95ca3580eabc5 -rf17ef475206dfa5950b19434ec135b157201c987 --- lams_common/src/java/org/lamsfoundation/lams/util/FileUtil.java (.../FileUtil.java) (revision 34b959260a0f8f8285793a4481a95ca3580eabc5) +++ lams_common/src/java/org/lamsfoundation/lams/util/FileUtil.java (.../FileUtil.java) (revision f17ef475206dfa5950b19434ec135b157201c987) @@ -288,4 +288,48 @@ } return dumpFilename; } + /** + * get file name from a string which may include directory information. + * For example : "c:\\dir\\ndp\\pp.txt"; will return pp.txt.? + * If file has no path infomation, then just return input fileName. + * + */ + public static String getFileName(String fileName){ + if(fileName == null) + return ""; + + fileName = fileName.trim(); + + int dotPos = fileName.lastIndexOf(File.separatorChar); + if (dotPos == -1){ + //just for window OS: it also can use "/" as file separatorChar. + dotPos = fileName.lastIndexOf("/"); + if(dotPos == -1) + return fileName; + } + return fileName.substring(dotPos + 1, fileName.length()); + + } + /** + * Get file directory info. + * @param fileName with path info. + * @return return only path info with the given fileName + */ + public static String getFileDirectory(String fileName){ + if(fileName == null) + return ""; + + fileName = fileName.trim(); + + int dotPos = fileName.lastIndexOf(File.separatorChar); + if (dotPos == -1){ + //just for window OS: it also can use "/" as file separatorChar. + dotPos = fileName.lastIndexOf("/"); + if(dotPos == -1) + return fileName; + } + //return the last char is '/' + return fileName.substring(0,dotPos+1); + + } }