All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dspace.app.itemimport.ItemImportServiceImpl Maven / Gradle / Ivy

There is a newer version: 8.0
Show newest version
/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.app.itemimport;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.mail.MessagingException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.commons.collections4.ComparatorUtils;
import org.apache.commons.io.FileDeleteStrategy;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.Logger;
import org.apache.xpath.XPathAPI;
import org.dspace.app.itemimport.service.ItemImportService;
import org.dspace.app.util.LocalSchemaFilenameFilter;
import org.dspace.app.util.RelationshipUtils;
import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.ResourcePolicy;
import org.dspace.authorize.service.AuthorizeService;
import org.dspace.authorize.service.ResourcePolicyService;
import org.dspace.content.Bitstream;
import org.dspace.content.BitstreamFormat;
import org.dspace.content.Bundle;
import org.dspace.content.Collection;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataSchema;
import org.dspace.content.MetadataSchemaEnum;
import org.dspace.content.MetadataValue;
import org.dspace.content.Relationship;
import org.dspace.content.RelationshipType;
import org.dspace.content.WorkspaceItem;
import org.dspace.content.service.BitstreamFormatService;
import org.dspace.content.service.BitstreamService;
import org.dspace.content.service.BundleService;
import org.dspace.content.service.CollectionService;
import org.dspace.content.service.InstallItemService;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.MetadataFieldService;
import org.dspace.content.service.MetadataSchemaService;
import org.dspace.content.service.MetadataValueService;
import org.dspace.content.service.RelationshipService;
import org.dspace.content.service.RelationshipTypeService;
import org.dspace.content.service.WorkspaceItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.Email;
import org.dspace.core.I18nUtil;
import org.dspace.core.LogHelper;
import org.dspace.eperson.EPerson;
import org.dspace.eperson.Group;
import org.dspace.eperson.service.EPersonService;
import org.dspace.eperson.service.GroupService;
import org.dspace.handle.service.HandleService;
import org.dspace.services.ConfigurationService;
import org.dspace.workflow.WorkflowItem;
import org.dspace.workflow.WorkflowService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


