Java tutorial
/* * Copyright (c) 2008-2012, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.application.djigzo.james.service; import java.io.StringWriter; import java.security.KeyPair; import java.security.KeyPairGenerator; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import mitm.application.djigzo.DKIMProperties; import mitm.application.djigzo.DjigzoConfigurator; import mitm.application.djigzo.GlobalPreferencesManager; import mitm.application.djigzo.UserProperties; import mitm.application.djigzo.admin.DefaultAdmins; import mitm.application.djigzo.service.SystemServices; import mitm.common.hibernate.SessionManagedAutoCommitProxyFactory; import mitm.common.hibernate.SessionManager; import mitm.common.hibernate.annotations.StartTransaction; import mitm.common.net.CipherSuiteOverrideSSLProtocolSocketFactory; import mitm.common.properties.HierarchicalPropertiesException; import mitm.common.reflection.ProxyFactoryException; import mitm.common.security.crl.ThreadedCRLStoreUpdater; import mitm.common.sms.SMSGateway; import mitm.common.util.Base32; import org.apache.avalon.framework.activity.Disposable; import org.apache.avalon.framework.configuration.Configurable; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; import org.apache.commons.lang.StringUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A James service which is used by Djigzo to initialize global resources like Spring registry etc. * * @author Martijn Brinkers * */ public class DjigzoServiceImpl extends AbstractLogEnabled implements Configurable, Disposable, Serviceable, DjigzoService { /* * The default nr of random bytes used for the generation of the server secret */ private static final int DEFAULT_SERVER_SECRET_LENGTH = 64; private String getElement(Configuration configuration, String parent) throws ConfigurationException { Configuration child = configuration.getChild(parent); String value = null; if (child != null) { value = child.getValue().trim(); } return value; } /* * Note: Don't use SLF4J or any other logger. We need to use the logger from James */ private void log(String message) { /* * Log to James log */ getLogger().info(message); /* * Also log to system.out to make sure the log will be readable on the standard log. * We cannot use Log4J or SLF4J here because the log has not yet been initialized. */ System.out.println(message); } /** * Called by James upon startup. */ @Override public void configure(Configuration configuration) throws ConfigurationException { log("configure DjigzoServiceImpl."); String springConfig = getElement(configuration, "springconfig"); String additionalSpringConfigDir = getElement(configuration, "additionalspringconfigdir"); String log4jConfig = getElement(configuration, "log4jconfig"); DjigzoConfigurator.configure(springConfig, additionalSpringConfigDir, log4jConfig); String cipherSuites = null; try { cipherSuites = getElement(configuration, "cipherSuites"); } catch (ConfigurationException e) { log("cipherSuites configuration parameter not found."); } /* * Make sure HTTPClient will only use certain cipher suites. We sometimes have problems with certain * cipher suites (100% CPU while setting up a connection). This seems to occur when BC is enabled. */ CipherSuiteOverrideSSLProtocolSocketFactory.register(cipherSuites); initializeSystemSettings(); initializeCRLStoreUpdater(); startSMSGateway(); addDefaultAdmins(); } /* * Inner helper class to enable automatic transaction support using @StartTransaction annotations */ static class AutoTranscationProxy { private final static Logger logger = LoggerFactory.getLogger(DjigzoServiceImpl.AutoTranscationProxy.class); /* * Read the factory properties from the properties file and synchronize the settings with the database */ @StartTransaction public void synchronizeFactoryProperties() throws ConfigurationException { GlobalPreferencesManager globalPreferencesManager = SystemServices.getGlobalPreferencesManager(); try { globalPreferencesManager.syncFactoryProperties(); } catch (HierarchicalPropertiesException e) { throw new ConfigurationException("Error synchronizing factory properties."); } } private void initializeServerSecret(UserProperties properties) throws ConfigurationException { try { /* * Generate a random global server secret if there is no server secret set */ if (StringUtils.isBlank(properties.getServerSecret())) { logger.info("Generating server secret."); byte[] secret = SystemServices.getRandomGenerator() .generateRandom(DEFAULT_SERVER_SECRET_LENGTH); properties.setServerSecret(Base32.encode(secret)); } } catch (HierarchicalPropertiesException e) { throw new ConfigurationException("Error initializing server secret."); } } private void initializeDKIMKeyPair(UserProperties properties) throws ConfigurationException { try { /* * Generate a DKIM key pair if it does not exist */ DKIMProperties dkimProperties = properties.getDKIMProperties(); if (StringUtils.isBlank(dkimProperties.getKeyPair())) { logger.info("Generating DKIM key pair."); /* * The DKIM pair will be generated with BC since we need the key to be * exportable (which doesn't work with an HSM) * * Note: if HSM support is required for this key, we need to store the * key in the key store. */ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); keyPairGenerator.initialize(2048); KeyPair keyPair = keyPairGenerator.generateKeyPair(); StringWriter pem = new StringWriter(); PEMWriter writer = new PEMWriter(pem); writer.writeObject(keyPair); writer.close(); dkimProperties.setKeyPair(pem.toString()); } } catch (Exception e) { throw new ConfigurationException("Error initializing DKIM key pair."); } } @StartTransaction public void initializeDefaultSettings() throws ConfigurationException { try { UserProperties properties = SystemServices.getGlobalPreferencesManager().getGlobalUserPreferences() .getProperties(); initializeServerSecret(properties); initializeDKIMKeyPair(properties); } catch (HierarchicalPropertiesException e) { throw new ConfigurationException("Error initializing default settings."); } } } /* * Initializes the system settings. * * TODO: Move this to a proper Spring bean that will be automatically started. */ private void initializeSystemSettings() throws ConfigurationException { SessionManager sessionManager = SystemServices.getSessionManager(); AutoTranscationProxy proxy; try { proxy = new SessionManagedAutoCommitProxyFactory<AutoTranscationProxy>(AutoTranscationProxy.class, sessionManager).createProxy(); } catch (ProxyFactoryException e) { throw new ConfigurationException("Error initializing AutoTranscationProxy."); } catch (NoSuchMethodException e) { throw new ConfigurationException("Error initializing AutoTranscationProxy."); } proxy.synchronizeFactoryProperties(); proxy.initializeDefaultSettings(); } private void startSMSGateway() { SMSGateway gateway = SystemServices.getSMSGateway(); gateway.start(); } /* * Adds all the default admins and authorities (roles) */ private void addDefaultAdmins() { DefaultAdmins defaultAdmins = SystemServices.getDefaultAdmins(); defaultAdmins.addDefaultAdminsAndAuthorities(); } private void initializeCRLStoreUpdater() throws ConfigurationException { ThreadedCRLStoreUpdater storeUpdater = SystemServices.getThreadedCRLStoreUpdater(); storeUpdater.start(); } @Override public void dispose() { log("Disposing DjigzoServiceImpl."); } @Override public void service(ServiceManager serviceManager) throws ServiceException { try { Context context = new InitialContext(); /* * We will bind the avalon service manager to the JNDI context so we can get a hold of the * service manager from spring using a JndiObjectFactoryBean. */ context = context.createSubcontext("djigzo"); context.bind("avalonServiceManager", serviceManager); } catch (NamingException e) { throw new ServiceException("DjigzoService", "Unable to register James service manager.", e); } } }