001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *  http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    
020    package org.apache.geronimo.farm.deployment;
021    
022    import java.io.File;
023    import java.io.IOException;
024    import java.net.URL;
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Set;
028    
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    import org.apache.geronimo.farm.config.ClusterInfo;
032    import org.apache.geronimo.farm.config.ExtendedJMXConnectorInfo;
033    import org.apache.geronimo.farm.config.NodeInfo;
034    import org.apache.geronimo.deployment.plugin.remote.FileUploadClient;
035    import org.apache.geronimo.deployment.plugin.remote.FileUploadProgress;
036    import org.apache.geronimo.deployment.plugin.remote.FileUploadServletClient;
037    import org.apache.geronimo.gbean.AbstractName;
038    import org.apache.geronimo.gbean.AbstractNameQuery;
039    import org.apache.geronimo.gbean.GBeanInfo;
040    import org.apache.geronimo.gbean.GBeanInfoBuilder;
041    import org.apache.geronimo.kernel.Kernel;
042    import org.apache.geronimo.kernel.config.ConfigurationData;
043    import org.apache.geronimo.kernel.config.InvalidConfigException;
044    import org.apache.geronimo.kernel.repository.Artifact;
045    
046    /**
047     *
048     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
049     */
050    public class BasicClusterConfigurationStoreClient implements ClusterConfigurationStoreClient {
051        private static final Log log = LogFactory.getLog(BasicClusterConfigurationStoreClient.class);
052    
053        private static final String[] METHOD_SIGNATURE_INSTALL =
054            new String[] {ConfigurationData.class.getName(), File.class.getName()};
055        private static final String[] METHOD_SIGNATURE_UNINSTALL = new String[] {Artifact.class.getName()};
056    
057        private final AbstractNameQuery clusterConfigurationStoreNameQuery;
058        private final DirectoryPackager packager;
059        private final FileUploadClient fileUploadClient;
060    
061        public BasicClusterConfigurationStoreClient(AbstractNameQuery clusterConfigurationStoreNameQuery) {
062            if (null == clusterConfigurationStoreNameQuery) {
063                throw new IllegalArgumentException("clusterConfigurationStoreNameQuery is required");
064            }
065            this.clusterConfigurationStoreNameQuery = clusterConfigurationStoreNameQuery;
066            
067            packager = newDirectoryPackager();
068            fileUploadClient = newFileUploadClient();
069        }
070    
071        public void install(ClusterInfo clusterInfo, ConfigurationData configurationData)
072                throws IOException, InvalidConfigException {
073            Collection<NodeInfo> nodeInfos = clusterInfo.getNodeInfos();
074    
075            Collection<NodeInfo> installedToNodeInfos = new ArrayList<NodeInfo>();
076            for (NodeInfo nodeInfo : nodeInfos) {
077                try {
078                    install(nodeInfo, configurationData);
079                    installedToNodeInfos.add(nodeInfo);
080                } catch (Exception e) {
081                    uninstall(clusterInfo, configurationData.getId(), installedToNodeInfos);
082                    if (e instanceof IOException) {
083                        throw (IOException) e;
084                    } else if (e instanceof InvalidConfigException) {
085                        throw (InvalidConfigException) e;
086                    }
087                    throw (IOException) new IOException("See nested").initCause(e);
088                }
089            }
090        }
091    
092        public void uninstall(ClusterInfo clusterInfo, Artifact configId) {
093            uninstall(clusterInfo, configId, clusterInfo.getNodeInfos());
094        }
095    
096        protected void uninstall(ClusterInfo clusterInfo, Artifact configId, Collection<NodeInfo> installedToNodeInfos) {
097            for (NodeInfo nodeInfo : installedToNodeInfos) {
098                try {
099                    uninstall(nodeInfo, configId);
100                } catch (Exception e) {
101                    log.info("Ignoring error while uninstalling [" + configId + "]from [" + nodeInfo + "]", e);
102                }
103            }
104        }
105        
106        protected void install(NodeInfo nodeInfo, ConfigurationData configurationData) throws IOException {
107            Kernel kernel = nodeInfo.newKernel();
108    
109            AbstractName clusterConfigurationStoreName = searchClusterConfigurationStore(kernel);
110    
111            File configurationDataFile = uploadConfiguration(kernel, nodeInfo, configurationData);
112    
113            boolean inVMCall = nodeInfo.getConnectorInfo().isLocal();
114            File oldConfigurationDir = null;
115            if (inVMCall) {
116                oldConfigurationDir = configurationData.getConfigurationDir();
117            }
118            Object[] params = new Object[] {configurationData, configurationDataFile};
119            try {
120                kernel.invoke(clusterConfigurationStoreName, "install", params, METHOD_SIGNATURE_INSTALL);
121            } catch (Exception e) {
122                throw (IOException) new IOException("See nested").initCause(e);
123            } finally {
124                if (inVMCall) {
125                    configurationData.setConfigurationDir(oldConfigurationDir);
126                }
127            }
128        }
129    
130        protected void uninstall(NodeInfo nodeInfo, Artifact configId) throws IOException {
131            Kernel kernel = nodeInfo.newKernel();
132            
133            AbstractName clusterConfigurationStoreName = searchClusterConfigurationStore(kernel);
134            
135            Object[] params = new Object[] {configId};
136            try {
137                kernel.invoke(clusterConfigurationStoreName, "uninstall", params, METHOD_SIGNATURE_UNINSTALL);
138            } catch (Exception e) {
139                throw (IOException) new IOException("See nested").initCause(e);
140            }
141        }
142        
143        protected File uploadConfiguration(Kernel kernel, NodeInfo nodeInfo, ConfigurationData configurationData) throws IOException {
144            File packedConfigurationDir = packager.pack(configurationData.getConfigurationDir());
145    
146            if (nodeInfo.getConnectorInfo().isLocal()) {
147                return packedConfigurationDir;
148            }
149            
150            URL remoteDeployUploadURL = fileUploadClient.getRemoteDeployUploadURL(kernel);
151    
152            ConfigurationUploadProgress configurationUploadProgress = new ConfigurationUploadProgress(configurationData);
153            File[] configurationDataFiles = new File[] {packedConfigurationDir};
154            ExtendedJMXConnectorInfo connectorInfo = nodeInfo.getConnectorInfo();
155            fileUploadClient.uploadFilesToServer(remoteDeployUploadURL, 
156                connectorInfo.getUsername(),
157                connectorInfo.getPassword(),
158                configurationDataFiles,
159                configurationUploadProgress);
160    
161            if (configurationUploadProgress.failure) {
162                if (null != configurationUploadProgress.exception) {
163                    throw (IOException) new IOException("See nested").initCause(configurationUploadProgress.exception);
164                }
165                throw new IOException(configurationUploadProgress.failureMessage);
166            }
167            
168            return configurationDataFiles[0];
169        }
170    
171        protected DirectoryPackager newDirectoryPackager() {
172            return new ZipDirectoryPackager();
173        }
174    
175        protected FileUploadClient newFileUploadClient() {
176            return new FileUploadServletClient();
177        }
178    
179        protected AbstractName searchClusterConfigurationStore(Kernel kernel) throws IOException {
180            Set<AbstractName> clusterConfigurationStoreNames = kernel.listGBeans(clusterConfigurationStoreNameQuery);
181            if (1 != clusterConfigurationStoreNames.size()) {
182                throw new IOException("Cannot locate remote store. Found [" + clusterConfigurationStoreNames + "]");
183            }
184            return clusterConfigurationStoreNames.iterator().next();
185        }
186    
187        protected class ConfigurationUploadProgress implements FileUploadProgress {
188            private final ConfigurationData configurationData;
189            private boolean failure;
190            private Exception exception;
191            private String failureMessage;
192    
193            public ConfigurationUploadProgress(ConfigurationData configurationData) {
194                this.configurationData = configurationData;
195            }
196    
197            public void fail(String message) {
198                failure = true;
199                failureMessage = "Upload of configuration [" + configurationData.getId() + "] - [" + message + "]";
200                log.error("Upload of configuration [" + configurationData.getId() + "] - [" + message + "]");
201            }
202    
203            public void fail(Exception exception) {
204                failure = true;
205                this.exception = exception;
206                log.error("Upload of configuration [" + configurationData.getId() + "]", exception);
207            }
208    
209            public void updateStatus(String message) {
210                log.info("Upload of configuration [" + configurationData.getId() + "] - [" + message + "]");
211            }
212        }
213    
214        public static final GBeanInfo GBEAN_INFO;
215    
216        public static final String GBEAN_J2EE_TYPE = "ClusterConfigurationStoreClient";
217        public static final String GBEAN_ATTR_CLUSTER_CONF_STORE_NAME_QUERY = "clusterConfigurationStoreNameQuery";
218    
219        static {
220            GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(BasicClusterConfigurationStoreClient.class, GBEAN_J2EE_TYPE);
221            
222            builder.addAttribute(GBEAN_ATTR_CLUSTER_CONF_STORE_NAME_QUERY, AbstractNameQuery.class, true);
223            
224            builder.addInterface(ClusterConfigurationStoreClient.class);
225    
226            builder.setConstructor(new String[]{GBEAN_ATTR_CLUSTER_CONF_STORE_NAME_QUERY});
227    
228            GBEAN_INFO = builder.getBeanInfo();
229        }
230    
231        public static GBeanInfo getGBeanInfo() {
232            return GBEAN_INFO;
233        }
234        
235    }