/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2014 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.appclient.server.core;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.ApplicationClientDescriptor;
import com.sun.enterprise.deployment.archivist.AppClientArchivist;
import com.sun.enterprise.deployment.deploy.shared.JarArchive;
import com.sun.enterprise.deployment.deploy.shared.OutputJarArchive;
import com.sun.logging.LogDomains;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.api.deployment.DeployCommandParameters;
import org.glassfish.api.deployment.DeploymentContext;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.appclient.server.core.jws.JavaWebStartInfo;
import org.glassfish.appclient.server.core.jws.servedcontent.DynamicContent;
import org.glassfish.appclient.server.core.jws.servedcontent.FixedContent;
import org.glassfish.appclient.server.core.jws.servedcontent.TokenHelper;
import org.glassfish.deployment.common.Artifacts;
import org.glassfish.deployment.common.Artifacts.FullAndPartURIs;
import org.glassfish.deployment.versioning.VersioningSyntaxException;
import org.glassfish.deployment.versioning.VersioningUtils;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.internal.deployment.ExtendedDeploymentContext;
/**
* Encapsulates logic that is specific to stand-alone app client
* deployments regarding the generation of app client files.
* <p>
* The facade JAR file - ${appName}Client.jar - will reside at the top of
* the user's local directory and will refer to the developer's original
* app client JAR which will reside in the ${appName}Client subdirectory
* within the user's download directory.
*
* @author tjquinn
*/
public class StandaloneAppClientDeployerHelper extends AppClientDeployerHelper {
private static final Logger logger = Logger.getLogger(JavaWebStartInfo.APPCLIENT_SERVER_MAIN_LOGGER,
JavaWebStartInfo.APPCLIENT_SERVER_LOGMESSAGE_RESOURCE);
private Set<FullAndPartURIs> clientLevelDownloads = null;
StandaloneAppClientDeployerHelper(final DeploymentContext dc,
final ApplicationClientDescriptor bundleDesc,
final AppClientArchivist archivist,
final ClassLoader gfClientModuleClassLoader,
final Application application,
final ServiceLocator habitat) throws IOException {
super(dc, bundleDesc, archivist, gfClientModuleClassLoader,
application, habitat);
}
/**
* Returns the name (no path, no type) of the facade JAR. This is used
* in both creating the full name and URI of the facade as well as for
* the name of a subdirectory in the user's download directory.
*
* @param dc
* @return
*/
private String facadeNameOnly(DeploymentContext dc) {
DeployCommandParameters params = dc.getCommandParameters(DeployCommandParameters.class);
final String appName = params.name();
try {
return VersioningUtils.getUntaggedName(appName) + "Client";
} catch (VersioningSyntaxException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
return appName + "Client";
}
@Override
public File rootForSignedFilesInApp() {
return new File(dc().getScratchDir("xml"), "signed/");
}
@Override
protected void prepareJARs() throws IOException, URISyntaxException {
super.prepareJARs();
/*
* The app client JAR will have been expanded and deleted, so create
* a JAR from the expanded directory.
*/
copyOriginalAppClientJAR(dc());
}
@Override
protected void addTopLevelContentToClientFacade(OutputJarArchive facadeArchive) throws IOException{
addClientPolicyFiles(facadeArchive);
}
@Override
public void createAndAddLibraryJNLPs(AppClientDeployerHelper helper, TokenHelper tHelper, Map<String, DynamicContent> dynamicContent) {
}
/**
* Copies a file's contents to the top-level JAR generated by this
* deployment.
*
* @param clientFacadeArchive
* @param f
* @param path
* @throws IOException
*/
@Override
protected void copyFileToTopLevelJAR(final OutputJarArchive clientFacadeArchive, final File f, final String path) throws IOException {
copyToArchive(f, clientFacadeArchive, path);
}
private void copyToArchive(final File inputFile, final OutputJarArchive outputArchive, final String pathInJar) throws IOException {
final OutputStream os = outputArchive.putNextEntry(pathInJar);
final InputStream is = new BufferedInputStream(new FileInputStream(inputFile));
final byte[] buffer = new byte[512];
int bytesRead;
try {
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
} finally {
try {
os.flush();
} finally {
is.close();
}
}
}
@Override
public FixedContent fixedContentWithinEAR(String uriString) {
/*
* There can be no fixed content within the EAR for a stand-alone
* app client.
*/
return null;
}
/**
* Returns the file name and type of the facade.
* <p>
* For stand-alone app clients, the facade is ${appName}Client.jar.
* @param dc
* @return
*/
@Override
protected String facadeFileNameAndType(DeploymentContext dc) {
return facadeNameOnly(dc) + ".jar";
}
/**
* Returns the URI for the generated facade JAR.
* <p>
* The facade is ${appName}Client.jar and for stand-alone app clients
* is stored at generated/xml/${appName}/${appName}Client.jar.
* @param dc
* @return
*/
@Override
public URI facadeServerURI(DeploymentContext dc) {
File genXMLDir = dc.getScratchDir("xml");
return genXMLDir.toURI().resolve(facadeFileNameAndType(dc));
}
/**
* Returns the URI for the facade within the user's download directory.
* <p>
* The facade for a stand-alone app client will reside at the top level
* of the user's download directory.
* @param dc
* @return
*/
@Override
public URI facadeUserURI(DeploymentContext dc) {
return URI.create(facadeFileNameAndType(dc));
}
@Override
public URI groupFacadeUserURI(DeploymentContext dc) {
return null;
}
@Override
public URI groupFacadeServerURI(DeploymentContext dc) {
return null;
}
/**
* Returns the URI for the developer's original app client JAR within the
* user's download directory.
*
* @param dc
* @return
*/
@Override
public URI appClientUserURI(DeploymentContext dc) {
return URI.create(facadeNameOnly(dc) + '/' + appClientURIWithinApp(dc));
}
/**
* Returns the URI to the server
* @param dc
* @return
*/
@Override
public URI appClientServerURI(DeploymentContext dc) {
File genXMLDir = dc.getScratchDir("xml");
return genXMLDir.toURI().resolve(appClientURIWithinApp(dc));
}
@Override
public URI appClientServerOriginalAnchor(DeploymentContext dc) {
return ((ExtendedDeploymentContext) dc).getOriginalSource().getURI();
}
/**
* Returns the URI for the app client within the artificial containing
* app. For stand-alone clients the module URI is reported as
* the directory into which the app client JAR was expanded, without the
* "_jar" suffix. To that we add .jar to get a URI at the "top-level"
* of the pseudo-containing app.
*
* @param dc
* @return
*/
@Override
public URI appClientURIWithinApp(DeploymentContext dc) {
String uriText = appClientDesc().getModuleDescriptor().getArchiveUri();
String uriRoot = "";
String archiveName = uriText;
int lastIndex = uriText.lastIndexOf("/");
if(lastIndex > -1) {
uriRoot = uriText.substring(0, lastIndex);
archiveName = uriText.substring(lastIndex);
}
try {
archiveName = VersioningUtils.getUntaggedName(archiveName);
} catch (VersioningSyntaxException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
}
// if ( ! uriText.endsWith(".jar")) {
// uriText += ".jar";
// }
if ( ! archiveName.endsWith(".jar")) {
archiveName += ".jar";
}
uriText = uriRoot + archiveName;
return URI.create(uriText);
}
@Override
protected Set<FullAndPartURIs> clientLevelDownloads() throws IOException {
if (clientLevelDownloads == null) {
/*
* Stand-alone client deployments involve these downloads:
* 1. the original app client JAR,
* 2. the facade JAR
*/
Set<FullAndPartURIs> downloads = new HashSet<FullAndPartURIs>();
downloads.add(new Artifacts.FullAndPartURIs(
appClientServerURI(dc()),
appClientUserURI(dc())));
downloads.add(new Artifacts.FullAndPartURIs(
facadeServerURI(dc()),
facadeUserURI(dc())));
clientLevelDownloads = downloads;
}
return clientLevelDownloads;
}
@Override
public Set<FullAndPartURIs> earLevelDownloads() throws IOException {
return Collections.EMPTY_SET;
}
@Override
public URI appClientUserURIForFacade(DeploymentContext dc) {
return appClientUserURI(dc);
}
@Override
public URI URIWithinAppDir(DeploymentContext dc, URI absoluteURI) {
return dc.getSource().getURI().relativize(absoluteURI);
}
@Override
public String pathToAppclientWithinApp(DeploymentContext dc) {
return "";
}
@Override
protected String facadeClassPath() {
/*
* For app client deployments, the facade class path refers only
* to the developer's original JAR, renamed to ${name}.orig.jar
* (or some similar name using orig-${unique-number} to avoid
* naming collisions.
*/
return appClientUserURI(dc()).toASCIIString();
}
protected void copyOriginalAppClientJAR(final DeploymentContext dc) throws IOException {
ReadableArchive originalSource = ((ExtendedDeploymentContext) dc).getOriginalSource();
originalSource.open(originalSource.getURI());
OutputJarArchive target = new OutputJarArchive();
target.create(appClientServerURI(dc));
/*
* Copy the manifest explicitly because ReadableArchive.entries()
* excludes the manifest.
*/
Manifest originalManifest = originalSource.getManifest();
OutputStream os = target.putNextEntry(JarFile.MANIFEST_NAME);
originalManifest.write(os);
target.closeEntry();
copyArchive(originalSource, target, Collections.EMPTY_SET);
target.close();
originalSource.close();
}
@Override
protected String PUScanTargets() {
return null;
}
}