| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.sling.maven.bundlesupport.fsresource; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.jar.JarFile; |
| import java.util.jar.Manifest; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; |
| import org.apache.maven.model.Resource; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.logging.Log; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.sling.commons.osgi.ManifestHeader; |
| import org.apache.sling.commons.osgi.ManifestHeader.Entry; |
| |
| /** |
| * Manages OSGi configurations for File System Resource Provider for Sling-Initial-Content. |
| */ |
| public final class SlingInitialContentMounter { |
| |
| /** Header containing the sling initial content information. */ |
| private static final String HEADER_INITIAL_CONTENT = "Sling-Initial-Content"; |
| |
| private final Log log; |
| private final MavenProject project; |
| private final FsMountHelper helper; |
| |
| public SlingInitialContentMounter(Log log, CloseableHttpClient httpClient, MavenProject project) { |
| this.log = log; |
| this.project = project; |
| this.helper = new FsMountHelper(log, httpClient, project); |
| } |
| |
| /** |
| * Add configurations to a running OSGi instance for initial content. |
| * @param targetUrl The web console base url |
| * @param bundleFile The artifact (bundle) |
| * @throws MojoExecutionException Exception |
| */ |
| public void mount(final URI targetUrl, final File bundleFile) throws MojoExecutionException { |
| // first, let's get the manifest and see if initial content is configured |
| ManifestHeader header = null; |
| try { |
| final Manifest mf = getManifest(bundleFile); |
| final String value = mf.getMainAttributes().getValue(HEADER_INITIAL_CONTENT); |
| if ( value == null ) { |
| log.debug("Bundle has no initial content - no file system provider config created."); |
| return; |
| } |
| header = ManifestHeader.parse(value); |
| if ( header == null || header.getEntries().length == 0 ) { |
| log.warn("Unable to parse header or header is empty: " + value); |
| return; |
| } |
| } catch (IOException ioe) { |
| throw new MojoExecutionException("Unable to read manifest from file " + bundleFile, ioe); |
| } |
| |
| log.info("Trying to configure file system provider..."); |
| // quick check if resources are configured |
| final List resources = project.getResources(); |
| if ( resources == null || resources.size() == 0 ) { |
| throw new MojoExecutionException("No resources configured for this project."); |
| } |
| |
| final List<FsResourceConfiguration> cfgs = new ArrayList<>(); |
| final Entry[] entries = header.getEntries(); |
| for (final Entry entry : entries) { |
| String path = entry.getValue(); |
| if ( path != null && !path.endsWith("/") ) { |
| path += "/"; |
| } |
| // check if we should ignore this |
| final String ignoreValue = entry.getDirectiveValue("maven:mount"); |
| if ( ignoreValue != null && ignoreValue.equalsIgnoreCase("false") ) { |
| log.debug("Ignoring " + path); |
| continue; |
| } |
| String installPath = entry.getDirectiveValue("path"); |
| if ( installPath == null ) { |
| installPath = "/"; |
| } |
| // search the path in the resources (usually this should be the first resource |
| // entry but this might be reconfigured |
| File dir = null; |
| final Iterator i = resources.iterator(); |
| while ( dir == null && i.hasNext() ) { |
| final Resource rsrc = (Resource)i.next(); |
| String child = path; |
| // if resource mapping defines a target path: remove target path from checked resource path |
| String targetPath = rsrc.getTargetPath(); |
| if ( targetPath != null && !targetPath.endsWith("/") ) { |
| targetPath = targetPath + "/"; |
| } |
| if ( targetPath != null && path != null && path.startsWith(targetPath) ) { |
| child = child.substring(targetPath.length()); |
| } |
| dir = new File(rsrc.getDirectory(), child); |
| if ( !dir.exists() ) { |
| dir = null; |
| } |
| } |
| if ( dir == null ) { |
| throw new MojoExecutionException("No resource entry found containing " + path); |
| } |
| // check for root mapping - which we don't support atm |
| if ( "/".equals(installPath) ) { |
| throw new MojoExecutionException("Mapping to root path not supported by fs provider at the moment. Please adapt your initial content configuration."); |
| } |
| |
| // check further initial content directives |
| StringBuilder importOptions = new StringBuilder(); |
| String overwriteValue = entry.getDirectiveValue("overwrite"); |
| if (StringUtils.isNotBlank(overwriteValue)) { |
| importOptions.append("overwrite:=" + overwriteValue); |
| } |
| String ignoreImportProvidersValue = entry.getDirectiveValue("ignoreImportProviders"); |
| if (StringUtils.isNotBlank(overwriteValue)) { |
| if (importOptions.length() > 0) { |
| importOptions.append(";"); |
| } |
| importOptions.append("ignoreImportProviders:=\"" + ignoreImportProvidersValue + "\""); |
| } |
| |
| cfgs.add(new FsResourceConfiguration() |
| .fsMode(FsMode.INITIAL_CONTENT) |
| .contentRootDir(dir.getAbsolutePath()) |
| .providerRootPath(installPath) |
| .initialContentImportOptions(importOptions.toString())); |
| } |
| |
| if (!cfgs.isEmpty()) { |
| helper.addConfigurations(targetUrl, cfgs); |
| } |
| } |
| |
| /** |
| * Remove configurations from a running OSGi instance for initial content. |
| * @param targetUrl The web console base url |
| * @param bundleFile The artifact (bundle) |
| * @throws MojoExecutionException Exception |
| */ |
| public void unmount(final URI targetUrl, final File bundleFile) throws MojoExecutionException { |
| log.info("Removing file system provider configurations..."); |
| |
| // remove all current configs for this project |
| final Map<String,FsResourceConfiguration> oldConfigs = helper.getCurrentConfigurations(targetUrl); |
| helper.removeConfigurations(targetUrl, oldConfigs); |
| } |
| |
| /** |
| * Get the manifest from the File. |
| * @param bundleFile The bundle jar |
| * @return The manifest. |
| * @throws IOException Exception |
| */ |
| private Manifest getManifest(final File bundleFile) throws IOException { |
| JarFile file = null; |
| try { |
| file = new JarFile(bundleFile); |
| return file.getManifest(); |
| } |
| finally { |
| if (file != null) { |
| try { |
| file.close(); |
| } |
| catch (IOException ex) { |
| // ignore |
| } |
| } |
| } |
| } |
| |
| } |