Java tutorial
/* * Copyright 2010 MINT Working Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.nema.medical.mint.server; import java.io.*; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.naming.Context; import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.hibernate.SessionFactory; import org.nema.medical.mint.datadictionary.DataDictionaryIO; import org.nema.medical.mint.datadictionary.MetadataType; import org.nema.medical.mint.dcm2mint.ProcessImportDir; import org.nema.medical.mint.server.domain.ChangeDAO; import org.nema.medical.mint.server.domain.JobInfoDAO; import org.nema.medical.mint.server.domain.StudyDAO; import org.nema.medical.mint.server.receiver.DICOMReceive; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.io.ClassPathResource; import org.springframework.jndi.JndiObjectFactoryBean; import org.springframework.orm.hibernate3.HibernateTransactionManager; import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean; import org.springframework.transaction.support.AbstractPlatformTransactionManager; import org.springframework.util.FileCopyUtils; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; @Configuration public class ServerConfig { /* * Common stuff */ private static final Logger LOG = Logger.getLogger(ServerConfig.class); protected static HibernateTransactionManager hibernateTransactionManager = null; protected static SessionFactory sessionFactory = null; protected Context envContext = null; protected Properties properties = null; /* * MINTServer specific */ protected String xmlStylesheet = null; protected StudyDAO studyDAO = null; protected ChangeDAO updateDAO = null; protected JobInfoDAO jobInfoDAO = null; protected File jobTemp = null; protected File studiesRoot = null; protected File mintHome = null; protected File typesRoot = null; protected Boolean enableSCP = null; protected Boolean enableProcessor = null; protected File storageRootDir = null; protected String aeTitle = null; protected Integer port = null; protected Integer reaperTimeoutMS = null; protected URI serverURI = null; protected Boolean useXMLNotGPB = null; protected Boolean deletePhysicalFiles = null; protected Boolean forceCreate = null; protected Integer binaryInlineThreshold = null; protected DICOMReceive dcmRcv = null; protected ScheduledExecutorService dcm2MintExecutor = null; protected Integer binaryItemStreamBufferSize = null; protected Integer binaryItemResponseBufferSize = null; protected Integer fileResponseBufferSize = null; protected Integer fileStreamBufferSize = null; protected ArrayList<String> availableTypeNames = null; protected HashMap<String, File> availableTypeFiles = null; protected HashMap<String, MetadataType> availableTypes = null; @PostConstruct public void postConstruct() { //@PostConstruct method cannot throw checked exception try { setUpCStoreSCP(); setUpDICOM2MINT(); } catch (final IOException e) { throw new RuntimeException(e); } catch (final URISyntaxException e) { throw new RuntimeException(e); } } private void setUpCStoreSCP() throws IOException { if (!enableSCP()) { return; } dcmRcv = new DICOMReceive(); dcmRcv.setStorageRootDir(storageRootDir()); dcmRcv.setAETitle(aeTitle()); dcmRcv.setPort(port()); dcmRcv.setReaperTimeout(reaperTimeoutMS()); try { dcmRcv.start(); } catch (final IOException e) { //Need to catch and re-throw checked exceptions for @PostConstruct throw new RuntimeException(e); } } private void setUpDICOM2MINT() throws IOException, URISyntaxException { if (!enableProcessor()) { return; } dcm2MintExecutor = Executors.newScheduledThreadPool(2); //Create an instance of the Directory Processing Class final ProcessImportDir importProcessor = new ProcessImportDir(storageRootDir(), serverURI(), useXMLNotGPB(), deletePhysicalFiles(), forceCreate(), binaryInlineThreshold()); final Runnable checkResponsesTask = new Runnable() { public void run() { try { importProcessor.handleResponses(); importProcessor.handleSends(); } catch (final Throwable e) { System.err.println("An exception occurred while uploading to the server:"); e.printStackTrace(); } } }; dcm2MintExecutor.scheduleWithFixedDelay(checkResponsesTask, 1, 1, TimeUnit.SECONDS); final Runnable dirTraverseTask = new Runnable() { public void run() { try { importProcessor.processDir(); } catch (final Throwable e) { System.err.println("An exception occurred while processing files:"); e.printStackTrace(); } } }; dcm2MintExecutor.scheduleWithFixedDelay(dirTraverseTask, 20, 3, TimeUnit.SECONDS); } @PreDestroy public void preDestroy() { //TODO The teardowns below could be done in parallel //Tear down CStore SCP if (dcmRcv != null) { dcmRcv.stop(); dcmRcv = null; } //Tear down DICOM-to-MINT if (dcm2MintExecutor != null) { dcm2MintExecutor.shutdown(); try { if (!dcm2MintExecutor.awaitTermination(2, TimeUnit.SECONDS)) { dcm2MintExecutor.shutdownNow(); if (!dcm2MintExecutor.awaitTermination(1, TimeUnit.SECONDS)) { LOG.warn("DICOM-to-MINT Executor service did not terminate."); } } } catch (final InterruptedException e) { dcm2MintExecutor.shutdownNow(); Thread.currentThread().interrupt(); } dcm2MintExecutor = null; } } @Bean(name = "mintHome", autowire = Autowire.BY_NAME) public File mintHome() { if (mintHome == null) { String path = System.getenv("MINT_HOME"); if (path == null) { path = System.getProperty("user.home") + "/MINT_HOME"; LOG.warn("MINT_HOME enviornment variable not found, using " + path); } mintHome = new File(path); if (!mintHome.exists()) { LOG.warn("MINT_HOME does not exist, creating"); mintHome.mkdirs(); } } return mintHome; } private Properties envSpecificProperties() throws IOException { if (properties == null) { final ClassPathResource classPathResource = new ClassPathResource("default.config"); final Properties defaultProperties = new Properties(); defaultProperties.load(classPathResource.getInputStream()); final File mintHome = mintHome(); final File customConfigFile = new File(mintHome, "mint-server.config"); if (!customConfigFile.exists()) { final Writer customConfigWriter = new BufferedWriter(new FileWriter(customConfigFile)); try { defaultProperties.store(customConfigWriter, null); } finally { customConfigWriter.close(); } } final Properties customProperties = new Properties(); final Reader customConfigReader = new BufferedReader(new FileReader(customConfigFile)); try { customProperties.load(customConfigReader); } finally { customConfigReader.close(); } final Set<Object> defaultPropKeys = defaultProperties.keySet(); final Set<Object> customPropKeys = customProperties.keySet(); final Set<Object> defaultNotCustomKeys = new HashSet<Object>(defaultPropKeys); defaultNotCustomKeys.removeAll(customPropKeys); final Set<Object> customNotDefaultKeys = new HashSet<Object>(customPropKeys); customNotDefaultKeys.removeAll(defaultPropKeys); if (!customNotDefaultKeys.isEmpty()) { final StringBuilder diffKeysText = new StringBuilder( "The following properties exist in mint-server.config," + " but not in default.config:"); for (final Object key : customNotDefaultKeys) { diffKeysText.append(' '); diffKeysText.append(key); } LOG.warn(diffKeysText); } if (!defaultNotCustomKeys.isEmpty()) { final StringBuilder diffKeysText = new StringBuilder( "The following properties exist in default.config," + " but not in mint-server.config;" + " default values (in parentheses) will be used:"); for (final Object key : defaultNotCustomKeys) { final Object defaultPropValue = defaultProperties.get(key); diffKeysText.append(' '); diffKeysText.append(key); diffKeysText.append(" ("); diffKeysText.append(defaultPropValue); diffKeysText.append(')'); //Also add default property to custom properties customProperties.put(key, defaultPropValue); } LOG.warn(diffKeysText); } properties = customProperties; } return properties; } protected String getConfigString(final String key) throws IOException { return envSpecificProperties().getProperty(key); } protected Integer getConfigInt(final String key) throws IOException { final String stringVal = getConfigString(key); return StringUtils.isBlank(stringVal) ? null : Integer.valueOf(stringVal); } protected Boolean getConfigBool(final String key) throws IOException { final String stringVal = getConfigString(key); return StringUtils.isBlank(stringVal) ? null : Boolean.valueOf(stringVal); } protected String[] getPackagesToScan() { return new String[] { "org.nema.medical.mint.server" }; } @Bean(destroyMethod = "close") public SessionFactory sessionFactory() throws Exception { if (sessionFactory == null) { final AnnotationSessionFactoryBean annotationSessionFactoryBean = new AnnotationSessionFactoryBean(); if (StringUtils.isBlank(getConfigString("hibernate.connection.datasource"))) { // Not using JNDI data source final BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(getConfigString("hibernate.connection.driver_class")); String url = getConfigString("hibernate.connection.url"); url = url.replace("$MINT_HOME", mintHome().getPath()); dataSource.setUrl(url); dataSource.setUsername(getConfigString("hibernate.connection.username")); dataSource.setPassword(getConfigString("hibernate.connection.password")); annotationSessionFactoryBean.setDataSource(dataSource); } else { // Using a JNDI dataSource final JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean(); jndiObjectFactoryBean.setExpectedType(DataSource.class); jndiObjectFactoryBean.setJndiName(getConfigString("hibernate.connection.datasource")); jndiObjectFactoryBean.afterPropertiesSet(); annotationSessionFactoryBean.setDataSource((DataSource) jndiObjectFactoryBean.getObject()); } final Properties hibernateProperties = new Properties(); hibernateProperties.put("hibernate.connection.autocommit", Boolean.TRUE); final String dialect = getConfigString("hibernate.dialect"); if (StringUtils.isNotBlank(dialect)) { hibernateProperties.put("hibernate.dialect", dialect); } final String hbm2dll = getConfigString("hibernate.hbm2ddl.auto"); hibernateProperties.put("hibernate.hbm2ddl.auto", hbm2dll == null ? "verify" : hbm2dll); hibernateProperties.put("hibernate.show_sql", "true".equalsIgnoreCase(getConfigString("hibernate.show_sql"))); hibernateProperties.put("hibernate.c3p0.max_statement", 50); hibernateProperties.put("hibernate.c3p0.maxPoolSize", 20); hibernateProperties.put("hibernate.c3p0.minPoolSize", 5); hibernateProperties.put("hibernate.c3p0.testConnectionOnCheckout", Boolean.FALSE); hibernateProperties.put("hibernate.c3p0.timeout", 600); annotationSessionFactoryBean.setHibernateProperties(hibernateProperties); annotationSessionFactoryBean.setPackagesToScan(getPackagesToScan()); annotationSessionFactoryBean.afterPropertiesSet(); sessionFactory = annotationSessionFactoryBean.getObject(); } return sessionFactory; } @Bean public HibernateTransactionManager transactionManager() throws Exception { if (hibernateTransactionManager == null) { hibernateTransactionManager = new HibernateTransactionManager(); hibernateTransactionManager.setSessionFactory(sessionFactory()); hibernateTransactionManager.setTransactionSynchronization( AbstractPlatformTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION); hibernateTransactionManager.afterPropertiesSet(); } return hibernateTransactionManager; } @Bean(name = "jobTemp", autowire = Autowire.BY_NAME) public File jobTemp() throws Exception { jobTemp = new File(mintHome(), "jobtemp"); return jobTemp; } @Bean(name = "studiesRoot", autowire = Autowire.BY_NAME) public File studiesRoot() throws Exception { studiesRoot = new File(mintHome(), "studies"); return studiesRoot; } @Bean(name = "typesRoot", autowire = Autowire.BY_NAME) public File typesRoot() throws Exception { typesRoot = new File(mintHome(), "types"); if (!typesRoot.exists()) { typesRoot.mkdirs(); } //When including other types by default, their .xml definition files must be copied here File dicomFile = new File(typesRoot, "DICOM.xml"); if (!dicomFile.exists()) { InputStream internalDicomFile = getDicomTypeDefStream(); FileCopyUtils.copy(internalDicomFile, new FileOutputStream(dicomFile)); } return typesRoot; } private InputStream getDicomTypeDefStream() { return ServerConfig.class.getClassLoader().getResourceAsStream("DICOM.xml"); } @Bean public ArrayList<String> availableTypeNames() throws Exception { if (availableTypeNames == null) { final File typesRoot = typesRoot(); final FileFilter typeFilter = new FileFilter() { @Override //Accept a file as indicating a type if its name ends with ".xml" public final boolean accept(final File file) { return file.getName().endsWith(".xml"); } }; final File[] typeXMLFiles = typesRoot.listFiles(typeFilter); availableTypeNames = new ArrayList<String>(); for (final File typeXMLFile : typeXMLFiles) { final String fileName = typeXMLFile.getName(); availableTypeNames.add(fileName.substring(0, fileName.length() - ".xml".length())); } } return availableTypeNames; } @Bean public HashMap<String, File> availableTypeFiles() throws Exception { if (availableTypeFiles == null) { final File typesRoot = typesRoot(); final Collection<String> availableTypeNames = availableTypeNames(); availableTypeFiles = new HashMap<String, File>(availableTypeNames.size()); for (final String typeName : availableTypeNames) { final File typeFile = new File(typesRoot, typeName + ".xml"); availableTypeFiles.put(typeName, typeFile); } } return availableTypeFiles; } @Bean public HashMap<String, MetadataType> availableTypes() throws Exception { if (availableTypes == null) { final File typesRoot = typesRoot(); final Map<String, File> availableTypeFiles = availableTypeFiles(); availableTypes = new HashMap<String, MetadataType>(availableTypeFiles.size()); for (final Map.Entry<String, File> typeFile : availableTypeFiles.entrySet()) { final MetadataType dataDictionary = DataDictionaryIO.parseFromXML(typeFile.getValue()); availableTypes.put(typeFile.getKey(), dataDictionary); } } return availableTypes; } @Bean(name = "studyDAO", autowire = Autowire.BY_NAME) public StudyDAO studyDAO() throws Exception { if (studyDAO == null) { studyDAO = new StudyDAO(); studyDAO.setSessionFactory(sessionFactory()); studyDAO.afterPropertiesSet(); } return studyDAO; } @Bean(name = "updateDAO", autowire = Autowire.BY_NAME) public ChangeDAO updateDAO() throws Exception { if (updateDAO == null) { updateDAO = new ChangeDAO(); updateDAO.setSessionFactory(sessionFactory()); updateDAO.afterPropertiesSet(); } return updateDAO; } @Bean(name = "jobInfoDAO", autowire = Autowire.BY_NAME) public JobInfoDAO jobInfoDAO() throws Exception { if (jobInfoDAO == null) { jobInfoDAO = new JobInfoDAO(); jobInfoDAO.setSessionFactory(sessionFactory()); jobInfoDAO.afterPropertiesSet(); } return jobInfoDAO; } @Bean public ViewResolver viewResolver() { final InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver(); internalResourceViewResolver.setOrder(Ordered.HIGHEST_PRECEDENCE); internalResourceViewResolver.setPrefix("/WEB-INF/jsp/"); internalResourceViewResolver.setSuffix(".jsp"); internalResourceViewResolver.setViewClass(JstlView.class); return internalResourceViewResolver; } @Bean public Boolean enableSCP() throws IOException { if (enableSCP == null) { enableSCP = getConfigBool("scp.enable"); } return enableSCP; } @Bean public File storageRootDir() throws IOException { if (storageRootDir == null) { final String prop = getConfigString("scp.storage.path"); if (StringUtils.isNotBlank(prop)) { storageRootDir = new File(prop); storageRootDir.mkdirs(); } } return storageRootDir; } @Bean public String aeTitle() throws IOException { if (aeTitle == null) { final String prop = getConfigString("scp.aetitle"); if (StringUtils.isBlank(prop)) { aeTitle = ""; } else { aeTitle = prop; } } return aeTitle; } @Bean public Integer port() throws IOException { if (port == null) { port = getConfigInt("scp.port"); } return port; } @Bean public Integer reaperTimeoutMS() throws IOException { if (reaperTimeoutMS == null) { reaperTimeoutMS = getConfigInt("scp.reaper_timeout_ms"); } return reaperTimeoutMS; } @Bean public Boolean enableProcessor() throws IOException { if (enableProcessor == null) { enableProcessor = getConfigBool("processor.enable"); } return enableProcessor; } @Bean public URI serverURI() throws IOException, URISyntaxException { //TODO set this automatically from local information if (serverURI == null) { final String prop = getConfigString("processor.server_uri"); if (StringUtils.isNotBlank(prop)) { serverURI = new URI(prop); } } return serverURI; } @Bean public Boolean useXMLNotGPB() throws IOException { if (useXMLNotGPB == null) { useXMLNotGPB = getConfigBool("processor.use_xml_not_gpb"); } return useXMLNotGPB; } @Bean public Boolean deletePhysicalFiles() throws IOException { if (deletePhysicalFiles == null) { deletePhysicalFiles = getConfigBool("processor.delete_files"); } return deletePhysicalFiles; } @Bean public Boolean forceCreate() throws IOException { if (forceCreate == null) { forceCreate = getConfigBool("processor.force_create"); } return forceCreate; } @Bean public Integer binaryInlineThreshold() throws IOException { if (binaryInlineThreshold == null) { binaryInlineThreshold = getConfigInt("processor.binary_inline_threshold"); } return binaryInlineThreshold; } @Bean public Integer binaryItemResponseBufferSize() throws IOException { if (binaryItemResponseBufferSize == null) { binaryItemResponseBufferSize = getConfigInt("binaryitem.response.bufsize"); } return binaryItemResponseBufferSize; } @Bean public Integer binaryItemStreamBufferSize() throws IOException { if (binaryItemStreamBufferSize == null) { binaryItemStreamBufferSize = getConfigInt("binaryitem.stream.bufsize"); } return binaryItemStreamBufferSize; } @Bean public Integer fileResponseBufferSize() throws IOException { if (fileResponseBufferSize == null) { fileResponseBufferSize = getConfigInt("file.response.bufsize"); } return fileResponseBufferSize; } @Bean public Integer fileStreamBufferSize() throws IOException { if (fileStreamBufferSize == null) { fileStreamBufferSize = getConfigInt("file.stream.bufsize"); } return fileStreamBufferSize; } @Bean public String xmlStylesheet() { return "type=\"text/xsl\" href=\"style.xsl\""; } }