Java tutorial
/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is part of dcm4che, an implementation of DICOM(TM) in * Java(TM), available at http://sourceforge.net/projects/dcm4che. * * The Initial Developer of the Original Code is * TIANI Medgraph AG. * Portions created by the Initial Developer are Copyright (C) 2005 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Gunter Zeilinger <gunter.zeilinger@tiani.com> * Franz Willer <franz.willer@gwi-ag.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.dcm4chex.archive.hsm; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.management.AttributeNotFoundException; import javax.management.InstanceNotFoundException; import javax.management.MBeanException; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import javax.management.ReflectionException; import org.apache.commons.compress.tar.TarEntry; import org.apache.commons.compress.tar.TarOutputStream; import org.dcm4che.data.Dataset; import org.dcm4che.data.DcmElement; import org.dcm4che.data.DcmObjectFactory; import org.dcm4che.dict.Tags; import org.dcm4che.util.BufferedOutputStream; import org.dcm4che.util.MD5Utils; import org.dcm4chex.archive.common.Availability; import org.dcm4chex.archive.common.BaseJmsOrder; import org.dcm4chex.archive.config.ForwardingRules; import org.dcm4chex.archive.ejb.interfaces.FileSystemMgt2; import org.dcm4chex.archive.ejb.interfaces.FileSystemMgt2Home; import org.dcm4chex.archive.ejb.interfaces.MD5; import org.dcm4chex.archive.ejb.interfaces.Storage; import org.dcm4chex.archive.ejb.jdbc.FileInfo; import org.dcm4chex.archive.ejb.jdbc.QueryCmd; import org.dcm4chex.archive.util.EJBHomeFactory; import org.dcm4chex.archive.util.FileUtils; /** * @author gunter.zeilinger@tiani.com * @version $Revision: 18249 $ $Date: 2014-02-21 13:18:22 -0300 (sex, 21 fev 2014) $ * @since Nov 9, 2005 */ public class FileCopyService extends AbstractFileCopyService { private static final String NONE = "NONE"; public static final String NEW_LINE = System.getProperty("line.separator", "\n"); private static final int MD5SUM_ENTRY_LEN = 52; private ObjectName hsmModuleServicename = null; public final String getHSMModulServicename() { return hsmModuleServicename == null ? NONE : hsmModuleServicename.toString(); } public final void setHSMModulServicename(String name) throws MalformedObjectNameException { this.hsmModuleServicename = NONE.equals(name) ? null : ObjectName.getInstance(name); } public String getAvailableHSMModules() { try { Set names = server.queryNames(new ObjectName("*:service=FileCopyHSMModule,*"), null); StringBuilder sb = new StringBuilder(names.size() * 40); for (Iterator it = names.iterator(); it.hasNext();) { sb.append(it.next()).append(NEW_LINE); } return sb.toString(); } catch (Exception x) { log.error("Failed to get list of available HSM modules!", x); return "ERROR"; } } public boolean isReady() { if (getState() == STARTED) { try { if (hsmModuleServicename == null || (server.isRegistered(hsmModuleServicename) && (Integer) server.getAttribute(hsmModuleServicename, "State") == STARTED)) { return true; } } catch (Exception e) { log.error("Failed to get state of hsmModuleServicename:" + hsmModuleServicename); return false; } } return false; } public boolean copyFilesOfStudy(String studyIUID) throws Exception { if (!checkDestinationFsPath(destination)) { log.warn("Destination is not configured or doesn't exist! skip this copyFilesOfStudy call!"); return false; } log.info("Start copy files of study " + studyIUID); Dataset queryDs = DcmObjectFactory.getInstance().newDataset(); queryDs.putCS(Tags.QueryRetrieveLevel, "SERIES"); queryDs.putUI(Tags.StudyInstanceUID, studyIUID); queryDs.putUI(Tags.SeriesInstanceUID); QueryCmd cmd = QueryCmd.create(queryDs, null, true, false, false, false, null); try { cmd.setFetchSize(getFetchSize()).execute(); while (cmd.next()) { doCopyFilesOfSeries(cmd.getDataset().getString(Tags.SeriesInstanceUID)); } } finally { cmd.close(); } return true; } public boolean copyFilesOfSeries(String seriesIUID) throws Exception { if (!checkDestinationFsPath(destination)) { log.warn("Destination is not configured or doesn't exist! skip this copyFilesOfSeries call!"); return false; } return doCopyFilesOfSeries(seriesIUID); } private boolean doCopyFilesOfSeries(String seriesIUID) throws Exception { Dataset queryDs = DcmObjectFactory.getInstance().newDataset(); queryDs.putCS(Tags.QueryRetrieveLevel, "IMAGE"); queryDs.putUI(Tags.StudyInstanceUID); queryDs.putUI(Tags.SeriesInstanceUID, seriesIUID); queryDs.putUI(Tags.SOPInstanceUID); QueryCmd cmd = QueryCmd.create(queryDs, null, true, false, false, false, null); try { cmd.execute(); Dataset ds = null; Dataset ian = DcmObjectFactory.getInstance().newDataset(); Dataset refSeries = ian.putSQ(Tags.RefSeriesSeq).addNewItem(); DcmElement refSOPs = refSeries.putSQ(Tags.RefSOPSeq); while (cmd.next()) { ds = cmd.getDataset(); refSeries.putUI(Tags.SeriesInstanceUID, ds.getString(Tags.SeriesInstanceUID)); refSOPs.addNewItem().putUI(Tags.RefSOPInstanceUID, ds.getString(Tags.SOPInstanceUID)); } if (ds != null) { ian.putUI(Tags.StudyInstanceUID, ds.getString(Tags.StudyInstanceUID)); refSeries.putUI(Tags.SeriesInstanceUID, ds.getString(Tags.SeriesInstanceUID)); schedule(createOrder(ian), 0l); log.info("Copy files of series " + seriesIUID + " scheduled!"); return true; } else { log.info("No instances found for file copy! seriesIUID:" + seriesIUID); log.debug(queryDs); return false; } } finally { cmd.close(); } } protected BaseJmsOrder createOrder(Dataset ian) { FileCopyOrder fileCopyOrder = new FileCopyOrder(ian, ForwardingRules.toAET(destination), getRetrieveAETs(), getFetchSize()); fileCopyOrder.processOrderProperties(); return fileCopyOrder; } protected void process(BaseJmsOrder order) throws Exception { FileCopyOrder fileCopyOrder = (FileCopyOrder) order; String destPath = fileCopyOrder.getDestinationFileSystemPath(); if (!checkDestinationFsPath(destPath)) { log.error("Destination file system doesn't exist! Skip this FileCopy order!"); return; } List<FileInfo> fileInfos = fileCopyOrder.getFileInfos(); int removed = removeOfflineOrTarSourceFiles(fileInfos); if (removed > 0) log.info(removed + " Files (Offline or on tar FS) removed from FileCopy Order!" + "\nRemaining files to copy:" + fileInfos.size()); if (fileInfos.isEmpty()) { log.info("Skip FileCopy Order! No files to copy!"); return; } if (destPath.startsWith("tar:")) { copyTar(fileInfos, destPath); } else { copyFiles(fileInfos, destPath); } } private void copyFiles(List<FileInfo> fileInfos, String destPath) throws Exception { byte[] buffer = new byte[bufferSize]; Storage storage = getStorageHome().create(); Exception ex = null; MessageDigest digest = null; if (verifyCopy) digest = MessageDigest.getInstance("MD5"); for (Iterator<FileInfo> iter = fileInfos.iterator(); iter.hasNext();) { FileInfo finfo = iter.next(); File src = FileUtils.toFile(finfo.basedir + '/' + finfo.fileID); File dst = FileUtils.toFile(destPath + '/' + finfo.fileID); try { copy(src, dst); byte[] md5sum0 = finfo.md5 != null ? MD5Utils.toBytes(finfo.md5) : null; if (md5sum0 != null && digest != null) { byte[] md5sum = MD5Utils.md5sum(dst, digest, buffer); if (!Arrays.equals(md5sum0, md5sum)) { String prompt = "md5 sum of copy " + dst + " differs from md5 sum in DB for file " + src; log.warn(prompt); throw new IOException(prompt); } } byte[] origMd5sum0 = finfo.origMd5 != null ? MD5Utils.toBytes(finfo.origMd5) : null; storage.storeFile(finfo.sopIUID, finfo.tsUID, destPath, finfo.fileID, (int) finfo.size, md5sum0, origMd5sum0, fileStatus); iter.remove(); } catch (Exception e) { dst.delete(); ex = e; } } if (ex != null) throw ex; } private void copy(File src, File dst) throws IOException { try { File dir = dst.getParentFile(); if (dir.mkdirs()) { log.info("M-WRITE dir:" + dir); } log.info("M-WRITE file:" + dst); FileUtils.copyFile(src, dst); } catch (IOException e) { log.error("Copy file " + src + " failed", e); throw e; } } private void copyTar(List<FileInfo> fileInfos, String destPath) throws Exception { FileInfo file1Info = (FileInfo) fileInfos.get(0); String tarPath = mkTarPath(file1Info.fileID); String[] tarEntryNames = new String[fileInfos.size()]; for (int i = 0; i < tarEntryNames.length; i++) { tarEntryNames[i] = mkTarEntryName(fileInfos.get(i)); } if (hsmModuleServicename == null) { File tarFile = FileUtils.toFile(destPath.substring(4), tarPath); mkTar(fileInfos, tarFile, tarEntryNames); } else { File tarFile = prepareHSMFile(destPath, tarPath); try { mkTar(fileInfos, tarFile, tarEntryNames); tarPath = storeHSMFile(tarFile, destPath, tarPath); } catch (Exception x) { log.error("Make Tar file failed!", x); tarFile.delete(); failedHSMFile(tarFile, destPath, tarPath); throw x; } } Storage storage = getStorageHome().create(); for (int i = 0; i < tarEntryNames.length; i++) { String fileId = tarPath + '!' + tarEntryNames[i]; FileInfo finfo = fileInfos.get(i); byte[] origMd5sum = finfo.origMd5 != null ? MD5Utils.toBytes(finfo.origMd5) : null; storage.storeFile(finfo.sopIUID, finfo.tsUID, destPath, fileId, (int) finfo.size, MD5.toBytes(finfo.md5), origMd5sum, fileStatus); } } private File prepareHSMFile(String fsID, String filePath) throws InstanceNotFoundException, MBeanException, ReflectionException { return (File) server.invoke(hsmModuleServicename, "prepareHSMFile", new Object[] { fsID, filePath }, new String[] { String.class.getName(), String.class.getName() }); } private String storeHSMFile(File file, String fsID, String filePath) throws InstanceNotFoundException, MBeanException, ReflectionException { return (String) server.invoke(hsmModuleServicename, "storeHSMFile", new Object[] { file, fsID, filePath }, new String[] { File.class.getName(), String.class.getName(), String.class.getName() }); } private void failedHSMFile(File file, String fsID, String filePath) throws InstanceNotFoundException, MBeanException, ReflectionException { server.invoke(hsmModuleServicename, "failedHSMFile", new Object[] { file, fsID, filePath }, new String[] { File.class.getName(), String.class.getName(), String.class.getName() }); } private void mkTar(List<FileInfo> fileInfos, File tarFile, String[] tarEntryNames) throws Exception { try { if (tarFile.getParentFile().mkdirs()) { log.info("M-WRITE " + tarFile.getParent()); } log.info("M-WRITE " + tarFile); TarOutputStream tar = new TarOutputStream(new FileOutputStream(tarFile)); try { writeMD5SUM(tar, fileInfos, tarEntryNames); for (int i = 0; i < tarEntryNames.length; i++) { writeFile(tar, fileInfos.get(i), tarEntryNames[i]); } } finally { tar.close(); } if (verifyCopy) { VerifyTar.verify(tarFile, new byte[bufferSize]); } } catch (Exception e) { log.error("M-DELETE tar file due to an error! " + tarFile); tarFile.delete(); throw e; } } private int removeOfflineOrTarSourceFiles(List<FileInfo> fileInfos) { int removed = 0; FileInfo fi; for (Iterator<FileInfo> iter = fileInfos.iterator(); iter.hasNext();) { fi = iter.next(); if (fi.availability != Availability.ONLINE || fi.basedir.startsWith("tar:")) { removed++; iter.remove(); } } return removed; } private void writeMD5SUM(TarOutputStream tar, List<FileInfo> fileInfos, String[] tarEntryNames) throws IOException { byte[] md5sum = new byte[fileInfos.size() * MD5SUM_ENTRY_LEN]; final TarEntry tarEntry = new TarEntry("MD5SUM"); tarEntry.setSize(md5sum.length); tar.putNextEntry(tarEntry); int i = 0; for (int j = 0; j < tarEntryNames.length; j++) { MD5Utils.toHexChars(MD5.toBytes(fileInfos.get(j).md5), md5sum, i); md5sum[i + 32] = ' '; md5sum[i + 33] = ' '; System.arraycopy(tarEntryNames[j].getBytes("US-ASCII"), 0, md5sum, i + 34, 17); md5sum[i + 51] = '\n'; i += MD5SUM_ENTRY_LEN; } tar.write(md5sum); tar.closeEntry(); } private void writeFile(TarOutputStream tar, FileInfo fileInfo, String tarEntryName) throws IOException, FileNotFoundException { File file = FileUtils.toFile(fileInfo.basedir, fileInfo.fileID); if (file.length() != fileInfo.size) { log.error("Filesize doesn't match for file entry:" + fileInfo + "!(" + file.length() + " vs. " + fileInfo.size + ") skipped!"); throw new IOException("Filesize doesn't match! file:" + file); } TarEntry entry = new TarEntry(tarEntryName); entry.setSize(fileInfo.size); tar.putNextEntry(entry); FileInputStream fis = new FileInputStream(file); try { tar.copyEntryContents(fis); } finally { fis.close(); } tar.closeEntry(); } private String mkTarEntryName(FileInfo fileInfo) { StringBuilder sb = new StringBuilder(17); sb.append(FileUtils.toHex(fileInfo.seriesIUID.hashCode())); sb.append('/'); sb.append(FileUtils.toHex((int) (fileInfo.pk))); return sb.toString(); } private String mkTarPath(String filePath) { StringBuffer sb = new StringBuffer(filePath); sb.setLength(filePath.lastIndexOf('/')); sb.append('-').append(System.currentTimeMillis() % 3600000).append(".tar"); return sb.toString(); } private boolean checkDestinationFsPath(String destFsPath) throws Exception { if (destFsPath == null) return false; FileSystemMgt2 mgr = ((FileSystemMgt2Home) EJBHomeFactory.getFactory().lookup(FileSystemMgt2Home.class, FileSystemMgt2Home.JNDI_NAME)).create(); try { return mgr.getFileSystem(destFsPath) != null; } catch (Exception x) { return false; } } }