001 /*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2009-2011 Oracle and/or its affiliates. All rights reserved.
005 *
006 * The contents of this file are subject to the terms of either the GNU
007 * General Public License Version 2 only ("GPL") or the Common Development
008 * and Distribution License("CDDL") (collectively, the "License"). You
009 * may not use this file except in compliance with the License. You can
010 * obtain a copy of the License at
011 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
012 * or packager/legal/LICENSE.txt. See the License for the specific
013 * language governing permissions and limitations under the License.
014 *
015 * When distributing the software, include this License Header Notice in each
016 * file and include the License file at packager/legal/LICENSE.txt.
017 *
018 * GPL Classpath Exception:
019 * Oracle designates this particular file as subject to the "Classpath"
020 * exception as provided by Oracle in the GPL Version 2 section of the License
021 * file that accompanied this code.
022 *
023 * Modifications:
024 * If applicable, add the following below the License Header, with the fields
025 * enclosed by brackets [] replaced by your own identifying information:
026 * "Portions Copyright [year] [name of copyright owner]"
027 *
028 * Contributor(s):
029 * If you wish your version of this file to be governed by only the CDDL or
030 * only the GPL Version 2, indicate your decision by adding "[Contributor]
031 * elects to include this software in this distribution under the [CDDL or GPL
032 * Version 2] license." If you don't indicate a single choice of license, a
033 * recipient has the option to distribute your version of this file under
034 * either the CDDL, the GPL Version 2 or to extend the choice of license to
035 * its licensees as provided above. However, if you add GPL Version 2 code
036 * and therefore, elected the GPL Version 2 license, then the option applies
037 * only if the new code is made subject to such option by the copyright
038 * holder.
039 */
040
041 package com.sun.enterprise.admin.cli.embeddable;
042
043 import org.glassfish.admin.payload.PayloadFilesManager;
044 import org.glassfish.admin.payload.PayloadImpl;
045 import org.glassfish.api.ActionReport;
046 import org.glassfish.api.admin.CommandException;
047 import org.glassfish.api.admin.ParameterMap;
048 import org.glassfish.api.admin.Payload;
049 import org.glassfish.embeddable.Deployer;
050 import org.glassfish.embeddable.GlassFishException;
051 import org.jvnet.hk2.annotations.ContractProvided;
052 import org.jvnet.hk2.annotations.Inject;
053 import org.jvnet.hk2.annotations.Scoped;
054 import org.jvnet.hk2.annotations.Service;
055 import org.jvnet.hk2.component.Habitat;
056 import org.jvnet.hk2.component.PerLookup;
057
058 import java.io.ByteArrayInputStream;
059 import java.io.ByteArrayOutputStream;
060 import java.io.File;
061 import java.io.FileInputStream;
062 import java.io.FileOutputStream;
063 import java.io.IOException;
064 import java.io.InputStream;
065 import java.io.OutputStream;
066 import java.net.URI;
067 import java.util.ArrayList;
068 import java.util.Collection;
069 import java.util.Properties;
070 import java.util.logging.Level;
071 import java.util.logging.Logger;
072
073 /**
074 * This is an implementation of {@link Deployer}.
075 * Unlike the other EmbeddedDeployer, this deployer uses admin command execution
076 * framework to execute the underlying command, as a result we don't by-pass things like command replication code.
077 *
078 * @author Sanjeeb.Sahoo@Sun.COM
079 */
080
081 @Service()
082 @Scoped(PerLookup.class)
083 @ContractProvided(Deployer.class) // bcos Deployer interface can't depend on HK2, we need ContractProvided here.
084 public class DeployerImpl implements Deployer {
085
086 private static final Logger logger =
087 Logger.getLogger(DeployerImpl.class.getPackage().getName());
088
089 /*
090 * This class currently copies generic URIs to a file before processing. Once deployment backend
091 * supports URI, we should be able to use URIs directly.
092 */
093
094 @Inject
095 Habitat habitat;
096
097 @Override
098 public String deploy(URI archive, String... params) throws GlassFishException {
099 File file;
100 try {
101 file = convertToFile(archive);
102 } catch (IOException e) {
103 throw new GlassFishException("Unable to make a file out of " + archive, e);
104 }
105 return deploy(file, params);
106 }
107
108 @Override
109 public String deploy(File file, String... params) throws GlassFishException {
110 String[] newParams = new String[params.length + 1];
111 System.arraycopy(params, 0, newParams, 0, params.length);
112 newParams[params.length] = file.getAbsolutePath();
113 CommandExecutorImpl executer = habitat.getComponent(CommandExecutorImpl.class);
114 try {
115 String command = "deploy";
116 ActionReport actionReport = executer.createActionReport();
117 ParameterMap commandParams = executer.getParameters(command, newParams);
118 org.glassfish.api.admin.CommandRunner.CommandInvocation inv =
119 executer.getCommandRunner().getCommandInvocation(command, actionReport);
120 inv.parameters(commandParams);
121 // set outputbound payload if --retrieve option is specified.
122 Payload.Outbound outboundPayload = null;
123 String retrieveOpt = commandParams.getOne("retrieve");
124 File retrieve = retrieveOpt != null ? new File(retrieveOpt) : null;
125 if (retrieve != null && retrieve.exists()) {
126 outboundPayload = PayloadImpl.Outbound.newInstance();
127 inv.outbound(outboundPayload);
128 }
129 inv.execute();
130 // extract the outbound payload.
131 if (outboundPayload != null) {
132 extractPayload(outboundPayload, actionReport, retrieve);
133 }
134
135 return actionReport.getResultType(String.class);
136 } catch (CommandException e) {
137 throw new GlassFishException(e);
138 }
139 }
140
141 @Override
142 public String deploy(InputStream is, String... params) throws GlassFishException {
143 try {
144 return deploy(createFile(is), params);
145 } catch (IOException e) {
146 throw new GlassFishException(e);
147 }
148 }
149
150 @Override
151 public void undeploy(String appName, String... params) throws GlassFishException {
152 String[] newParams = new String[params.length + 1];
153 System.arraycopy(params, 0, newParams, 0, params.length);
154 newParams[params.length] = appName;
155 CommandExecutorImpl executer = habitat.getComponent(CommandExecutorImpl.class);
156 try {
157 ActionReport actionReport = executer.executeCommand("undeploy", newParams);
158 actionReport.writeReport(System.out);
159 } catch (CommandException e) {
160 throw new GlassFishException(e);
161 } catch (IOException e) {
162 throw new GlassFishException(e);
163 }
164 }
165
166 @Override
167 public Collection<String> getDeployedApplications() throws GlassFishException {
168 try {
169 CommandExecutorImpl executer = habitat.getComponent(CommandExecutorImpl.class);
170 ActionReport report = executer.executeCommand("list-components");
171 Properties props = report.getTopMessagePart().getProps();
172 return new ArrayList<String>(props.stringPropertyNames());
173 } catch (Exception e) {
174 throw new GlassFishException(e);
175 }
176 }
177
178 private File convertToFile(URI archive) throws IOException {
179 File file;
180 if ("file".equalsIgnoreCase(archive.getScheme())) {
181 file = new File(archive);
182 } else {
183 file = createFile(archive.toURL().openStream());
184 }
185 return file;
186 }
187
188 private File createFile(InputStream in) throws IOException {
189 File file;
190 file = File.createTempFile("app", "tmp");
191 file.deleteOnExit();
192 OutputStream out = null;
193 try {
194 out = new FileOutputStream(file);
195 copyStream(in, out);
196 } finally {
197 if (in != null) {
198 try {
199 in.close();
200 } catch (IOException e) {
201 // ignore
202 }
203 }
204 if (out != null) {
205 try {
206 out.close();
207 } finally {
208 // ignore
209 }
210 }
211 }
212 return file;
213 }
214
215 private void copyStream(InputStream in, OutputStream out) throws IOException {
216 byte[] buf = new byte[4096];
217 int len;
218 while ((len = in.read(buf)) >= 0) {
219 out.write(buf, 0, len);
220 }
221 }
222
223 /**
224 * Extract the payload (client side stub jar files) to the directory specified via
225 * --retrieve option.
226 *
227 * @param outboundPayload Payload to be extracted
228 * @param actionReport ActionReport of the deploy command.
229 * @param retrieveDir Directory where the payload should be extracted to.
230 */
231 private void extractPayload(Payload.Outbound outboundPayload,
232 ActionReport actionReport, File retrieveDir) {
233 File payloadZip = null;
234 FileOutputStream payloadOutputStream = null;
235 FileInputStream payloadInputStream = null;
236 try {
237 /*
238 * Add the report to the payload to mimic what the normal
239 * non-embedded server does.
240 */
241 final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
242 actionReport.writeReport(baos);
243 final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
244 final Properties reportProps = new Properties();
245 reportProps.setProperty("data-request-type", "report");
246 outboundPayload.addPart(0, actionReport.getContentType(), "report", reportProps, bais);
247
248 /*
249 * Now process the payload as an *inbound* payload as the non-embedded
250 * admin client does, by writing the *outbound* payload to a temporary file
251 * then reading from that file.
252 */
253 payloadZip = File.createTempFile("appclient", ".zip");
254 payloadOutputStream = new FileOutputStream(payloadZip);
255 outboundPayload.writeTo(payloadOutputStream);
256 payloadOutputStream.flush();
257 payloadOutputStream.close();
258
259 /*
260 * Use the temp file's contents as the inbound payload to
261 * correctly process the downloaded files.
262 */
263 final PayloadFilesManager pfm = new PayloadFilesManager.Perm(
264 retrieveDir, null /* no action report to record extraction results */, logger);
265 payloadInputStream = new FileInputStream(payloadZip);
266 final PayloadImpl.Inbound inboundPayload = PayloadImpl.Inbound.newInstance(
267 "application/zip", payloadInputStream);
268 pfm.processParts(inboundPayload); // explodes the payloadZip.
269 } catch (Exception ex) {
270 // Log error and ignore exception.
271 logger.log(Level.WARNING, ex.getMessage(), ex);
272 } finally {
273 if (payloadOutputStream != null) {
274 try {
275 payloadOutputStream.close();
276 } catch (IOException ioex) {
277 logger.warning(ioex.getMessage());
278 }
279 }
280 if (payloadInputStream != null) {
281 try {
282 payloadInputStream.close();
283 } catch (IOException ioex) {
284 logger.warning(ioex.getMessage());
285 }
286 }
287 if (payloadZip != null) {
288 payloadZip.delete();
289 }
290 }
291 }
292
293 }