/**
 * Import items into DSpace. The conventional use is upload files by copying
 * them. DSpace writes the item's bitstreams into its assetstore. Metadata is
 * also loaded to the DSpace database.
 * 

* A second use assumes the bitstream files already exist in a storage * resource accessible to DSpace. In this case the bitstreams are 'registered'. * That is, the metadata is loaded to the DSpace database and DSpace is given * the location of the file which is subsumed into DSpace. *

* The distinction is controlled by the format of lines in the 'contents' file. * See comments in processContentsFile() below. *

* Modified by David Little, UCSD Libraries 12/21/04 to * allow the registration of files (bitstreams) into DSpace. */ public class ItemImportServiceImpl implements ItemImportService, InitializingBean { private final Logger log = org.apache.logging.log4j.LogManager.getLogger(ItemImportServiceImpl.class); @Autowired(required = true) protected AuthorizeService authorizeService; @Autowired(required = true) protected BitstreamService bitstreamService; @Autowired(required = true) protected BitstreamFormatService bitstreamFormatService; @Autowired(required = true) protected BundleService bundleService; @Autowired(required = true) protected CollectionService collectionService; @Autowired(required = true) protected EPersonService ePersonService; @Autowired(required = true) protected HandleService handleService; @Autowired(required = true) protected ItemService itemService; @Autowired(required = true) protected InstallItemService installItemService; @Autowired(required = true) protected GroupService groupService; @Autowired(required = true) protected MetadataFieldService metadataFieldService; @Autowired(required = true) protected MetadataSchemaService metadataSchemaService; @Autowired(required = true) protected ResourcePolicyService resourcePolicyService; @Autowired(required = true) protected WorkspaceItemService workspaceItemService; @Autowired(required = true) protected WorkflowService workflowService; @Autowired(required = true) protected ConfigurationService configurationService; @Autowired(required = true) protected RelationshipService relationshipService; @Autowired(required = true) protected RelationshipTypeService relationshipTypeService; @Autowired(required = true) protected MetadataValueService metadataValueService; protected String tempWorkDir; protected boolean isTest = false; protected boolean isResume = false; protected boolean useWorkflow = false; protected boolean useWorkflowSendEmail = false; protected boolean isQuiet = false; //remember which folder item was imported from Map itemFolderMap = null; @Override public void afterPropertiesSet() throws Exception { tempWorkDir = configurationService.getProperty("org.dspace.app.batchitemimport.work.dir"); //Ensure tempWorkDir exists File tempWorkDirFile = new File(tempWorkDir); if (!tempWorkDirFile.exists()) { boolean success = tempWorkDirFile.mkdir(); if (success) { log.info("Created org.dspace.app.batchitemimport.work.dir of: " + tempWorkDir); } else { log.error("Cannot create batch import directory! " + tempWorkDir); } } } // File listing filter to look for metadata files protected FilenameFilter metadataFileFilter = new LocalSchemaFilenameFilter(); // File listing filter to check for folders protected FilenameFilter directoryFilter = new FilenameFilter() { @Override public boolean accept(File dir, String n) { File item = new File(dir.getAbsolutePath() + File.separatorChar + n); return item.isDirectory(); } }; protected ItemImportServiceImpl() { //Protected consumer to ensure that we use spring to create a bean, NEVER make this public } @Override public void addItemsAtomic(Context c, List mycollections, String sourceDir, String mapFile, boolean template) throws Exception { try { addItems(c, mycollections, sourceDir, mapFile, template); } catch (Exception addException) { log.error("AddItems encountered an error, will try to revert. Error: " + addException.getMessage()); deleteItems(c, mapFile); log.info("Attempted to delete partial (errored) import"); throw addException; } } @Override public void addItems(Context c, List mycollections, String sourceDir, String mapFile, boolean template) throws Exception { // create the mapfile File outFile = null; PrintWriter mapOut = null; try { Map skipItems = new HashMap<>(); // set of items to skip if in 'resume' // mode itemFolderMap = new HashMap<>(); System.out.println("Adding items from directory: " + sourceDir); log.debug("Adding items from directory: " + sourceDir); System.out.println("Generating mapfile: " + mapFile); log.debug("Generating mapfile: " + mapFile); boolean directoryFileCollections = false; if (mycollections == null) { directoryFileCollections = true; } if (!isTest) { // get the directory names of items to skip (will be in keys of // hash) if (isResume) { skipItems = readMapFile(mapFile); } // sneaky isResume == true means open file in append mode outFile = new File(mapFile); mapOut = new PrintWriter(new FileWriter(outFile, isResume)); if (mapOut == null) { throw new Exception("can't open mapfile: " + mapFile); } } // open and process the source directory File d = new java.io.File(sourceDir); if (d == null || !d.isDirectory()) { throw new Exception("Error, cannot open source directory " + sourceDir); } String[] dircontents = d.list(directoryFilter); Arrays.sort(dircontents, ComparatorUtils.naturalComparator()); for (int i = 0; i < dircontents.length; i++) { if (skipItems.containsKey(dircontents[i])) { System.out.println("Skipping import of " + dircontents[i]); //we still need the item in the map for relationship linking String skippedHandle = skipItems.get(dircontents[i]); Item skippedItem = (Item) handleService.resolveToObject(c, skippedHandle); itemFolderMap.put(dircontents[i], skippedItem); } else { List clist; if (directoryFileCollections) { String path = sourceDir + File.separatorChar + dircontents[i]; try { List cols = processCollectionFile(c, path, "collections"); if (cols == null) { System.out .println("No collections specified for item " + dircontents[i] + ". Skipping."); continue; } clist = cols; } catch (IllegalArgumentException e) { System.out.println(e.getMessage() + " Skipping."); continue; } } else { clist = mycollections; } Item item = addItem(c, clist, sourceDir, dircontents[i], mapOut, template); itemFolderMap.put(dircontents[i], item); c.uncacheEntity(item); System.out.println(i + " " + dircontents[i]); } } //now that all items are imported, iterate again to link relationships addRelationships(c, sourceDir); } finally { if (mapOut != null) { mapOut.flush(); mapOut.close(); } } } /** * Add relationships from a 'relationships' manifest file. * * @param c Context * @param sourceDir The parent import source directory * @throws Exception */ protected void addRelationships(Context c, String sourceDir) throws Exception { for (Map.Entry itemEntry : itemFolderMap.entrySet()) { String folderName = itemEntry.getKey(); String path = sourceDir + File.separatorChar + folderName; Item item = itemEntry.getValue(); //look for a 'relationship' manifest Map> relationships = processRelationshipFile(path, "relationships"); if (!relationships.isEmpty()) { for (Map.Entry> relEntry : relationships.entrySet()) { String relationshipType = relEntry.getKey(); List identifierList = relEntry.getValue(); for (String itemIdentifier : identifierList) { if (isTest) { System.out.println("\tAdding relationship (type: " + relationshipType + ") from " + folderName + " to " + itemIdentifier); continue; } //find referenced item Item relationItem = resolveRelatedItem(c, itemIdentifier); if (null == relationItem) { throw new Exception("Could not find item for " + itemIdentifier); } //get entity type of entity and item String itemEntityType = getEntityType(item); String relatedEntityType = getEntityType(relationItem); //find matching relationship type List relTypes = relationshipTypeService.findByLeftwardOrRightwardTypeName( c, relationshipType); RelationshipType foundRelationshipType = RelationshipUtils.matchRelationshipType( relTypes, relatedEntityType, itemEntityType, relationshipType); if (foundRelationshipType == null) { throw new Exception("No Relationship type found for:\n" + "Target type: " + relatedEntityType + "\n" + "Origin referer type: " + itemEntityType + "\n" + "with typeName: " + relationshipType ); } boolean left = false; if (foundRelationshipType.getLeftwardType().equalsIgnoreCase(relationshipType)) { left = true; } // Placeholder items for relation placing Item leftItem = null; Item rightItem = null; if (left) { leftItem = item; rightItem = relationItem; } else { leftItem = relationItem; rightItem = item; } // Create the relationship int leftPlace = relationshipService.findNextLeftPlaceByLeftItem(c, leftItem); int rightPlace = relationshipService.findNextRightPlaceByRightItem(c, rightItem); Relationship persistedRelationship = relationshipService.create( c, leftItem, rightItem, foundRelationshipType, leftPlace, rightPlace); // relationshipService.update(c, persistedRelationship); System.out.println("\tAdded relationship (type: " + relationshipType + ") from " + leftItem.getHandle() + " to " + rightItem.getHandle()); } } } } } /** * Get the item's entity type from meta. * * @param item * @return */ protected String getEntityType(Item item) throws Exception { return itemService.getMetadata(item, "dspace", "entity", "type", Item.ANY).get(0).getValue(); } /** * Read the relationship manifest file. * * Each line in the file contains a relationship type id and an item identifier in the following format: * * relation. * * The input_item_folder should refer the folder name of another item in this import batch. * * @param path The main import folder path. * @param filename The name of the manifest file to check ('relationships') * @return Map of found relationships * @throws Exception */ protected Map> processRelationshipFile(String path, String filename) throws Exception { File file = new File(path + File.separatorChar + filename); Map> result = new HashMap<>(); if (file.exists()) { System.out.println("\tProcessing relationships file: " + filename); BufferedReader br = null; try { br = new BufferedReader(new FileReader(file)); String line = null; while ((line = br.readLine()) != null) { line = line.trim(); if ("".equals(line)) { continue; } String relationshipType = null; String itemIdentifier = null; StringTokenizer st = new StringTokenizer(line); if (st.hasMoreTokens()) { relationshipType = st.nextToken(); if (relationshipType.split("\\.").length > 1) { relationshipType = relationshipType.split("\\.")[1]; } } else { throw new Exception("Bad mapfile line:\n" + line); } if (st.hasMoreTokens()) { itemIdentifier = st.nextToken("").trim(); } else { throw new Exception("Bad mapfile line:\n" + line); } if (!result.containsKey(relationshipType)) { result.put(relationshipType, new ArrayList<>()); } result.get(relationshipType).add(itemIdentifier); } } catch (FileNotFoundException e) { System.out.println("\tNo relationships file found."); } finally { if (br != null) { try { br.close(); } catch (IOException e) { System.out.println("Non-critical problem releasing resources."); } } } } return result; } /** * Resolve an item identifier referred to in the relationships manifest file. * * The import item map will be checked first to see if the identifier refers to an item folder * that was just imported. Next it will try to find the item by handle or UUID, or by a unique * meta value. * * @param c Context * @param itemIdentifier The identifier string found in the import manifest (handle, uuid, or import subfolder) * @return Item if found, or null. * @throws Exception */ protected Item resolveRelatedItem(Context c, String itemIdentifier) throws Exception { if (itemIdentifier.contains(":")) { if (itemIdentifier.startsWith("folderName:") || itemIdentifier.startsWith("rowName:")) { //identifier refers to a folder name in this import int i = itemIdentifier.indexOf(":"); String folderName = itemIdentifier.substring(i + 1); if (itemFolderMap.containsKey(folderName)) { return itemFolderMap.get(folderName); } } else { //lookup by meta value int i = itemIdentifier.indexOf(":"); String metaKey = itemIdentifier.substring(0, i); String metaValue = itemIdentifier.substring(i + 1); return findItemByMetaValue(c, metaKey, metaValue); } } else if (itemIdentifier.indexOf('/') != -1) { //resolve by handle return (Item) handleService.resolveToObject(c, itemIdentifier); } else { //try to resolve by UUID return itemService.findByIdOrLegacyId(c, itemIdentifier); } return null; } /** * Lookup an item by a (unique) meta value. * * @param metaKey * @param metaValue * @return Item * @throws Exception if single item not found. */ protected Item findItemByMetaValue(Context c, String metaKey, String metaValue) throws Exception { Item item = null; String mf[] = metaKey.split("\\."); if (mf.length < 2) { throw new Exception("Bad metadata field in reference: '" + metaKey + "' (expected syntax is schema.element[.qualifier])"); } String schema = mf[0]; String element = mf[1]; String qualifier = mf.length == 2 ? null : mf[2]; try { MetadataField mfo = metadataFieldService.findByElement(c, schema, element, qualifier); Iterator mdv = metadataValueService.findByFieldAndValue(c, mfo, metaValue); if (mdv.hasNext()) { MetadataValue mdvVal = mdv.next(); UUID uuid = mdvVal.getDSpaceObject().getID(); if (mdv.hasNext()) { throw new Exception("Ambiguous reference; multiple matches in db: " + metaKey); } item = itemService.find(c, uuid); } } catch (SQLException e) { throw new Exception("Error looking up item by metadata reference: " + metaKey, e); } if (item == null) { throw new Exception("Item not found by metadata reference: " + metaKey); } return item; } @Override public void replaceItems(Context c, List mycollections, String sourceDir, String mapFile, boolean template) throws Exception { // verify the source directory File d = new java.io.File(sourceDir); if (d == null || !d.isDirectory()) { throw new Exception("Error, cannot open source directory " + sourceDir); } // read in HashMap first, to get list of handles & source dirs Map myHash = readMapFile(mapFile); // for each handle, re-import the item, discard the new handle // and re-assign the old handle for (Map.Entry mapEntry : myHash.entrySet()) { // get the old handle String newItemName = mapEntry.getKey(); String oldHandle = mapEntry.getValue(); Item oldItem = null; if (oldHandle.indexOf('/') != -1) { System.out.println("\tReplacing: " + oldHandle); // add new item, locate old one oldItem = (Item) handleService.resolveToObject(c, oldHandle); } else { oldItem = itemService.findByIdOrLegacyId(c, oldHandle); } /* Rather than exposing public item methods to change handles -- * two handles can't exist at the same time due to key constraints * so would require temp handle being stored, old being copied to new and * new being copied to old, all a bit messy -- a handle file is written to * the import directory containing the old handle, the existing item is * deleted and then the import runs as though it were loading an item which * had already been assigned a handle (so a new handle is not even assigned). * As a commit does not occur until after a successful add, it is safe to * do a delete as any error results in an aborted transaction without harming * the original item */ File handleFile = new File(sourceDir + File.separatorChar + newItemName + File.separatorChar + "handle"); PrintWriter handleOut = new PrintWriter(new FileWriter(handleFile, true)); if (handleOut == null) { throw new Exception("can't open handle file: " + handleFile.getCanonicalPath()); } handleOut.println(oldHandle); handleOut.close(); deleteItem(c, oldItem); Item newItem = addItem(c, mycollections, sourceDir, newItemName, null, template); c.uncacheEntity(oldItem); c.uncacheEntity(newItem); } } @Override public void deleteItems(Context c, String mapFile) throws Exception { System.out.println("Deleting items listed in mapfile: " + mapFile); // read in the mapfile Map myhash = readMapFile(mapFile); // now delete everything that appeared in the mapFile Iterator i = myhash.keySet().iterator(); while (i.hasNext()) { String itemID = myhash.get(i.next()); if (itemID.indexOf('/') != -1) { String myhandle = itemID; System.out.println("Deleting item " + myhandle); deleteItem(c, myhandle); } else { // it's an ID Item myitem = itemService.findByIdOrLegacyId(c, itemID); System.out.println("Deleting item " + itemID); deleteItem(c, myitem); c.uncacheEntity(myitem); } } } /** * item? try and add it to the archive. * * @param c current Context * @param mycollections - add item to these Collections. * @param path - directory containing the item directories. * @param itemname handle - non-null means we have a pre-defined handle already * @param mapOut - mapfile we're writing * @param template whether to use collection template item as starting point * @return Item * @throws Exception if error occurs */ protected Item addItem(Context c, List mycollections, String path, String itemname, PrintWriter mapOut, boolean template) throws Exception { String mapOutputString = null; System.out.println("Adding item from directory " + itemname); log.debug("adding item from directory " + itemname); // create workspace item Item myitem = null; WorkspaceItem wi = null; WorkflowItem wfi = null; if (!isTest) { wi = workspaceItemService.create(c, mycollections.iterator().next(), template); myitem = wi.getItem(); } // now fill out dublin core for item loadMetadata(c, myitem, path + File.separatorChar + itemname + File.separatorChar); // and the bitstreams from the contents file // process contents file, add bistreams and bundles, return any // non-standard permissions List options = processContentsFile(c, myitem, path + File.separatorChar + itemname, "contents"); if (useWorkflow) { // don't process handle file // start up a workflow if (!isTest) { // Should we send a workflow alert email or not? if (useWorkflowSendEmail) { wfi = workflowService.start(c, wi); } else { wfi = workflowService.startWithoutNotify(c, wi); } // send ID to the mapfile mapOutputString = itemname + " " + myitem.getID(); } } else { // only process handle file if not using workflow system String myhandle = processHandleFile(c, myitem, path + File.separatorChar + itemname, "handle"); // put item in system if (!isTest) { try { installItemService.installItem(c, wi, myhandle); } catch (Exception e) { workspaceItemService.deleteAll(c, wi); log.error("Exception after install item, try to revert...", e); throw e; } // find the handle, and output to map file myhandle = handleService.findHandle(c, myitem); mapOutputString = itemname + " " + myhandle; } // set permissions if specified in contents file if (options.size() > 0) { System.out.println("Processing options"); processOptions(c, myitem, options); } } // now add to multiple collections if requested if (mycollections.size() > 1) { for (int i = 1; i < mycollections.size(); i++) { if (!isTest) { collectionService.addItem(c, mycollections.get(i), myitem); } } } // made it this far, everything is fine, commit transaction if (mapOut != null) { mapOut.println(mapOutputString); } //Clear intermediary objects from the cache c.uncacheEntity(wi); c.uncacheEntity(wfi); return myitem; } // remove, given the actual item protected void deleteItem(Context c, Item myitem) throws Exception { if (!isTest) { ArrayList removeList = new ArrayList<>(); List collections = myitem.getCollections(); // Save items to be removed to prevent concurrent modification exception DS-3322 for (Collection collection : collections) { removeList.add(collection); } // Remove item from all the collections it's in for (Collection collection : removeList) { collectionService.removeItem(c, collection, myitem); } } } // remove, given a handle protected void deleteItem(Context c, String myhandle) throws Exception { // bit of a hack - to remove an item, you must remove it // from all collections it's a part of, then it will be removed Item myitem = (Item) handleService.resolveToObject(c, myhandle); if (myitem == null) { System.out.println("Error - cannot locate item - already deleted?"); } else { deleteItem(c, myitem); c.uncacheEntity(myitem); } } //////////////////////////////////// // utility methods //////////////////////////////////// // read in the map file and generate a hashmap of (file,handle) pairs protected Map readMapFile(String filename) throws Exception { Map myHash = new HashMap<>(); BufferedReader is = null; try { is = new BufferedReader(new FileReader(filename)); String line; while ((line = is.readLine()) != null) { String myFile; String myHandle; // a line should be archive filenamehandle StringTokenizer st = new StringTokenizer(line); if (st.hasMoreTokens()) { myFile = st.nextToken(); } else { throw new Exception("Bad mapfile line:\n" + line); } if (st.hasMoreTokens()) { myHandle = st.nextToken(); } else { throw new Exception("Bad mapfile line:\n" + line); } myHash.put(myFile, myHandle); } } finally { if (is != null) { is.close(); } } return myHash; } // Load all metadata schemas into the item. protected void loadMetadata(Context c, Item myitem, String path) throws SQLException, IOException, ParserConfigurationException, SAXException, TransformerException, AuthorizeException { // Load the dublin core metadata loadDublinCore(c, myitem, path + "dublin_core.xml"); // Load any additional metadata schemas File folder = new File(path); File file[] = folder.listFiles(metadataFileFilter); for (int i = 0; i < file.length; i++) { loadDublinCore(c, myitem, file[i].getAbsolutePath()); } } protected void loadDublinCore(Context c, Item myitem, String filename) throws SQLException, IOException, ParserConfigurationException, SAXException, TransformerException, AuthorizeException { Document document = loadXML(filename); // Get the schema, for backward compatibility we will default to the // dublin core schema if the schema name is not available in the import // file String schema; NodeList metadata = XPathAPI.selectNodeList(document, "/dublin_core"); Node schemaAttr = metadata.item(0).getAttributes().getNamedItem( "schema"); if (schemaAttr == null) { schema = MetadataSchemaEnum.DC.getName(); } else { schema = schemaAttr.getNodeValue(); } // Get the nodes corresponding to formats NodeList dcNodes = XPathAPI.selectNodeList(document, "/dublin_core/dcvalue"); if (!isQuiet) { System.out.println("\tLoading dublin core from " + filename); } // Add each one as a new format to the registry for (int i = 0; i < dcNodes.getLength(); i++) { Node n = dcNodes.item(i); addDCValue(c, myitem, schema, n); } } protected void addDCValue(Context c, Item i, String schema, Node n) throws TransformerException, SQLException, AuthorizeException { String value = getStringValue(n); //n.getNodeValue(); // compensate for empty value getting read as "null", which won't display if (value == null) { value = ""; } else { value = value.trim(); } // //getElementData(n, "element"); String element = getAttributeValue(n, "element"); String qualifier = getAttributeValue(n, "qualifier"); //NodeValue(); // //getElementData(n, // "qualifier"); String language = getAttributeValue(n, "language"); if (language != null) { language = language.trim(); } if (!isQuiet) { System.out.println("\tSchema: " + schema + " Element: " + element + " Qualifier: " + qualifier + " Value: " + value); } if ("none".equals(qualifier) || "".equals(qualifier)) { qualifier = null; } // only add metadata if it is no test and there is an actual value if (!isTest && !value.equals("")) { itemService.addMetadata(c, i, schema, element, qualifier, language, value); } else { // If we're just test the import, let's check that the actual metadata field exists. MetadataSchema foundSchema = metadataSchemaService.find(c, schema); if (foundSchema == null) { System.out.println("ERROR: schema '" + schema + "' was not found in the registry."); return; } MetadataField foundField = metadataFieldService.findByElement(c, foundSchema, element, qualifier); if (foundField == null) { System.out.println( "ERROR: Metadata field: '" + schema + "." + element + "." + qualifier + "' was not found in the " + "registry."); return; } } } /** * Read the collections file inside the item directory. If there * is one and it is not empty return a list of collections in * which the item should be inserted. If it does not exist or it * is empty return null. * * @param c The context * @param path The path to the data directory for this item * @param filename The collections file filename. Should be "collections" * @return A list of collections in which to insert the item or null * @throws IOException if IO error * @throws SQLException if database error */ protected List processCollectionFile(Context c, String path, String filename) throws IOException, SQLException { File file = new File(path + File.separatorChar + filename); ArrayList collections = new ArrayList<>(); List result = null; System.out.println("Processing collections file: " + filename); if (file.exists()) { BufferedReader br = null; try { br = new BufferedReader(new FileReader(file)); String line = null; while ((line = br.readLine()) != null) { DSpaceObject obj = null; if (line.indexOf('/') != -1) { obj = handleService.resolveToObject(c, line); if (obj == null || obj.getType() != Constants.COLLECTION) { obj = null; } } else { obj = collectionService.find(c, UUID.fromString(line)); } if (obj == null) { throw new IllegalArgumentException("Cannot resolve " + line + " to a collection."); } collections.add((Collection) obj); } result = collections; } catch (FileNotFoundException e) { System.out.println("No collections file found."); } finally { if (br != null) { try { br.close(); } catch (IOException e) { System.out.println("Non-critical problem releasing resources."); } } } } return result; } /** * Read in the handle file contents or return null if empty or doesn't exist * * @param c DSpace context * @param i DSpace item * @param path path to handle file * @param filename name of file * @return handle file contents or null if doesn't exist */ protected String processHandleFile(Context c, Item i, String path, String filename) { File file = new File(path + File.separatorChar + filename); String result = null; System.out.println("Processing handle file: " + filename); if (file.exists()) { BufferedReader is = null; try { is = new BufferedReader(new FileReader(file)); // result gets contents of file, or null result = is.readLine(); System.out.println("read handle: '" + result + "'"); } catch (FileNotFoundException e) { // probably no handle file, just return null System.out.println("It appears there is no handle file -- generating one"); } catch (IOException e) { // probably no handle file, just return null System.out.println("It appears there is no handle file -- generating one"); } finally { if (is != null) { try { is.close(); } catch (IOException e1) { System.err.println("Non-critical problem releasing resources."); } } } } else { // probably no handle file, just return null System.out.println("It appears there is no handle file -- generating one"); } return result; } /** * Given a contents file and an item, stuffing it with bitstreams from the * contents file Returns a List of Strings with lines from the contents * file that request non-default bitstream permission * * @param c DSpace Context * @param i DSpace item * @param path path as string * @param filename file name * @return List of Strings * @throws SQLException if database error * @throws IOException if IO error * @throws AuthorizeException if authorization error */ protected List processContentsFile(Context c, Item i, String path, String filename) throws SQLException, IOException, AuthorizeException { File contentsFile = new File(path + File.separatorChar + filename); String line = ""; List options = new ArrayList<>(); System.out.println("\tProcessing contents file: " + contentsFile); if (contentsFile.exists()) { BufferedReader is = null; try { is = new BufferedReader(new FileReader(contentsFile)); while ((line = is.readLine()) != null) { if ("".equals(line.trim())) { continue; } // 1) registered into dspace (leading -r) // 2) imported conventionally into dspace (no -r) if (line.trim().startsWith("-r ")) { // line should be one of these two: // -r -s n -f filepath // -r -s n -f filepath\tbundle:bundlename // where // n is the assetstore number // filepath is the path of the file to be registered // bundlename is an optional bundle name String sRegistrationLine = line.trim(); int iAssetstore = -1; String sFilePath = null; String sBundle = null; StringTokenizer tokenizer = new StringTokenizer(sRegistrationLine); while (tokenizer.hasMoreTokens()) { String sToken = tokenizer.nextToken(); if ("-r".equals(sToken)) { continue; } else if ("-s".equals(sToken) && tokenizer.hasMoreTokens()) { try { iAssetstore = Integer.parseInt(tokenizer.nextToken()); } catch (NumberFormatException e) { // ignore - iAssetstore remains -1 } } else if ("-f".equals(sToken) && tokenizer.hasMoreTokens()) { sFilePath = tokenizer.nextToken(); } else if (sToken.startsWith("bundle:")) { sBundle = sToken.substring(7); } else { // unrecognized token - should be no problem } } // while if (iAssetstore == -1 || sFilePath == null) { System.out.println("\tERROR: invalid contents file line"); System.out.println("\t\tSkipping line: " + sRegistrationLine); continue; } // look for descriptions boolean descriptionExists = false; String descriptionMarker = "\tdescription:"; int dMarkerIndex = line.indexOf(descriptionMarker); int dEndIndex = 0; if (dMarkerIndex > 0) { dEndIndex = line.indexOf("\t", dMarkerIndex + 1); if (dEndIndex == -1) { dEndIndex = line.length(); } descriptionExists = true; } String sDescription = ""; if (descriptionExists) { sDescription = line.substring(dMarkerIndex, dEndIndex); sDescription = sDescription.replaceFirst("description:", ""); } registerBitstream(c, i, iAssetstore, sFilePath, sBundle, sDescription); System.out.println("\tRegistering Bitstream: " + sFilePath + "\tAssetstore: " + iAssetstore + "\tBundle: " + sBundle + "\tDescription: " + sDescription); continue; // process next line in contents file } int bitstreamEndIndex = line.indexOf('\t'); if (bitstreamEndIndex == -1) { // no extra info processContentFileEntry(c, i, path, line, null, false); System.out.println("\tBitstream: " + line); } else { String bitstreamName = line.substring(0, bitstreamEndIndex); boolean bundleExists = false; boolean permissionsExist = false; boolean descriptionExists = false; // look for a bundle name String bundleMarker = "\tbundle:"; int bMarkerIndex = line.indexOf(bundleMarker); int bEndIndex = 0; if (bMarkerIndex > 0) { bEndIndex = line.indexOf("\t", bMarkerIndex + 1); if (bEndIndex == -1) { bEndIndex = line.length(); } bundleExists = true; } // look for permissions String permissionsMarker = "\tpermissions:"; int pMarkerIndex = line.indexOf(permissionsMarker); int pEndIndex = 0; if (pMarkerIndex > 0) { pEndIndex = line.indexOf("\t", pMarkerIndex + 1); if (pEndIndex == -1) { pEndIndex = line.length(); } permissionsExist = true; } // look for descriptions String descriptionMarker = "\tdescription:"; int dMarkerIndex = line.indexOf(descriptionMarker); int dEndIndex = 0; if (dMarkerIndex > 0) { dEndIndex = line.indexOf("\t", dMarkerIndex + 1); if (dEndIndex == -1) { dEndIndex = line.length(); } descriptionExists = true; } // is this the primary bitstream? String primaryBitstreamMarker = "\tprimary:true"; boolean primary = false; String primaryStr = ""; if (line.contains(primaryBitstreamMarker)) { primary = true; primaryStr = "\t **Setting as primary bitstream**"; } if (bundleExists) { String bundleName = line.substring(bMarkerIndex + bundleMarker.length(), bEndIndex).trim(); processContentFileEntry(c, i, path, bitstreamName, bundleName, primary); System.out.println("\tBitstream: " + bitstreamName + "\tBundle: " + bundleName + primaryStr); } else { processContentFileEntry(c, i, path, bitstreamName, null, primary); System.out.println("\tBitstream: " + bitstreamName + primaryStr); } if (permissionsExist || descriptionExists) { String extraInfo = bitstreamName; if (permissionsExist) { extraInfo = extraInfo + line.substring(pMarkerIndex, pEndIndex); } if (descriptionExists) { extraInfo = extraInfo + line.substring(dMarkerIndex, dEndIndex); } options.add(extraInfo); } } } } finally { if (is != null) { is.close(); } } } else { File dir = new File(path); String[] dirListing = dir.list(); for (String fileName : dirListing) { if (!"dublin_core.xml".equals(fileName) && !fileName.equals("handle") && !metadataFileFilter .accept(dir, fileName)) { throw new FileNotFoundException("No contents file found"); } } System.out.println("No contents file found - but only metadata files found. Assuming metadata only."); } return options; } /** * each entry represents a bitstream.... * * @param c DSpace Context * @param i Dspace Item * @param path path to file * @param fileName file name * @param bundleName bundle name * @param primary if primary bitstream * @throws SQLException if database error * @throws IOException if IO error * @throws AuthorizeException if authorization error */ protected void processContentFileEntry(Context c, Item i, String path, String fileName, String bundleName, boolean primary) throws SQLException, IOException, AuthorizeException { String fullpath = path + File.separatorChar + fileName; // get an input stream BufferedInputStream bis = new BufferedInputStream(new FileInputStream( fullpath)); Bitstream bs = null; String newBundleName = bundleName; if (bundleName == null) { // is it license.txt? if ("license.txt".equals(fileName)) { newBundleName = "LICENSE"; } else { // call it ORIGINAL newBundleName = "ORIGINAL"; } } if (!isTest) { // find the bundle List bundles = itemService.getBundles(i, newBundleName); Bundle targetBundle = null; if (bundles.size() < 1) { // not found, create a new one targetBundle = bundleService.create(c, i, newBundleName); } else { // put bitstreams into first bundle targetBundle = bundles.iterator().next(); } // now add the bitstream bs = bitstreamService.create(c, targetBundle, bis); bs.setName(c, fileName); // Identify the format // FIXME - guessing format guesses license.txt incorrectly as a text // file format! BitstreamFormat bf = bitstreamFormatService.guessFormat(c, bs); bitstreamService.setFormat(c, bs, bf); // Is this a the primary bitstream? if (primary) { targetBundle.setPrimaryBitstreamID(bs); bundleService.update(c, targetBundle); } bitstreamService.update(c, bs); } bis.close(); } /** * Register the bitstream file into DSpace * * @param c DSpace Context * @param i DSpace Item * @param assetstore assetstore number * @param bitstreamPath the full filepath expressed in the contents file * @param bundleName bundle name * @param description bitstream description * @throws SQLException if database error * @throws IOException if IO error * @throws AuthorizeException if authorization error */ protected void registerBitstream(Context c, Item i, int assetstore, String bitstreamPath, String bundleName, String description) throws SQLException, IOException, AuthorizeException { // TODO validate assetstore number // TODO make sure the bitstream is there Bitstream bs = null; String newBundleName = bundleName; if (StringUtils.isBlank(bundleName)) { // is it license.txt? if (bitstreamPath.endsWith("license.txt")) { newBundleName = "LICENSE"; } else { // call it ORIGINAL newBundleName = "ORIGINAL"; } } if (!isTest) { // find the bundle List bundles = itemService.getBundles(i, newBundleName); Bundle targetBundle = null; if (bundles.size() < 1) { // not found, create a new one targetBundle = bundleService.create(c, i, newBundleName); } else { // put bitstreams into first bundle targetBundle = bundles.iterator().next(); } // now add the bitstream bs = bitstreamService.register(c, targetBundle, assetstore, bitstreamPath); // set the name to just the filename int iLastSlash = bitstreamPath.lastIndexOf('/'); bs.setName(c, bitstreamPath.substring(iLastSlash + 1)); // Identify the format // FIXME - guessing format guesses license.txt incorrectly as a text file format! BitstreamFormat bf = bitstreamFormatService.guessFormat(c, bs); bitstreamService.setFormat(c, bs, bf); bs.setDescription(c, description); bitstreamService.update(c, bs); } } /** * Process the Options to apply to the Item. The options are tab delimited * * Options: * {@code * 48217870-MIT.pdf permissions: -r 'MIT Users' description: Full printable version (MIT only) * permissions:[r|w]-['group name'] * description: 'the description of the file' * } * where: * {@code * [r|w] (meaning: read|write) * ['MIT Users'] (the group name) * } * * @param c DSpace Context * @param myItem DSpace Item * @param options List of option strings * @throws SQLException if database error * @throws AuthorizeException if authorization error */ protected void processOptions(Context c, Item myItem, List options) throws SQLException, AuthorizeException { for (String line : options) { System.out.println("\tprocessing " + line); boolean permissionsExist = false; boolean descriptionExists = false; String permissionsMarker = "\tpermissions:"; int pMarkerIndex = line.indexOf(permissionsMarker); int pEndIndex = 0; if (pMarkerIndex > 0) { pEndIndex = line.indexOf("\t", pMarkerIndex + 1); if (pEndIndex == -1) { pEndIndex = line.length(); } permissionsExist = true; } String descriptionMarker = "\tdescription:"; int dMarkerIndex = line.indexOf(descriptionMarker); int dEndIndex = 0; if (dMarkerIndex > 0) { dEndIndex = line.indexOf("\t", dMarkerIndex + 1); if (dEndIndex == -1) { dEndIndex = line.length(); } descriptionExists = true; } int bsEndIndex = line.indexOf("\t"); String bitstreamName = line.substring(0, bsEndIndex); int actionID = -1; String groupName = ""; Group myGroup = null; if (permissionsExist) { String thisPermission = line.substring(pMarkerIndex + permissionsMarker.length(), pEndIndex); // get permission type ("read" or "write") int pTypeIndex = thisPermission.indexOf('-'); // get permission group (should be in single quotes) int groupIndex = thisPermission.indexOf('\'', pTypeIndex); int groupEndIndex = thisPermission.indexOf('\'', groupIndex + 1); // if not in single quotes, assume everything after type flag is // group name if (groupIndex == -1) { groupIndex = thisPermission.indexOf(' ', pTypeIndex); groupEndIndex = thisPermission.length(); } groupName = thisPermission.substring(groupIndex + 1, groupEndIndex); if (thisPermission.toLowerCase().charAt(pTypeIndex + 1) == 'r') { actionID = Constants.READ; } else if (thisPermission.toLowerCase().charAt(pTypeIndex + 1) == 'w') { actionID = Constants.WRITE; } try { myGroup = groupService.findByName(c, groupName); } catch (SQLException sqle) { System.out.println("SQL Exception finding group name: " + groupName); // do nothing, will check for null group later } } String thisDescription = ""; if (descriptionExists) { thisDescription = line.substring( dMarkerIndex + descriptionMarker.length(), dEndIndex) .trim(); } Bitstream bs = null; boolean notfound = true; if (!isTest) { // find bitstream List bitstreams = itemService.getNonInternalBitstreams(c, myItem); for (int j = 0; j < bitstreams.size() && notfound; j++) { if (bitstreams.get(j).getName().equals(bitstreamName)) { bs = bitstreams.get(j); notfound = false; } } } if (notfound && !isTest) { // this should never happen System.out.println("\tdefault permissions set for " + bitstreamName); } else if (!isTest) { if (permissionsExist) { if (myGroup == null) { System.out.println("\t" + groupName + " not found, permissions set to default"); } else if (actionID == -1) { System.out .println("\tinvalid permissions flag, permissions set to default"); } else { System.out.println("\tSetting special permissions for " + bitstreamName); setPermission(c, myGroup, actionID, bs); } } if (descriptionExists) { System.out.println("\tSetting description for " + bitstreamName); bs.setDescription(c, thisDescription); bitstreamService.update(c, bs); } } } } /** * Set the Permission on a Bitstream. * * @param c DSpace Context * @param g Dspace Group * @param actionID action identifier * @param bs Bitstream * @throws SQLException if database error * @throws AuthorizeException if authorization error * @see org.dspace.core.Constants */ protected void setPermission(Context c, Group g, int actionID, Bitstream bs) throws SQLException, AuthorizeException { if (!isTest) { // remove the default policy authorizeService.removeAllPolicies(c, bs); // add the policy ResourcePolicy rp = resourcePolicyService.create(c); rp.setdSpaceObject(bs); rp.setAction(actionID); rp.setGroup(g); resourcePolicyService.update(c, rp); } else { if (actionID == Constants.READ) { System.out.println("\t\tpermissions: READ for " + g.getName()); } else if (actionID == Constants.WRITE) { System.out.println("\t\tpermissions: WRITE for " + g.getName()); } } } // XML utility methods /** * Lookup an attribute from a DOM node. * * @param n node * @param name attribute name * @return attribute value */ private String getAttributeValue(Node n, String name) { NamedNodeMap nm = n.getAttributes(); for (int i = 0; i < nm.getLength(); i++) { Node node = nm.item(i); if (name.equals(node.getNodeName())) { return node.getNodeValue(); } } return ""; } /** * Return the String value of a Node. * * @param node node * @return string value */ protected String getStringValue(Node node) { String value = node.getNodeValue(); if (node.hasChildNodes()) { Node first = node.getFirstChild(); if (first.getNodeType() == Node.TEXT_NODE) { return first.getNodeValue(); } } return value; } /** * Load in the XML from file. * * @param filename the filename to load from * @return the DOM representation of the XML file * @throws IOException if IO error * @throws ParserConfigurationException if config error * @throws SAXException if XML error */ protected Document loadXML(String filename) throws IOException, ParserConfigurationException, SAXException { DocumentBuilder builder = DocumentBuilderFactory.newInstance() .newDocumentBuilder(); return builder.parse(new File(filename)); } /** * Delete a directory and its child files and directories * * @param path The directory to delete * @return Whether the deletion was successful or not */ protected boolean deleteDirectory(File path) { if (path.exists()) { File[] files = path.listFiles(); for (int i = 0; i < files.length; i++) { if (files[i].isDirectory()) { deleteDirectory(files[i]); } else { if (!files[i].delete()) { log.error("Unable to delete file: " + files[i].getName()); } } } } boolean pathDeleted = path.delete(); return (pathDeleted); } @Override public String unzip(File zipfile) throws IOException { return unzip(zipfile, null); } @Override public String unzip(File zipfile, String destDir) throws IOException { // 2 // does the zip file exist and can we write to the temp directory if (!zipfile.canRead()) { log.error("Zip file '" + zipfile.getAbsolutePath() + "' does not exist, or is not readable."); } String destinationDir = destDir; if (destinationDir == null) { destinationDir = tempWorkDir; } File tempdir = new File(destinationDir); if (!tempdir.isDirectory()) { log.error("'" + configurationService.getProperty("org.dspace.app.itemexport.work.dir") + "' as defined by the key 'org.dspace.app.itemexport.work.dir' in dspace.cfg " + "is not a valid directory"); } if (!tempdir.exists() && !tempdir.mkdirs()) { log.error("Unable to create temporary directory: " + tempdir.getAbsolutePath()); } String sourcedir = destinationDir + System.getProperty("file.separator") + zipfile.getName(); String zipDir = destinationDir + System.getProperty("file.separator") + zipfile.getName() + System .getProperty("file.separator"); // 3 String sourceDirForZip = sourcedir; ZipFile zf = new ZipFile(zipfile); ZipEntry entry; Enumeration entries = zf.entries(); while (entries.hasMoreElements()) { entry = entries.nextElement(); if (entry.isDirectory()) { if (!new File(zipDir + entry.getName()).mkdirs()) { log.error("Unable to create contents directory: " + zipDir + entry.getName()); } } else { String entryName = entry.getName(); File outFile = new File(zipDir + entryName); // Verify that this file will be extracted into our zipDir (and not somewhere else!) if (!outFile.toPath().normalize().startsWith(zipDir)) { throw new IOException("Bad zip entry: '" + entryName + "' in file '" + zipfile.getAbsolutePath() + "'!" + " Cannot process this file."); } else { System.out.println("Extracting file: " + entryName); log.info("Extracting file: " + entryName); int index = entryName.lastIndexOf('/'); if (index == -1) { // Was it created on Windows instead? index = entryName.lastIndexOf('\\'); } if (index > 0) { File dir = new File(zipDir + entryName.substring(0, index)); if (!dir.exists() && !dir.mkdirs()) { log.error("Unable to create directory: " + dir.getAbsolutePath()); } //Entries could have too many directories, and we need to adjust the sourcedir // file1.zip (SimpleArchiveFormat / item1 / contents|dublin_core|... // SimpleArchiveFormat / item2 / contents|dublin_core|... // or // file2.zip (item1 / contents|dublin_core|... // item2 / contents|dublin_core|... //regex supports either windows or *nix file paths String[] entryChunks = entryName.split("/|\\\\"); if (entryChunks.length > 2) { if (StringUtils.equals(sourceDirForZip, sourcedir)) { sourceDirForZip = sourcedir + "/" + entryChunks[0]; } } } byte[] buffer = new byte[1024]; int len; InputStream in = zf.getInputStream(entry); BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(outFile)); while ((len = in.read(buffer)) >= 0) { out.write(buffer, 0, len); } in.close(); out.close(); } } } //Close zip file zf.close(); if (!StringUtils.equals(sourceDirForZip, sourcedir)) { sourcedir = sourceDirForZip; System.out.println("Set sourceDir using path inside of Zip: " + sourcedir); log.info("Set sourceDir using path inside of Zip: " + sourcedir); } return sourcedir; } @Override public String unzip(String sourcedir, String zipfilename) throws IOException { File zipfile = new File(sourcedir + File.separator + zipfilename); return unzip(zipfile); } /** * Generate a random filename based on current time * * @param hidden set to add . as a prefix to make the file hidden * @return the filename */ protected String generateRandomFilename(boolean hidden) { String filename = String.format("%s", RandomStringUtils.randomAlphanumeric(8)); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmm"); String datePart = sdf.format(new Date()); filename = datePart + "_" + filename; return filename; } /** * Given a local file or public URL to a zip file that has the Simple Archive Format, this method imports the * contents to DSpace * * @param filepath The filepath to local file or the public URL of the zip file * @param owningCollection The owning collection the items will belong to * @param otherCollections The collections the created items will be inserted to, apart from the owning one * @param resumeDir In case of a resume request, the directory that containsthe old mapfile and data * @param inputType The input type of the data (bibtex, csv, etc.), in case of local file * @param context The context * @param template whether to use template item * @throws Exception if error */ @Override public void processUIImport(String filepath, Collection owningCollection, String[] otherCollections, String resumeDir, String inputType, Context context, final boolean template) throws Exception { final EPerson oldEPerson = context.getCurrentUser(); final String[] theOtherCollections = otherCollections; final Collection theOwningCollection = owningCollection; final String theFilePath = filepath; final String theInputType = inputType; final String theResumeDir = resumeDir; final boolean useTemplateItem = template; Thread go = new Thread() { @Override public void run() { Context context = null; String importDir = null; EPerson eperson = null; try { // create a new dspace context context = new Context(); eperson = ePersonService.find(context, oldEPerson.getID()); context.setCurrentUser(eperson); context.turnOffAuthorisationSystem(); boolean isResume = theResumeDir != null; List collectionList = new ArrayList<>(); if (theOtherCollections != null) { for (String colID : theOtherCollections) { UUID colId = UUID.fromString(colID); if (!theOwningCollection.getID().equals(colId)) { Collection col = collectionService.find(context, colId); if (col != null) { collectionList.add(col); } } } } importDir = configurationService.getProperty( "org.dspace.app.batchitemimport.work.dir") + File.separator + "batchuploads" + File.separator + context .getCurrentUser() .getID() + File.separator + (isResume ? theResumeDir : (new GregorianCalendar()) .getTimeInMillis()); File importDirFile = new File(importDir); if (!importDirFile.exists()) { boolean success = importDirFile.mkdirs(); if (!success) { log.info("Cannot create batch import directory!"); throw new Exception("Cannot create batch import directory!"); } } String dataPath = null; String dataDir = null; if (theInputType.equals("saf")) { //In case of Simple Archive Format import (from remote url) dataPath = importDirFile + File.separator + "data.zip"; dataDir = importDirFile + File.separator + "data_unzipped2" + File.separator; } else if (theInputType .equals("safupload")) { //In case of Simple Archive Format import (from upload file) FileUtils.copyFileToDirectory(new File(theFilePath), importDirFile); dataPath = importDirFile + File.separator + (new File(theFilePath)).getName(); dataDir = importDirFile + File.separator + "data_unzipped2" + File.separator; } else { // For all other imports dataPath = importDirFile + File.separator + (new File(theFilePath)).getName(); dataDir = importDirFile + File.separator + "data" + File.separator; } //Clear these files, if a resume if (isResume) { if (!theInputType.equals("safupload")) { (new File(dataPath)).delete(); } (new File(importDirFile + File.separator + "error.txt")).delete(); FileDeleteStrategy.FORCE.delete(new File(dataDir)); FileDeleteStrategy.FORCE .delete(new File(importDirFile + File.separator + "data_unzipped" + File.separator)); } //In case of Simple Archive Format import we need an extra effort to download the zip file and // unzip it String sourcePath = null; if (theInputType.equals("saf")) { OutputStream os = new FileOutputStream(dataPath); byte[] b = new byte[2048]; int length; InputStream is = new URL(theFilePath).openStream(); while ((length = is.read(b)) != -1) { os.write(b, 0, length); } is.close(); os.close(); sourcePath = unzip(new File(dataPath), dataDir); //Move files to the required folder FileUtils.moveDirectory(new File(sourcePath), new File( importDirFile + File.separator + "data_unzipped" + File.separator)); FileDeleteStrategy.FORCE.delete(new File(dataDir)); dataDir = importDirFile + File.separator + "data_unzipped" + File.separator; } else if (theInputType.equals("safupload")) { sourcePath = unzip(new File(dataPath), dataDir); //Move files to the required folder FileUtils.moveDirectory(new File(sourcePath), new File( importDirFile + File.separator + "data_unzipped" + File.separator)); FileDeleteStrategy.FORCE.delete(new File(dataDir)); dataDir = importDirFile + File.separator + "data_unzipped" + File.separator; } //Create mapfile path String mapFilePath = importDirFile + File.separator + "mapfile"; List finalCollections = null; if (theOwningCollection != null) { finalCollections = new ArrayList<>(); finalCollections.add(theOwningCollection); finalCollections.addAll(collectionList); } setResume(isResume); if (theInputType.equals("saf") || theInputType .equals("safupload")) { //In case of Simple Archive Format import addItems(context, finalCollections, dataDir, mapFilePath, template); } // email message letting user know the file is ready for // download emailSuccessMessage(context, eperson, mapFilePath); context.complete(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); String exceptionString = ExceptionUtils.getStackTrace(e); try { File importDirFile = new File(importDir + File.separator + "error.txt"); PrintWriter errorWriter = new PrintWriter(importDirFile); errorWriter.print(exceptionString); errorWriter.close(); emailErrorMessage(eperson, exceptionString); throw new Exception(e.getMessage()); } catch (Exception e2) { // wont throw here } } finally { // Make sure the database connection gets closed in all conditions. try { context.complete(); } catch (SQLException sqle) { context.abort(); } } } }; go.isDaemon(); go.start(); } @Override public void emailSuccessMessage(Context context, EPerson eperson, String fileName) throws MessagingException { try { Locale supportedLocale = I18nUtil.getEPersonLocale(eperson); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "bte_batch_import_success")); email.addRecipient(eperson.getEmail()); email.addArgument(fileName); email.send(); } catch (Exception e) { log.warn(LogHelper.getHeader(context, "emailSuccessMessage", "cannot notify user of import"), e); } } @Override public void emailErrorMessage(EPerson eperson, String error) throws MessagingException { log.warn("An error occurred during item import, the user will be notified. " + error); try { Locale supportedLocale = I18nUtil.getEPersonLocale(eperson); Email email = Email.getEmail(I18nUtil.getEmailFilename(supportedLocale, "bte_batch_import_error")); email.addRecipient(eperson.getEmail()); email.addArgument(error); email.addArgument(configurationService.getProperty("dspace.ui.url") + "/feedback"); email.send(); } catch (Exception e) { log.warn("error during item import error notification", e); } } @Override public List getImportsAvailable(EPerson eperson) throws Exception { File uploadDir = new File(getImportUploadableDirectory(eperson)); if (!uploadDir.exists() || !uploadDir.isDirectory()) { return null; } Map fileNames = new TreeMap<>(); for (String fileName : uploadDir.list()) { File file = new File(uploadDir + File.separator + fileName); if (file.isDirectory()) { BatchUpload upload = new BatchUpload(file); fileNames.put(upload.getDir().getName(), upload); } } if (fileNames.size() > 0) { return new ArrayList<>(fileNames.values()); } return null; } @Override public String getImportUploadableDirectory(EPerson ePerson) throws Exception { String uploadDir = configurationService.getProperty("org.dspace.app.batchitemimport.work.dir"); if (uploadDir == null) { throw new Exception( "A dspace.cfg entry for 'org.dspace.app.batchitemimport.work.dir' does not exist."); } String uploadDirBasePath = uploadDir + File.separator + "batchuploads" + File.separator; //Check for backwards compatibility with the old identifier File uploadDirectory = new File(uploadDirBasePath + ePerson.getLegacyId()); if (!uploadDirectory.exists()) { uploadDirectory = new File(uploadDirBasePath + ePerson.getID()); } return uploadDirectory.getAbsolutePath(); } @Override public void deleteBatchUpload(Context c, String uploadId) throws Exception { String uploadDir = null; String mapFilePath = null; uploadDir = getImportUploadableDirectory(c.getCurrentUser()) + File.separator + uploadId; mapFilePath = uploadDir + File.separator + "mapfile"; this.deleteItems(c, mapFilePath); FileDeleteStrategy.FORCE.delete(new File(uploadDir)); } @Override public String getTempWorkDir() { return tempWorkDir; } @Override public File getTempWorkDirFile() throws IOException { File tempDirFile = new File(getTempWorkDir()); if (!tempDirFile.exists()) { boolean success = tempDirFile.mkdirs(); if (!success) { throw new IOException("Work directory " + tempDirFile.getAbsolutePath() + " could not be created."); } else { log.debug("Created directory " + tempDirFile.getAbsolutePath()); } } else { log.debug("Work directory exists: " + tempDirFile.getAbsolutePath()); } return tempDirFile; } @Override public void cleanupZipTemp() { System.out.println("Deleting temporary zip directory: " + tempWorkDir); log.debug("Deleting temporary zip directory: " + tempWorkDir); deleteDirectory(new File(tempWorkDir)); } @Override public void setTest(boolean isTest) { this.isTest = isTest; } @Override public void setResume(boolean isResume) { this.isResume = isResume; } @Override public void setUseWorkflow(boolean useWorkflow) { this.useWorkflow = useWorkflow; } @Override public void setUseWorkflowSendEmail(boolean useWorkflowSendEmail) { this.useWorkflowSendEmail = useWorkflowSendEmail; } @Override public void setQuiet(boolean isQuiet) { this.isQuiet = isQuiet; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy