/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2009-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.jws;
import com.sun.logging.LogDomains;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.appclient.server.core.AppClientDeployerHelper;
import org.glassfish.appclient.server.core.ApplicationSignedJARManager;
import org.glassfish.appclient.server.core.NestedAppClientDeployerHelper;
import org.glassfish.appclient.server.core.StandaloneAppClientDeployerHelper;
import org.glassfish.appclient.server.core.jws.servedcontent.Content;
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.SimpleDynamicContentImpl;
import org.glassfish.appclient.server.core.jws.servedcontent.StaticContent;
import org.glassfish.logging.annotation.LogMessageInfo;
/**
* Abstracts the XPath information for developer-provided references to
* other resources, whether static content (such as JARs or native libraries)
* or dynamic content (such as other JNLP documents). (Note that these
* are dynamic content because the server adjusts them - even the
* developer-provided ones - at HTTP request time with, for example, the
* code base.)
*
* @param <T> either StaticContent or DynamicContent
*/
abstract class XPathToDeveloperProvidedContentRefs<T extends Content> {
private static final String STATIC_REFS_PROPERTY_NAME = "static.refs";
private static final String DYNAMIC_REFS_PROPERTY_NAME = "dynamic.refs";
private final static XPathFactory xPathFactory = XPathFactory.newInstance();
private final static XPath xPath = xPathFactory.newXPath();
private static final Logger logger = Logger.getLogger(JavaWebStartInfo.APPCLIENT_SERVER_MAIN_LOGGER, JavaWebStartInfo.APPCLIENT_SERVER_LOGMESSAGE_RESOURCE);
@LogMessageInfo (
message = "Client JNLP document {0} refers to the static resource {1} that does not exist or is not readable.",
cause = "The developer-provided JNLP content refers to a file as if the file is in the application but the server could not find the file.",
action = "Make sure the file is packaged in the application and that the reference to the file is correct. Then rebuild and redeploy the application.")
public static final String BAD_STATIC_CONTENT = "AS-ACDEPL-00111";
@LogMessageInfo (
message = "The ApplicationSignedJARManager for a nested app client deployer helper is unexpectedly null.",
cause = "During deployment of nested app clients (those inside EARs), the system should use an ApplicationSignedJARManager but it is null.",
action = "This is a system error. Please report this as a bug.")
public static final String SIGNED_JAR_MGR_NULL = "AS-ACDEPL-00114";
@LogMessageInfo(
message = "Tbe custom JNLP document {0} in a stand-alone app client incorrectly refers to a JAR {1}",
cause = "The app client includes a custom JNLP document which refers to a JAR. Stand-alone app clients cannot refer to other JARs because they are self-contained deployment units.",
action = "Remove references to JAR from the custom JNLP document or package the app client inside an EAR that also contains the referenced JAR.")
public static final String USER_REFERENCED_JAR = "AS-ACDEPL-00115";
private enum Type {
STATIC(STATIC_REFS_PROPERTY_NAME), DYNAMIC(DYNAMIC_REFS_PROPERTY_NAME);
private String propertyName;
Type(final String propName) {
this.propertyName = propName;
}
}
private final XPathExpression xPathExpr;
private XPathToDeveloperProvidedContentRefs(final String path) {
super();
try {
xPathExpr = xPath.compile(path);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static List<XPathToDeveloperProvidedContentRefs> parse(final Properties p) {
List<XPathToDeveloperProvidedContentRefs> result = new ArrayList<XPathToDeveloperProvidedContentRefs>();
result.addAll(XPathToDeveloperProvidedContentRefs.
parse(p, XPathToDeveloperProvidedContentRefs.Type.STATIC));
result.addAll(XPathToDeveloperProvidedContentRefs.
parse(p, XPathToDeveloperProvidedContentRefs.Type.DYNAMIC));
return result;
}
/**
* Extracts the relevant information from the Properties object and
* creates the correct set of content objects depending on which type
* of xpath reference the caller requested (static or dynamic).
* @param p Properties read from the on-disk config file
* @param type static or dynamic
* @return xpath-related objects for the selected type of reference points
*/
private static List<XPathToDeveloperProvidedContentRefs> parse(final Properties p, Type type) {
final List<XPathToDeveloperProvidedContentRefs> result = new ArrayList<XPathToDeveloperProvidedContentRefs>();
final String refs = p.getProperty(type.propertyName);
for (String ref : refs.split(",")) {
result.add((type == Type.STATIC) ? new XPathToStaticContent(ref) : new XPathToDynamicContent(ref));
}
return result;
}
XPathExpression xPathExpr() {
return xPathExpr;
}
/**
* Adds the referenced data for this object to either the static
* or dynamic content, depending on whether this object is for
* static or dynamic content.
* <p>
* The concrete implementation in the subclasses will actually
* update either staticContent or dynamicContent but not both.
* But providing both as arguments lets the caller not worry about
* which type of xpath content this object is.
*
* @param codebase the code base from the containing document
* @param pathToContent location of the content (relative or absolute)
* @param loader class loader which could be used to locate referenced content
* @param staticContent static content map
* @param dynamicContent dynamic content map
* @param appRootURI root URI for the application
* @throws URISyntaxException
* @throws IOException
*/
abstract void addToContentIfInApp(
DeveloperContentHandler dch,
final AppClientDeployerHelper helper,
String referringDocument,
URI codebase, String pathToContent,
ClassLoader loader, Map<String, StaticContent> staticContent,
Map<String, DynamicContent> dynamicContent, final URI appRootURI,
final ReadableArchive appClientArchive)
throws URISyntaxException, IOException;
/**
* Models XPath-related information for a developer-provided reference to
* static content (such as to a JAR, a native library, or an image).
*/
private static class XPathToStaticContent extends XPathToDeveloperProvidedContentRefs<StaticContent> {
XPathToStaticContent(final String path) {
super(path);
}
@Override
void addToContentIfInApp(
final DeveloperContentHandler dch,
final AppClientDeployerHelper helper,
final String referringDocument,
final URI codebase,
final String pathToContent,
final ClassLoader loader,
final Map<String,StaticContent> staticContent,
final Map<String,DynamicContent> dynamicContent,
final URI appRootURI,
final ReadableArchive appClientArchive) throws URISyntaxException {
final URI uriToContent = new URI(pathToContent);
final URI absURI = codebase.resolve(uriToContent);
if (absURI.equals(uriToContent)) {
return;
}
final URI fileURI = appRootURI.resolve(pathToContent);
final File f = new File(fileURI);
/*
* The developer might have referred to a JAR or other static file
* that is not actually in the app. If so, log a warning.
*/
if ( ! f.exists() || ! f.canRead()) {
logger.log(Level.WARNING,
BAD_STATIC_CONTENT,
new Object[] {referringDocument, pathToContent});
} else {
final ApplicationSignedJARManager signedJARManager = helper.signedJARManager();
if (signedJARManager == null && helper instanceof NestedAppClientDeployerHelper) {
/*
* The signed JAR manager should not be null when we deploy
* an app client nested inside an EAR. This is a system error.
*/
logger.log(Level.SEVERE, SIGNED_JAR_MGR_NULL);
} else if (helper instanceof StandaloneAppClientDeployerHelper) {
logger.log(Level.WARNING, USER_REFERENCED_JAR,
new Object[] {referringDocument, pathToContent});
} else {
try {
final URI signedURI = signedJARManager.addJAR(fileURI);
staticContent.put(pathToContent, signedJARManager.staticContent(signedURI));
} catch (IOException ex) {
logger.log(Level.SEVERE, null, ex);
}
}
}
}
}
/**
* Models Xpath-related information for a developer-provided reference to
* dynamic content (such as another JNLP document).
*/
private static class XPathToDynamicContent extends XPathToDeveloperProvidedContentRefs<DynamicContent>{
XPathToDynamicContent(final String path) {
super(path);
}
@Override
void addToContentIfInApp(
final DeveloperContentHandler dch,
final AppClientDeployerHelper helper,
final String referringDocument,
final URI codebase,
final String pathToContent,
final ClassLoader loader,
final Map<String,StaticContent> staticContent,
final Map<String,DynamicContent> dynamicContent,
final URI appRootURI,
final ReadableArchive appClientArchive) throws URISyntaxException, IOException {
final URI uriToContent = new URI(pathToContent);
final URI absURI = codebase.resolve(uriToContent);
if (absURI.equals(uriToContent)) {
return;
}
/*
* Find the developer-provided content.
*/
// InputStream is = JavaWebStartInfo.openEntry(appClientArchive, pathToContent);
// if (is == null) {
// return;
// }
//
// final byte[] buffer = new byte[1024];
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// int bytesRead;
//
// while ((bytesRead = is.read(buffer)) != -1) {
// baos.write(buffer, 0, bytesRead);
// }
// is.close();
/*
* Combine the developer's extension JNLP with the template so we
* can set the parts of the resulting document that we need to
* control.
*/
final String combinedContent = dch.combineJNLP(
JavaWebStartInfo.textFromURL(
JavaWebStartInfo.DEVELOPER_EXTENSION_DOCUMENT_TEMPLATE),
pathToContent);
dynamicContent.put(pathToContent,
new SimpleDynamicContentImpl(
combinedContent,
URLConnection.guessContentTypeFromName(pathToContent)));
/*
* Currently the only dynamic content processed from the developer's
* JNLP is an <extension> element which refers to another
* JNLP document. So we need to recursively process that
* document now also.
*/
dch.addDeveloperContent(pathToContent, combinedContent);
}
}
}