diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5fd4d5023f1463b5ba3970e33c460c1eb26d748d Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..eb91947648c859bf8ab9101dbe58bafb82db371a --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.3/apache-maven-3.3.3-bin.zip \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..49f275d0d88ed2c10de16d338f3d78fa5452ff84 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ + +# Sonatype Nexus Open-source Codebase + +## Requirements + +* Apache Maven 3.0.4+ +* Java 7+ +* Network access to https://repository.sonatype.org/content/groups/sonatype-public-grid + +## Building + +To build the project and generate the template assembly use the included Maven wrapper: + + ./mvnw clean install + +## Running + +To run Nexus, after building, unzip the assembly and start the server: + + unzip -d target assemblies/nexus-base-template/target/nexus-base-template-*.zip + ./target/nexus-base-template-*/bin/nexus console + +The `nexus-base-template` assembly is used as the basis for the official Sonatype Nexus distributions. diff --git a/assemblies/karaf-nexus-branding/pom.xml b/assemblies/karaf-nexus-branding/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..154f76aeed2b087ee8e1c10b8cdf34044ba7f84d --- /dev/null +++ b/assemblies/karaf-nexus-branding/pom.xml @@ -0,0 +1,66 @@ + + + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.0.0-SNAPSHOT + + + + karaf-nexus-branding + ${project.groupId}:${project.artifactId} + bundle + + + + 1.5 + 1.5 + + + + + org.osgi + org.osgi.core + provided + + + + org.apache.karaf + org.apache.karaf.main + provided + + + + + + + org.apache.felix + maven-bundle-plugin + + + + + diff --git a/assemblies/karaf-nexus-branding/src/main/filtered-resources/org/apache/karaf/branding/branding.properties b/assemblies/karaf-nexus-branding/src/main/filtered-resources/org/apache/karaf/branding/branding.properties new file mode 100644 index 0000000000000000000000000000000000000000..634fdff74fc5006c5d018dd4c15c3ffc18d5258e --- /dev/null +++ b/assemblies/karaf-nexus-branding/src/main/filtered-resources/org/apache/karaf/branding/branding.properties @@ -0,0 +1,26 @@ +# +# Sonatype Nexus (TM) Open Source Version +# Copyright (c) 2008-present Sonatype, Inc. +# All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. +# +# This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, +# which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. +# +# Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks +# of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the +# Eclipse Foundation. All other trademarks are the property of their respective owners. +# + +welcome=\ + \u001B[36m _ __ \u001B[0m\r\n\ + \u001B[36m / | / /__ _ ____ _______ \u001B[0m\r\n\ + \u001B[36m / |/ / _ \\| |/_/ / / / ___/ \u001B[0m\r\n\ + \u001B[36m / /| / __/> \u001B[0m' for a list of available commands\r\n\ + and '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\r\n\ + Hit '\u001B[1m\u001B[0m' or '\u001B[1msystem:shutdown\u001B[0m' to shutdown.\r\n + diff --git a/assemblies/karaf-nexus-branding/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties b/assemblies/karaf-nexus-branding/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties new file mode 100644 index 0000000000000000000000000000000000000000..46b0bc8be3ca1a13f159160ea87484c0c54482b8 --- /dev/null +++ b/assemblies/karaf-nexus-branding/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties @@ -0,0 +1,27 @@ +# +# Sonatype Nexus (TM) Open Source Version +# Copyright (c) 2008-present Sonatype, Inc. +# All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. +# +# This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, +# which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. +# +# Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks +# of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the +# Eclipse Foundation. All other trademarks are the property of their respective owners. +# + +welcome=\ + \u001B[36m _ __ \u001B[0m\r\n\ + \u001B[36m / | / /__ _ ____ _______ \u001B[0m\r\n\ + \u001B[36m / |/ / _ \\| |/_/ / / / ___/ \u001B[0m\r\n\ + \u001B[36m / /| / __/> \u001B[0m' for a list of available commands\r\n\ + and '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\r\n\ + Hit '\u001B[1m\u001B[0m' or '\u001B[1msystem:shutdown\u001B[0m' to shutdown.\r\n\ + Hit '\u001B[1m\u001B[0m' or type '\u001B[1mlogout\u001B[0m' to disconnect shell from current session.\r\n + diff --git a/assemblies/karaf-nexus-branding/src/main/java/org/sonatype/nexus/karaf/LockFile.java b/assemblies/karaf-nexus-branding/src/main/java/org/sonatype/nexus/karaf/LockFile.java new file mode 100644 index 0000000000000000000000000000000000000000..ada305fc81a55132a7767bcac0e20933d9909d14 --- /dev/null +++ b/assemblies/karaf-nexus-branding/src/main/java/org/sonatype/nexus/karaf/LockFile.java @@ -0,0 +1,150 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.karaf; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.management.ManagementFactory; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.charset.Charset; + +/** + * This is a copy of LockFile from nexus-bootstrap, adapted to run in the limited environment of Karaf's launcher. + * + * File locker implementation, inspired by Eclipse Locker. It uses Java NIO {@link FileChannel#tryLock(long, long, + * boolean)} method to perform locking. As a commodity function, it also writes to a file a payload, making problem + * diagnosing a bit easier, as reading (ie. from console) of the lock file content might reveal useful information + * about lock owner. + * + * All the limitations mentioned for {@link FileLock} stands. + * + * @since 3.0 + */ +public class LockFile +{ + private static final byte[] DEFAULT_PAYLOAD = ManagementFactory.getRuntimeMXBean().getName().getBytes( + Charset.forName("UTF-8")); + + private final File lockFile; + + private final byte[] payload; + + private FileLock fileLock; + + private RandomAccessFile randomAccessFile; + + /** + * Creates a LockFile with default payload (that contains the JVM name, usually {@code PID@hostname}). + */ + public LockFile(final File lockFile) { + this(lockFile, DEFAULT_PAYLOAD); + } + + /** + * Creates a LockFile with custom payload. + */ + public LockFile(final File lockFile, final byte[] payload) { + if (lockFile == null || payload == null) { + throw new NullPointerException(); + } + this.lockFile = lockFile; + this.payload = payload; + } + + /** + * Returns the file used by this instance. + */ + public File getFile() { + return lockFile; + } + + /** + * Returns the payload used by this instance. + */ + public byte[] getPayload() { + return payload; + } + + /** + * Performs locking. If returns {@code true}, locking was successful and caller holds the lock. Multiple invocations, + * after lock is acquired, does not have any effect, locking happens only once. + */ + public synchronized boolean lock() { + if (fileLock != null) { + return true; + } + try { + randomAccessFile = new RandomAccessFile(lockFile, "rws"); + fileLock = randomAccessFile.getChannel().tryLock(0L, 1L, false); + if (fileLock != null) { + randomAccessFile.setLength(0); + randomAccessFile.seek(0); + randomAccessFile.write(payload); + } + } + catch (Exception e) { + // logging is not configured yet, so use console + System.err.println("Failed to write lock file"); + e.printStackTrace(); + // handle it as null result + fileLock = null; + } + finally { + if (fileLock == null) { + release(); + return false; + } + } + return true; + } + + /** + * Releases the lock. Multiple invocations of this file are possible, release will happen only once. + */ + public synchronized void release() { + close(fileLock); + fileLock = null; + close(randomAccessFile); + randomAccessFile = null; + } + + /** + * Reads the contents of the lock file for confirmation purposes; only call this method when a lock has been + * obtained. Package-scoped as this is only used by tests. + */ + byte[] readBytes() throws IOException { + if (randomAccessFile == null) { + throw new IllegalStateException("No lock obtained, cannot read file contents."); + } + + byte[] buffer = new byte[(int) randomAccessFile.length()]; + randomAccessFile.seek(0); + randomAccessFile.read(buffer, 0, buffer.length); + return buffer; + } + + // == + + private static void close(AutoCloseable closeable) { + if (closeable != null) { + try { + closeable.close(); + } + catch (Exception e) { + // muted + } + } + } +} diff --git a/assemblies/karaf-nexus-branding/src/main/java/org/sonatype/nexus/karaf/NexusFileLock.java b/assemblies/karaf-nexus-branding/src/main/java/org/sonatype/nexus/karaf/NexusFileLock.java new file mode 100644 index 0000000000000000000000000000000000000000..53d843c0c7a65d636591c9d50ea250351a8ec9c3 --- /dev/null +++ b/assemblies/karaf-nexus-branding/src/main/java/org/sonatype/nexus/karaf/NexusFileLock.java @@ -0,0 +1,66 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.karaf; + +import java.io.File; + +import org.apache.felix.utils.properties.Properties; +import org.apache.karaf.main.ConfigProperties; +import org.apache.karaf.main.lock.Lock; +import org.osgi.framework.Version; + +/** + * Nexus implementation of Karaf's {@link Lock} that exits this process if another process has the lock. + * Also checks that the current JVM satisfies Nexus minimum version requirement. We do this here because + * this is the earliest place we can intercept the Karaf launch process. + * + * @since 3.0 + */ +public class NexusFileLock + implements Lock +{ + private static final Version MINIMUM_JAVA_VERSION = new Version(1, 8, 0); + + private final String dataDir; + + private final LockFile lockFile; + + public NexusFileLock(Properties properties) { + String currentVersion = System.getProperty("java.version"); + if (MINIMUM_JAVA_VERSION.compareTo(new Version(currentVersion.replace('_', '.'))) > 0) { + // logging is not configured yet, so use console + System.err.println("Nexus requires minimum java.version: " + MINIMUM_JAVA_VERSION); + System.exit(-1); + } + + dataDir = properties.getProperty(ConfigProperties.PROP_KARAF_DATA); + lockFile = new LockFile(new File(dataDir, "lock")); + } + + public boolean lock() { + if (!lockFile.lock()) { + // logging is not configured yet, so use console + System.err.println("Nexus data directory already in use: " + dataDir); + System.exit(-1); + } + return true; + } + + public void release() { + lockFile.release(); + } + + public boolean isAlive() { + return true; + } +} diff --git a/assemblies/nexus-base-edition/pom.xml b/assemblies/nexus-base-edition/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..be903fc428c2ae9465b9f06d2ded2c091cff85db --- /dev/null +++ b/assemblies/nexus-base-edition/pom.xml @@ -0,0 +1,77 @@ + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.0.0-SNAPSHOT + + + nexus-base-edition + ${project.groupId}:${project.artifactId} + feature + + + + org.sonatype.nexus + nexus-extender + + + + org.sonatype.nexus + nexus-plugin-api + + + + org.sonatype.nexus + nexus-core + + + + + + + org.codehaus.gmaven + gmaven-plugin + + + mvn-coordinates + initialize + + execute + + + + project.artifactMap.each{k,v-> + // add 'mvn:artifactId' properties representing Pax-URL path for each transitive artifact + project.properties['mvn:'+v.artifactId]='mvn:'+v.groupId+'/'+v.artifactId+'/'+v.version + } + + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + + + diff --git a/assemblies/nexus-base-edition/src/main/feature/feature.xml b/assemblies/nexus-base-edition/src/main/feature/feature.xml new file mode 100644 index 0000000000000000000000000000000000000000..4971fa42e53cee3e6087c78d8e32be4d6606839e --- /dev/null +++ b/assemblies/nexus-base-edition/src/main/feature/feature.xml @@ -0,0 +1,30 @@ + + + + + wrap:${mvn:javax.el}$overwrite=merge&Fragment-Host=org.hibernate.validator + wrap:${mvn:validation-api}$overwrite=merge&Fragment-Host=org.hibernate.validator + wrap:${mvn:paranamer}$overwrite=merge&Fragment-Host=org.hibernate.validator + wrap:${mvn:httpcore}$Bundle-SymbolicName=httpcore + wrap:${mvn:httpclient}$Bundle-SymbolicName=httpclient&Fragment-Host=httpcore + + ${mvn:nexus-extender} + + diff --git a/assemblies/nexus-base-template/pom.xml b/assemblies/nexus-base-template/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..b83827e285694155e68bf1d1f332338b1a545238 --- /dev/null +++ b/assemblies/nexus-base-template/pom.xml @@ -0,0 +1,327 @@ + + + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.0.0-SNAPSHOT + + + nexus-base-template + ${project.groupId}:${project.artifactId} + karaf-assembly + + + + org.sonatype.nexus.assemblies + karaf-nexus-branding + provided + + + + + org.apache.karaf.features + framework + kar + compile + + + + org.apache.karaf.features + standard + features + xml + runtime + + + + + org.sonatype.nexus.assemblies + nexus-boot-feature + features + xml + runtime + + + + org.sonatype.nexus.assemblies + nexus-base-edition + features + xml + runtime + + + + org.sonatype.nexus + nexus-oss-edition + features + xml + runtime + + + + + + org.sonatype.nexus.plugins + nexus-quartz-plugin + features + xml + runtime + + + + org.sonatype.nexus.plugins + nexus-siesta-plugin + features + xml + runtime + + + + org.sonatype.nexus.plugins + nexus-ssl-plugin + features + xml + runtime + + + + org.sonatype.nexus.plugins + nexus-rapture-plugin + features + xml + runtime + + + + org.sonatype.nexus.plugins + nexus-extdirect-plugin + features + xml + runtime + + + + org.sonatype.nexus.plugins + nexus-coreui-plugin + features + xml + runtime + + + + org.sonatype.nexus.plugins + nexus-repository-httpbridge + features + xml + runtime + + + + org.sonatype.nexus.plugins + nexus-repository-maven + features + xml + runtime + + + + org.sonatype.nexus.plugins + nexus-repository-raw + features + xml + runtime + + + + + com.orientechnologies + orientdb-community + distribution + zip + runtime + + + * + * + + + + + + + + + org.codehaus.gmaven + gmaven-plugin + + + mvn-coordinates + initialize + + execute + + + + project.artifactMap.each{k,v-> + // add 'mvn:artifactId' properties representing Pax-URL path for each transitive artifact + project.properties['mvn:'+v.artifactId]='mvn\:'+v.groupId+'/'+v.artifactId+'/'+v.baseVersion + } + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + configure-assembly + prepare-package + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + false + false + + config + standard + package + nexus-boot-feature + + + management + nexus-oss-edition + nexus-quartz-plugin + nexus-siesta-plugin + nexus-ssl-plugin + nexus-rapture-plugin + nexus-extdirect-plugin + nexus-coreui-plugin + nexus-repository-httpbridge + nexus-repository-maven + nexus-repository-raw + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + bundle + package + + single + + + false + + ${project.basedir}/src/main/assembly/bundle.xml + + + + + + + + + diff --git a/assemblies/nexus-base-template/src/main/assembly/bundle.xml b/assemblies/nexus-base-template/src/main/assembly/bundle.xml new file mode 100644 index 0000000000000000000000000000000000000000..879b05a84aa8a2fcdc3220002c71f6c9d5935943 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/assembly/bundle.xml @@ -0,0 +1,65 @@ + + + + + bundle + + + zip + + + false + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0644 + 0755 + + bin/* + + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0644 + 0755 + dos + + bin/*.bat + + + + + ${project.build.directory}/assembly + ${project.build.finalName} + 0755 + 0755 + unix + + bin/* + + + bin/*.bat + + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/LICENSE.txt b/assemblies/nexus-base-template/src/main/resources/overlay/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..d6b3020fc7e5fcc7971e2df653af622de77ca06d --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/LICENSE.txt @@ -0,0 +1,87 @@ +Eclipse Public License -v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + +a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and + +b) in the case of each subsequent Contributor: + +i) changes to the Program, and + +ii) additions to the Program; + +where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + +a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. + +b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. + +c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. + +d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +a) it complies with the terms and conditions of this Agreement; and + +b) its license agreement: + +i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; + +ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; + +iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and + +iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + +a) it must be made available under this Agreement; and + +b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/NOTICE.txt b/assemblies/nexus-base-template/src/main/resources/overlay/NOTICE.txt new file mode 100644 index 0000000000000000000000000000000000000000..d88aaa17c4c41dd8fbe45e4a54c362eec98e2506 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/NOTICE.txt @@ -0,0 +1,10 @@ +Sonatype Nexus (TM) Open Source Version. +Copyright (c) 2008-present Sonatype, Inc. All rights reserved. +All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions + +This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, +which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + +Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks +of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the +Eclipse Foundation. All other trademarks are the property of their respective owners. diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/instance b/assemblies/nexus-base-template/src/main/resources/overlay/bin/instance new file mode 100644 index 0000000000000000000000000000000000000000..105146205f139460ef3a46fc5a02b45cc7209229 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/bin/instance @@ -0,0 +1,340 @@ +#!/bin/sh +DIRNAME=`dirname "$0"` +PROGNAME=`basename "$0"` + +# +# Sourcing environment settings for karaf similar to tomcats setenv +# +KARAF_SCRIPT="instance" +export KARAF_SCRIPT +if [ -f "$DIRNAME/setenv" ]; then + . "$DIRNAME/setenv" +fi + +# +# Check/Set up some easily accessible MIN/MAX params for JVM mem usage +# +if [ "x$JAVA_MIN_MEM" = "x" ]; then + JAVA_MIN_MEM=128M + export JAVA_MIN_MEM +fi +if [ "x$JAVA_MAX_MEM" = "x" ]; then + JAVA_MAX_MEM=512M + export JAVA_MAX_MEM +fi + +warn() { + echo "${PROGNAME}: $*" +} + +die() { + warn "$*" + exit 1 +} + +detectOS() { + # OS specific support (must be 'true' or 'false'). + cygwin=false; + darwin=false; + aix=false; + os400=false; + case "`uname`" in + CYGWIN*) + cygwin=true + ;; + Darwin*) + darwin=true + ;; + AIX*) + aix=true + ;; + OS400*) + os400=true + ;; + esac + # For AIX, set an environment variable + if $aix; then + export LDR_CNTRL=MAXDATA=0xB0000000@DSA + echo $LDR_CNTRL + fi +} + +unlimitFD() { + # Use the maximum available, or set MAX_FD != -1 to use that + if [ "x$MAX_FD" = "x" ]; then + MAX_FD="maximum" + fi + + # Increase the maximum file descriptors if we can + if [ "$os400" = "false" ] && [ "$cygwin" = "false" ]; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ "$MAX_FD_LIMIT" != 'unlimited' ]; then + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + # use the system max + MAX_FD="$MAX_FD_LIMIT" + fi + + ulimit -n $MAX_FD > /dev/null + # echo "ulimit -n" `ulimit -n` + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query system maximum file descriptor limit: $MAX_FD_LIMIT" + fi + fi + fi +} + +locateHome() { + if [ "x$KARAF_HOME" != "x" ]; then + warn "Ignoring predefined value for KARAF_HOME" + fi + + # In POSIX shells, CDPATH may cause cd to write to stdout + (unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + KARAF_HOME=`cd "$DIRNAME/.."; pwd` + if [ ! -d "$KARAF_HOME" ]; then + die "KARAF_HOME is not valid: $KARAF_HOME" + fi +} + +locateBase() { + if [ "x$KARAF_BASE" != "x" ]; then + if [ ! -d "$KARAF_BASE" ]; then + die "KARAF_BASE is not valid: $KARAF_BASE" + fi + else + KARAF_BASE=$KARAF_HOME + fi +} + +locateData() { + if [ "x$KARAF_DATA" != "x" ]; then + if [ ! -d "$KARAF_DATA" ]; then + die "KARAF_DATA is not valid: $KARAF_DATA" + fi + else + KARAF_DATA=$KARAF_BASE/data + fi +} + +locateEtc() { + if [ "x$KARAF_ETC" != "x" ]; then + if [ ! -d "$KARAF_ETC" ]; then + die "KARAF_ETC is not valid: $KARAF_ETC" + fi + else + KARAF_ETC=$KARAF_BASE/etc + fi +} + +setupNativePath() { + # Support for loading native libraries + LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$KARAF_BASE/lib:$KARAF_HOME/lib" + + # For Cygwin, set PATH from LD_LIBRARY_PATH + if $cygwin; then + LD_LIBRARY_PATH=`cygpath --path --windows "$LD_LIBRARY_PATH"` + PATH="$PATH;$LD_LIBRARY_PATH" + export PATH + fi + export LD_LIBRARY_PATH +} + +pathCanonical() { + local dst="${1}" + while [ -h "${dst}" ] ; do + ls=`ls -ld "${dst}"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + dst="$link" + else + dst="`dirname "${dst}"`/$link" + fi + done + local bas=`basename "${dst}"` + local dir=`dirname "${dst}"` + if [ "$bas" != "$dir" ]; then + dst="`pathCanonical "$dir"`/$bas" + fi + echo "${dst}" | sed -e 's#//#/#g' -e 's#/./#/#g' -e 's#/[^/]*/../#/#g' +} + +locateJava() { + # Setup the Java Virtual Machine + if $cygwin ; then + [ -n "$JAVA" ] && JAVA=`cygpath --unix "$JAVA"` + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + fi + + if [ "x$JAVA_HOME" = "x" ] && [ "$darwin" = "true" ]; then + JAVA_HOME="$(/usr/libexec/java_home -v 1.7)" + fi + if [ "x$JAVA" = "x" ] && [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi + if [ "x$JAVA" = "x" ]; then + if [ "x$JAVA_HOME" != "x" ]; then + if [ ! -d "$JAVA_HOME" ]; then + die "JAVA_HOME is not valid: $JAVA_HOME" + fi + JAVA="$JAVA_HOME/bin/java" + else + warn "JAVA_HOME not set; results may vary" + JAVA=`type java` + JAVA=`expr "$JAVA" : '.* \(/.*\)$'` + if [ "x$JAVA" = "x" ]; then + die "java command not found" + fi + fi + fi + if [ "x$JAVA_HOME" = "x" ]; then + JAVA_HOME="$(dirname $(dirname $(pathCanonical "$JAVA")))" + fi +} + +detectJVM() { + #echo "`$JAVA -version`" + # This service should call `java -version`, + # read stdout, and look for hints + if $JAVA -version 2>&1 | grep "^IBM" ; then + JVM_VENDOR="IBM" + # on OS/400, java -version does not contain IBM explicitly + elif $os400; then + JVM_VENDOR="IBM" + else + JVM_VENDOR="SUN" + fi + # echo "JVM vendor is $JVM_VENDOR" +} + +setupDebugOptions() { + if [ "x$JAVA_OPTS" = "x" ]; then + JAVA_OPTS="$DEFAULT_JAVA_OPTS" + fi + export JAVA_OPTS + + if [ "x$EXTRA_JAVA_OPTS" != "x" ]; then + JAVA_OPTS="$JAVA_OPTS $EXTRA_JAVA_OPTS" + fi + + # Set Debug options if enabled + if [ "x$KARAF_DEBUG" != "x" ]; then + # Use the defaults if JAVA_DEBUG_OPTS was not set + if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then + JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS" + fi + + JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS" + warn "Enabling Java debug options: $JAVA_DEBUG_OPTS" + fi +} + +setupDefaults() { + DEFAULT_JAVA_OPTS="-Xms$JAVA_MIN_MEM -Xmx$JAVA_MAX_MEM " + + #Set the JVM_VENDOR specific JVM flags + if [ "$JVM_VENDOR" = "SUN" ]; then + # + # Check some easily accessible MIN/MAX params for JVM mem usage + # + if [ "x$JAVA_PERM_MEM" != "x" ]; then + DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:PermSize=$JAVA_PERM_MEM" + fi + if [ "x$JAVA_MAX_PERM_MEM" != "x" ]; then + DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:MaxPermSize=$JAVA_MAX_PERM_MEM" + fi + # SONATYPE: removed -Dcom.sun.management.jmxremote + DEFAULT_JAVA_OPTS="-server $DEFAULT_JAVA_OPTS" + elif [ "$JVM_VENDOR" = "IBM" ]; then + if $os400; then + DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS" + elif $aix; then + DEFAULT_JAVA_OPTS="-Xverify:none -Xdump:heap -Xlp $DEFAULT_JAVA_OPTS" + else + DEFAULT_JAVA_OPTS="-Xverify:none $DEFAULT_JAVA_OPTS" + fi + fi + + # Add the jars in the lib dir + for file in "$KARAF_HOME"/lib/*.jar + do + if [ -z "$CLASSPATH" ]; then + CLASSPATH="$file" + else + CLASSPATH="$CLASSPATH:$file" + fi + done + + DEFAULT_JAVA_DEBUG_PORT="5005" + if [ "x$JAVA_DEBUG_PORT" = "x" ]; then + JAVA_DEBUG_PORT="$DEFAULT_JAVA_DEBUG_PORT" + fi + DEFAULT_JAVA_DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$JAVA_DEBUG_PORT" + + ## + ## TODO: Move to conf/profiler/yourkit.{sh|cmd} + ## + # Uncomment to enable YourKit profiling + #DEFAULT_JAVA_DEBUG_OPTS="-Xrunyjpagent" +} + +init() { + # Determine if there is special OS handling we must perform + detectOS + + # Unlimit the number of file descriptors if possible + unlimitFD + + # Locate the Karaf home directory + locateHome + + # Locate the Karaf base directory + locateBase + + # Locate the Karaf data directory + locateData + + # Locate the Karaf etc directory + locateEtc + + # Setup the native library path + setupNativePath + + # Locate the Java VM to execute + locateJava + + # Determine the JVM vendor + detectJVM + + # Setup default options + setupDefaults + + # Install debug options + setupDebugOptions +} + +run() { + CLASSPATH="${KARAF_HOME}/system/org/apache/karaf/instance/org.apache.karaf.instance.command/3.0.4/org.apache.karaf.instance.command-3.0.4.jar:${KARAF_HOME}/system/org/apache/karaf/instance/org.apache.karaf.instance.core/3.0.4/org.apache.karaf.instance.core-3.0.4.jar:${KARAF_HOME}/system/org/apache/karaf/shell/org.apache.karaf.shell.console/3.0.4/org.apache.karaf.shell.console-3.0.4.jar:${KARAF_HOME}/system/org/apache/karaf/shell/org.apache.karaf.shell.table/3.0.4/org.apache.karaf.shell.table-3.0.4.jar:${KARAF_HOME}/system/org/apache/aries/blueprint/org.apache.aries.blueprint.api/1.0.1/org.apache.aries.blueprint.api-1.0.1.jar:${KARAF_HOME}/system/org/apache/aries/blueprint/org.apache.aries.blueprint.core/1.4.3/org.apache.aries.blueprint.core-1.4.3.jar:${KARAF_HOME}/system/org/apache/aries/blueprint/org.apache.aries.blueprint.cm/1.0.6/org.apache.aries.blueprint.cm-1.0.6.jar:${KARAF_HOME}/system/org/ops4j/pax/logging/pax-logging-api/1.8.3/pax-logging-api-1.8.3.jar:${KARAF_HOME}/system/org/apache/felix/org.apache.felix.framework/4.2.1/org.apache.felix.framework-4.2.1.jar:${KARAF_HOME}/system/jline/jline/2.12.1/jline-2.12.1.jar:$CLASSPATH" + + if $cygwin; then + KARAF_HOME=`cygpath --path --windows "$KARAF_HOME"` + KARAF_BASE=`cygpath --path --windows "$KARAF_BASE"` + KARAF_DATA=`cygpath --path --windows "$KARAF_DATA"` + KARAF_ETC=`cygpath --path --windows "$KARAF_ETC"` + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + fi + + exec "$JAVA" $JAVA_OPTS -Dkaraf.instances="${KARAF_HOME}/instances" -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Dkaraf.etc="$KARAF_ETC" -Djava.io.tmpdir="$KARAF_DATA/tmp" -Djava.util.logging.config.file="$KARAF_BASE/etc/java.util.logging.properties" $KARAF_OPTS $OPTS -classpath "$CLASSPATH" org.apache.karaf.instance.main.Execute "$@" +} + +main() { + init + run "$@" +} + +main "$@" + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/instance.bat b/assemblies/nexus-base-template/src/main/resources/overlay/bin/instance.bat new file mode 100644 index 0000000000000000000000000000000000000000..a44fd8d26aafc47bc5ae3bf234f3175303f10125 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/bin/instance.bat @@ -0,0 +1,138 @@ +@echo off +if not "%ECHO%" == "" echo %ECHO% + +setlocal +set DIRNAME=%~dp0% +set PROGNAME=%~nx0% +set ARGS=%* + +rem Sourcing environment settings for karaf similar to tomcats setenv +SET KARAF_SCRIPT="instance.bat" +if exist "%DIRNAME%setenv.bat" ( + call "%DIRNAME%setenv.bat" +) + +rem Check console window title. Set to Karaf by default +if not "%KARAF_TITLE%" == "" ( + title %KARAF_TITLE% +) else ( + title Karaf +) + +rem Check/Set up some easily accessible MIN/MAX params for JVM mem usage +if "%JAVA_MIN_MEM%" == "" ( + set JAVA_MIN_MEM=128M +) +if "%JAVA_MAX_MEM%" == "" ( + set JAVA_MAX_MEM=512M +) + +goto BEGIN + +:warn + echo %PROGNAME%: %* +goto :EOF + +:BEGIN + +rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +if not "%KARAF_HOME%" == "" ( + call :warn Ignoring predefined value for KARAF_HOME +) +set KARAF_HOME=%DIRNAME%.. +if not exist "%KARAF_HOME%" ( + call :warn KARAF_HOME is not valid: "%KARAF_HOME%" + goto END +) + +if not "%KARAF_BASE%" == "" ( + if not exist "%KARAF_BASE%" ( + call :warn KARAF_BASE is not valid: "%KARAF_BASE%" + goto END + ) +) +if "%KARAF_BASE%" == "" ( + set "KARAF_BASE=%KARAF_HOME%" +) + +if not "%KARAF_DATA%" == "" ( + if not exist "%KARAF_DATA%" ( + call :warn KARAF_DATA is not valid: "%KARAF_DATA%" + goto END + ) +) +if "%KARAF_DATA%" == "" ( + set "KARAF_DATA=%KARAF_BASE%\data" +) + +if not "%KARAF_ETC%" == "" ( + if not exist "%KARAF_ETC%" ( + call :warn KARAF_ETC is not valid: "%KARAF_ETC%" + goto END + ) +) +if "%KARAF_ETC%" == "" ( + set "KARAF_ETC=%KARAF_BASE%\etc" +) + +set DEFAULT_JAVA_OPTS= +set DEFAULT_JAVA_DEBUG_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + +rem Support for loading native libraries +set PATH=%PATH%;%KARAF_BASE%\lib;%KARAF_HOME%\lib + +rem Setup the Java Virtual Machine +if not "%JAVA%" == "" goto :Check_JAVA_END + set JAVA=java + if "%JAVA_HOME%" == "" call :warn JAVA_HOME not set; results may vary + if not "%JAVA_HOME%" == "" set JAVA=%JAVA_HOME%\bin\java + if not exist "%JAVA_HOME%" ( + call :warn JAVA_HOME is not valid: "%JAVA_HOME%" + goto END + ) +:Check_JAVA_END + +if "%JAVA_OPTS%" == "" set JAVA_OPTS=%DEFAULT_JAVA_OPTS% + +if "%EXTRA_JAVA_OPTS%" == "" goto :KARAF_EXTRA_JAVA_OPTS_END + set JAVA_OPTS="%JAVA_OPTS% %EXTRA_JAVA_OPTS%" +:KARAF_EXTRA_JAVA_OPTS_END + +if "%KARAF_DEBUG%" == "" goto :KARAF_DEBUG_END + rem Use the defaults if JAVA_DEBUG_OPTS was not set + if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS% + + set JAVA_OPTS="%JAVA_DEBUG_OPTS% %JAVA_OPTS%" + call :warn Enabling Java debug options: %JAVA_DEBUG_OPTS% +:KARAF_DEBUG_END + +rem Setup the classpath +pushd "%KARAF_HOME%\lib" +for %%G in (karaf*.jar) do call:APPEND_TO_CLASSPATH %%G +popd +goto CLASSPATH_END + +: APPEND_TO_CLASSPATH +set filename=%~1 +set suffix=%filename:~-4% +if %suffix% equ .jar set CLASSPATH=%CLASSPATH%;%KARAF_HOME%\lib\%filename% +goto :EOF + +:CLASSPATH_END + +set CLASSPATH=%KARAF_HOME%\system\org\apache\karaf\instance\org.apache.karaf.instance.command\3.0.4\org.apache.karaf.instance.command-3.0.4.jar;%KARAF_HOME%\system\org\apache\karaf\instance\org.apache.karaf.instance.core\3.0.4\org.apache.karaf.instance.core-3.0.4.jar;%KARAF_HOME%\system\org\apache\karaf\shell\org.apache.karaf.shell.console\3.0.4\org.apache.karaf.shell.console-3.0.4.jar;%KARAF_HOME%\system\org\apache\karaf\shell\org.apache.karaf.shell.table\3.0.4\org.apache.karaf.shell.table-3.0.4.jar;%KARAF_HOME%\system\org\apache\aries\blueprint\org.apache.aries.blueprint.api\1.0.1\org.apache.aries.blueprint.api-1.0.1.jar;%KARAF_HOME%\system\org\apache\aries\blueprint\org.apache.aries.blueprint.core\1.4.3\org.apache.aries.blueprint.core-1.4.3.jar;%KARAF_HOME%\system\org\apache\aries\blueprint\org.apache.aries.blueprint.cm\1.0.6\org.apache.aries.blueprint.cm-1.0.6.jar;%KARAF_HOME%\system\org\ops4j\pax\logging\pax-logging-api\1.8.3\pax-logging-api-1.8.3.jar;%KARAF_HOME%\system\org\apache\felix\org.apache.felix.framework\4.2.1\org.apache.felix.framework-4.2.1.jar;%KARAF_HOME%\system\jline\jline\2.12.1\jline-2.12.1.jar;%CLASSPATH% + +:EXECUTE + if "%SHIFT%" == "true" SET ARGS=%2 %3 %4 %5 %6 %7 %8 + if not "%SHIFT%" == "true" SET ARGS=%1 %2 %3 %4 %5 %6 %7 %8 + rem Execute the Java Virtual Machine + rem SONATYPE: removed -Djavax.management.builder.initial + "%JAVA%" %JAVA_OPTS% %OPTS% -classpath "%CLASSPATH%" -Dkaraf.instances="%KARAF_HOME%\instances" -Dkaraf.home="%KARAF_HOME%" -Dkaraf.base="%KARAF_BASE%" -Dkaraf.etc="%KARAF_ETC%" -Djava.io.tmpdir="%KARAF_DATA%\tmp" -Djava.util.logging.config.file="%KARAF_BASE%\etc\java.util.logging.properties" %KARAF_OPTS% org.apache.karaf.instance.main.Execute %ARGS% + +rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +:END + +endlocal + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/karaf b/assemblies/nexus-base-template/src/main/resources/overlay/bin/karaf new file mode 100644 index 0000000000000000000000000000000000000000..1f60685f6656d495619785b2124d53e82bc26055 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/bin/karaf @@ -0,0 +1,410 @@ +#!/bin/sh +DIRNAME=`dirname "$0"` +PROGNAME=`basename "$0"` + +# +# Sourcing environment settings for karaf similar to tomcats setenv +# +KARAF_SCRIPT="karaf" +export KARAF_SCRIPT +if [ -f "$DIRNAME/setenv" ]; then + . "$DIRNAME/setenv" +fi + +# +# Set up some easily accessible MIN/MAX params for JVM mem usage +# +if [ "x$JAVA_MIN_MEM" = "x" ]; then + JAVA_MIN_MEM=128M + export JAVA_MIN_MEM +fi +if [ "x$JAVA_MAX_MEM" = "x" ]; then + JAVA_MAX_MEM=512M + export JAVA_MAX_MEM +fi + +# +# Check the mode that initiated the script +# +if [ "x$1" != "x" ]; then + MODE=$1 +fi + +warn() { + echo "${PROGNAME}: $*" +} + +die() { + warn "$*" + exit 1 +} + +detectOS() { + # OS specific support (must be 'true' or 'false'). + cygwin=false; + darwin=false; + aix=false; + os400=false; + case "`uname`" in + CYGWIN*) + cygwin=true + ;; + Darwin*) + darwin=true + ;; + AIX*) + aix=true + ;; + OS400*) + os400=true + ;; + esac + # For AIX, set an environment variable + if $aix; then + export LDR_CNTRL=MAXDATA=0xB0000000@DSA + echo $LDR_CNTRL + fi +} + +unlimitFD() { + # Use the maximum available, or set MAX_FD != -1 to use that + if [ "x$MAX_FD" = "x" ]; then + MAX_FD="maximum" + fi + + # Increase the maximum file descriptors if we can + if [ "$os400" = "false" ] && [ "$cygwin" = "false" ]; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ "$MAX_FD_LIMIT" != 'unlimited' ]; then + if [ $? -eq 0 ]; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then + # use the system max + MAX_FD="$MAX_FD_LIMIT" + fi + + ulimit -n $MAX_FD > /dev/null + # echo "ulimit -n" `ulimit -n` + if [ $? -ne 0 ]; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query system maximum file descriptor limit: $MAX_FD_LIMIT" + fi + fi + fi +} + +locateHome() { + if [ "x$KARAF_HOME" = "x" ]; then + # In POSIX shells, CDPATH may cause cd to write to stdout + (unset CDPATH) >/dev/null 2>&1 && unset CDPATH + KARAF_HOME=`cd "$DIRNAME/.."; pwd` + fi + + if [ ! -d "$KARAF_HOME" ]; then + die "KARAF_HOME is not valid: $KARAF_HOME" + fi +} + +locateBase() { + if [ "x$KARAF_BASE" != "x" ]; then + if [ ! -d "$KARAF_BASE" ]; then + die "KARAF_BASE is not valid: $KARAF_BASE" + fi + else + KARAF_BASE=$KARAF_HOME + fi +} + +locateData() { + if [ "x$KARAF_DATA" != "x" ]; then + if [ ! -d "$KARAF_DATA" ]; then + die "KARAF_DATA is not valid: $KARAF_DATA" + fi + else + KARAF_DATA=$KARAF_BASE/data + fi +} + +locateEtc() { + if [ "x$KARAF_ETC" != "x" ]; then + if [ ! -d "$KARAF_ETC" ]; then + die "KARAF_ETC is not valid: $KARAF_ETC" + fi + else + KARAF_ETC=$KARAF_BASE/etc + fi +} + +setupNativePath() { + # Support for loading native libraries + LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:$KARAF_BASE/lib:$KARAF_HOME/lib" + + # For Cygwin, set PATH from LD_LIBRARY_PATH + if $cygwin; then + LD_LIBRARY_PATH=`cygpath --path --windows "$LD_LIBRARY_PATH"` + PATH="$PATH;$LD_LIBRARY_PATH" + export PATH + fi + export LD_LIBRARY_PATH +} + +pathCanonical() { + local dst="${1}" + while [ -h "${dst}" ] ; do + ls=`ls -ld "${dst}"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + dst="$link" + else + dst="`dirname "${dst}"`/$link" + fi + done + local bas=`basename "${dst}"` + local dir=`dirname "${dst}"` + if [ "$bas" != "$dir" ]; then + dst="`pathCanonical "$dir"`/$bas" + fi + echo "${dst}" | sed -e 's#//#/#g' -e 's#/./#/#g' -e 's#/[^/]*/../#/#g' +} + +locateJava() { + # Setup the Java Virtual Machine + if $cygwin ; then + [ -n "$JAVA" ] && JAVA=`cygpath --unix "$JAVA"` + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + fi + + if [ "x$JAVA_HOME" = "x" ] && [ "$darwin" = "true" ]; then + JAVA_HOME="$(/usr/libexec/java_home -v 1.7)" + fi + if [ "x$JAVA" = "x" ] && [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi + if [ "x$JAVA" = "x" ]; then + if [ "x$JAVA_HOME" != "x" ]; then + if [ ! -d "$JAVA_HOME" ]; then + die "JAVA_HOME is not valid: $JAVA_HOME" + fi + JAVA="$JAVA_HOME/bin/java" + else + warn "JAVA_HOME not set; results may vary" + JAVA=`type java` + JAVA=`expr "$JAVA" : '.* \(/.*\)$'` + if [ "x$JAVA" = "x" ]; then + die "java command not found" + fi + fi + fi + if [ "x$JAVA_HOME" = "x" ]; then + JAVA_HOME="$(dirname $(dirname $(pathCanonical "$JAVA")))" + fi +} + +detectJVM() { + #echo "`$JAVA -version`" + # This service should call `java -version`, + # read stdout, and look for hints + if $JAVA -version 2>&1 | grep "^IBM" ; then + JVM_VENDOR="IBM" + # on OS/400, java -version does not contain IBM explicitly + elif $os400; then + JVM_VENDOR="IBM" + else + JVM_VENDOR="SUN" + fi + # echo "JVM vendor is $JVM_VENDOR" +} + +checkJvmVersion() { + # echo "`$JAVA -version`" + VERSION=`$JAVA -version 2>&1 | egrep '"([0-9].[0-9]\..*[0-9]).*"' | awk '{print substr($3,2,length($3)-2)}' | awk '{print substr($1, 3, 3)}' | sed -e 's;\.;;g'` + # echo $VERSION + if [ "$VERSION" -lt "60" ]; then + echo "JVM must be greater than 1.6" + exit 1; + fi +} + +setupDebugOptions() { + if [ "x$JAVA_OPTS" = "x" ]; then + JAVA_OPTS="$DEFAULT_JAVA_OPTS" + fi + export JAVA_OPTS + + if [ "x$EXTRA_JAVA_OPTS" != "x" ]; then + JAVA_OPTS="$JAVA_OPTS $EXTRA_JAVA_OPTS" + fi + + # Set Debug options if enabled + if [ "x$KARAF_DEBUG" != "x" ]; then + # Ignore DEBUG in case of stop or client mode + if [ "x$MODE" = "xstop" ]; then + return + fi + if [ "x$MODE" = "xclient" ]; then + return + fi + # Use the defaults if JAVA_DEBUG_OPTS was not set + if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then + JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS" + fi + + JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS" + warn "Enabling Java debug options: $JAVA_DEBUG_OPTS" + fi +} + +setupDefaults() { + DEFAULT_JAVA_OPTS="-Xms$JAVA_MIN_MEM -Xmx$JAVA_MAX_MEM -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass " + + #Set the JVM_VENDOR specific JVM flags + if [ "$JVM_VENDOR" = "SUN" ]; then + # + # Check some easily accessible MIN/MAX params for JVM mem usage + # + if [ "x$JAVA_PERM_MEM" != "x" ]; then + DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:PermSize=$JAVA_PERM_MEM" + fi + if [ "x$JAVA_MAX_PERM_MEM" != "x" ]; then + DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS -XX:MaxPermSize=$JAVA_MAX_PERM_MEM" + fi + # SONATYPE: removed -Dcom.sun.management.jmxremote + DEFAULT_JAVA_OPTS="-server $DEFAULT_JAVA_OPTS" + elif [ "$JVM_VENDOR" = "IBM" ]; then + if $os400; then + DEFAULT_JAVA_OPTS="$DEFAULT_JAVA_OPTS" + elif $aix; then + DEFAULT_JAVA_OPTS="-Xverify:none -Xdump:heap -Xlp $DEFAULT_JAVA_OPTS" + else + DEFAULT_JAVA_OPTS="-Xverify:none $DEFAULT_JAVA_OPTS" + fi + fi + + # Add the jars in the lib dir + for file in "$KARAF_HOME"/lib/karaf*.jar + do + if [ -z "$CLASSPATH" ]; then + CLASSPATH="$file" + else + CLASSPATH="$CLASSPATH:$file" + fi + done + + DEFAULT_JAVA_DEBUG_PORT="5005" + if [ "x$JAVA_DEBUG_PORT" = "x" ]; then + JAVA_DEBUG_PORT="$DEFAULT_JAVA_DEBUG_PORT" + fi + DEFAULT_JAVA_DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$JAVA_DEBUG_PORT" + + ## + ## TODO: Move to conf/profiler/yourkit.{sh|cmd} + ## + # Uncomment to enable YourKit profiling + #DEFAULT_JAVA_DEBUG_OPTS="-Xrunyjpagent" +} + +init() { + # Determine if there is special OS handling we must perform + detectOS + + # Unlimit the number of file descriptors if possible + unlimitFD + + # Locate the Karaf home directory + locateHome + + # Locate the Karaf base directory + locateBase + + # Locate the Karaf data directory + locateData + + # Locate the Karaf etc directory + locateEtc + + # Setup the native library path + setupNativePath + + # Locate the Java VM to execute + locateJava + + # Determine the JVM vendor + detectJVM + + # Determine the JVM version >= 1.6 + checkJvmVersion + + # Setup default options + setupDefaults + + # Install debug options + setupDebugOptions + +} + +run() { + OPTS="-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=true" + MAIN=org.apache.karaf.main.Main + while [ "$1" != "" ]; do + case $1 in + 'clean') + rm -Rf "$KARAF_DATA" + shift + ;; + 'debug') + if [ "x$JAVA_DEBUG_OPTS" = "x" ]; then + JAVA_DEBUG_OPTS="$DEFAULT_JAVA_DEBUG_OPTS" + fi + JAVA_OPTS="$JAVA_DEBUG_OPTS $JAVA_OPTS" + shift + ;; + 'status') + MAIN=org.apache.karaf.main.Status + shift + ;; + 'stop') + MAIN=org.apache.karaf.main.Stop + shift + ;; + 'console') + shift + ;; + 'server') + OPTS="-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true" + shift + ;; + 'client') + OPTS="-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=false" + shift + ;; + *) + break + ;; + esac + done + + JAVA_ENDORSED_DIRS="${JAVA_HOME}/jre/lib/endorsed:${JAVA_HOME}/lib/endorsed:${KARAF_HOME}/lib/endorsed" + JAVA_EXT_DIRS="${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext:${KARAF_HOME}/lib/ext" + if $cygwin; then + KARAF_HOME=`cygpath --path --windows "$KARAF_HOME"` + KARAF_BASE=`cygpath --path --windows "$KARAF_BASE"` + KARAF_DATA=`cygpath --path --windows "$KARAF_DATA"` + KARAF_ETC=`cygpath --path --windows "$KARAF_ETC"` + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"` + JAVA_EXT_DIRS=`cygpath --path --windows "$JAVA_EXT_DIRS"` + fi + cd "$KARAF_BASE" + + # SONATYPE: removed -Djavax.management.builder.initial + exec "$JAVA" $JAVA_OPTS -Djava.endorsed.dirs="${JAVA_ENDORSED_DIRS}" -Djava.ext.dirs="${JAVA_EXT_DIRS}" -Dkaraf.instances="${KARAF_HOME}/instances" -Dkaraf.home="$KARAF_HOME" -Dkaraf.base="$KARAF_BASE" -Dkaraf.data="$KARAF_DATA" -Dkaraf.etc="$KARAF_ETC" -Djava.io.tmpdir="$KARAF_DATA/tmp" -Djava.util.logging.config.file="$KARAF_BASE/etc/java.util.logging.properties" $KARAF_OPTS $OPTS -classpath "$CLASSPATH" $MAIN "$@" +} + +main() { + init + run "$@" +} + +main "$@" diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/karaf.bat b/assemblies/nexus-base-template/src/main/resources/overlay/bin/karaf.bat new file mode 100644 index 0000000000000000000000000000000000000000..e4e8daeb263b5cc14fd54278ba1777f646b162ca --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/bin/karaf.bat @@ -0,0 +1,325 @@ +@echo off +if not "%ECHO%" == "" echo %ECHO% + +setlocal +set DIRNAME=%~dp0% +set PROGNAME=%~nx0% +set ARGS=%* + +rem Sourcing environment settings for karaf similar to tomcats setenv +SET KARAF_SCRIPT="karaf.bat" +if exist "%DIRNAME%setenv.bat" ( + call "%DIRNAME%setenv.bat" +) + +rem Check console window title. Set to Karaf by default +if not "%KARAF_TITLE%" == "" ( + title %KARAF_TITLE% +) else ( + title Karaf +) + +rem Check/Set up some easily accessible MIN/MAX params for JVM mem usage +if "%JAVA_MIN_MEM%" == "" ( + set JAVA_MIN_MEM=128M +) +if "%JAVA_MAX_MEM%" == "" ( + set JAVA_MAX_MEM=512M +) + +goto BEGIN + +:warn + echo %PROGNAME%: %* +goto :EOF + +:BEGIN + +rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +if not "%KARAF_HOME%" == "" ( + call :warn Ignoring predefined value for KARAF_HOME +) +set KARAF_HOME=%DIRNAME%.. +if not exist "%KARAF_HOME%" ( + call :warn KARAF_HOME is not valid: "%KARAF_HOME%" + goto END +) + +if not "%KARAF_BASE%" == "" ( + if not exist "%KARAF_BASE%" ( + call :warn KARAF_BASE is not valid: "%KARAF_BASE%" + goto END + ) +) +if "%KARAF_BASE%" == "" ( + set "KARAF_BASE=%KARAF_HOME%" +) + +if not "%KARAF_DATA%" == "" ( + if not exist "%KARAF_DATA%" ( + call :warn KARAF_DATA is not valid: "%KARAF_DATA%" + goto END + ) +) +if "%KARAF_DATA%" == "" ( + set "KARAF_DATA=%KARAF_BASE%\data" +) + +if not "%KARAF_ETC%" == "" ( + if not exist "%KARAF_ETC%" ( + call :warn KARAF_ETC is not valid: "%KARAF_ETC%" + goto END + ) +) +if "%KARAF_ETC%" == "" ( + set "KARAF_ETC=%KARAF_BASE%\etc" +) + +set LOCAL_CLASSPATH=%CLASSPATH% +set JAVA_MODE=-server + +set CLASSPATH=%LOCAL_CLASSPATH%;%KARAF_BASE%\conf +set DEFAULT_JAVA_DEBUG_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 + +if "%LOCAL_CLASSPATH%" == "" goto :KARAF_CLASSPATH_EMPTY + set CLASSPATH=%LOCAL_CLASSPATH%;%KARAF_BASE%\conf + goto :KARAF_CLASSPATH_END +:KARAF_CLASSPATH_EMPTY + set CLASSPATH=%KARAF_BASE%\conf +:KARAF_CLASSPATH_END + +rem Setup Karaf Home +if exist "%KARAF_HOME%\conf\karaf-rc.cmd" call %KARAF_HOME%\conf\karaf-rc.cmd +if exist "%HOME%\karaf-rc.cmd" call %HOME%\karaf-rc.cmd + +rem Support for loading native libraries +set PATH=%PATH%;%KARAF_BASE%\lib;%KARAF_HOME%\lib + +rem Setup the Java Virtual Machine +if not "%JAVA%" == "" goto :Check_JAVA_END + if not "%JAVA_HOME%" == "" goto :TryJDKEnd + call :warn JAVA_HOME not set; results may vary +:TryJRE + start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment" + if not exist __reg1.txt goto :TryJDK + type __reg1.txt | find "CurrentVersion" > __reg2.txt + if errorlevel 1 goto :TryJDK + for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JavaTemp=%%~x + if errorlevel 1 goto :TryJDK + set JavaTemp=%JavaTemp%## + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp:##=% + del __reg1.txt + del __reg2.txt + start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Runtime Environment\%JavaTemp%" + if not exist __reg1.txt goto :TryJDK + type __reg1.txt | find "JavaHome" > __reg2.txt + if errorlevel 1 goto :TryJDK + for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JAVA_HOME=%%~x + if errorlevel 1 goto :TryJDK + del __reg1.txt + del __reg2.txt + goto TryJDKEnd +:TryJDK + start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit" + if not exist __reg1.txt ( + goto TryRegJRE + ) + type __reg1.txt | find "CurrentVersion" > __reg2.txt + if errorlevel 1 ( + goto TryRegJRE + ) + for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JavaTemp=%%~x + if errorlevel 1 ( + goto TryRegJRE + ) + set JavaTemp=%JavaTemp%## + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp: ##=##% + set JavaTemp=%JavaTemp:##=% + del __reg1.txt + del __reg2.txt + start /w regedit /e __reg1.txt "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\%JavaTemp%" + if not exist __reg1.txt ( + goto TryRegJRE + ) + type __reg1.txt | find "JavaHome" > __reg2.txt + if errorlevel 1 ( + goto TryRegJRE + ) + for /f "tokens=2 delims==" %%x in (__reg2.txt) do set JAVA_HOME=%%~x + if errorlevel 1 ( + goto TryRegJRE + ) + del __reg1.txt + del __reg2.txt +:TryRegJRE + rem try getting the JAVA_HOME from registry + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment" /v CurrentVersion`) DO ( + set JAVA_VERSION=%%A + ) + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Runtime Environment\%JAVA_VERSION%" /v JavaHome`) DO ( + set JAVA_HOME=%%A %%B + ) + if not exist "%JAVA_HOME%" ( + goto TryRegJDK + ) + goto TryJDKEnd +:TryRegJDK + rem try getting the JAVA_HOME from registry + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit" /v CurrentVersion`) DO ( + set JAVA_VERSION=%%A + ) + FOR /F "usebackq tokens=3*" %%A IN (`REG QUERY "HKLM\Software\JavaSoft\Java Development Kit\%JAVA_VERSION%" /v JavaHome`) DO ( + set JAVA_HOME=%%A %%B + ) + if not exist "%JAVA_HOME%" ( + call :warn Unable to retrieve JAVA_HOME from Registry + ) + goto TryJDKEnd +:TryJDKEnd + if not exist "%JAVA_HOME%" ( + call :warn JAVA_HOME is not valid: "%JAVA_HOME%" + goto END + ) + set JAVA=%JAVA_HOME%\bin\java +:Check_JAVA_END + +if not exist "%JAVA_HOME%\bin\server\jvm.dll" ( + if not exist "%JAVA_HOME%\jre\bin\server\jvm.dll" ( + echo WARNING: Running Karaf on a Java HotSpot Client VM because server-mode is not available. + echo Install Java Developer Kit to fix this. + echo For more details see http://java.sun.com/products/hotspot/whitepaper.html#client + set JAVA_MODE=-client + ) +) +rem SONATYPE: removed -Dcom.sun.management.jmxremote +set DEFAULT_JAVA_OPTS=%JAVA_MODE% -Xms%JAVA_MIN_MEM% -Xmx%JAVA_MAX_MEM% -Dderby.system.home="%KARAF_DATA%\derby" -Dderby.storage.fileSyncTransactionLog=true -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass + +rem Check some easily accessible MIN/MAX params for JVM mem usage +if not "%JAVA_PERM_MEM%" == "" ( + set DEFAULT_JAVA_OPTS=%DEFAULT_JAVA_OPTS% -XX:PermSize=%JAVA_PERM_MEM% +) +if not "%JAVA_MAX_PERM_MEM%" == "" ( + set DEFAULT_JAVA_OPTS=%DEFAULT_JAVA_OPTS% -XX:MaxPermSize=%JAVA_MAX_PERM_MEM% +) + +if "%JAVA_OPTS%" == "" set JAVA_OPTS=%DEFAULT_JAVA_OPTS% + +if "%EXTRA_JAVA_OPTS%" == "" goto :KARAF_EXTRA_JAVA_OPTS_END + set JAVA_OPTS=%JAVA_OPTS% %EXTRA_JAVA_OPTS% +:KARAF_EXTRA_JAVA_OPTS_END + +if "%KARAF_DEBUG%" == "" goto :KARAF_DEBUG_END + if "%1" == "stop" goto :KARAF_DEBUG_END + if "%1" == "client" goto :KARAF_DEBUG_END + rem Use the defaults if JAVA_DEBUG_OPTS was not set + if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS% + + set JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS% + call :warn Enabling Java debug options: %JAVA_DEBUG_OPTS% +:KARAF_DEBUG_END + +if "%KARAF_PROFILER%" == "" goto :KARAF_PROFILER_END + set KARAF_PROFILER_SCRIPT=%KARAF_HOME%\conf\profiler\%KARAF_PROFILER%.cmd + + if exist "%KARAF_PROFILER_SCRIPT%" goto :KARAF_PROFILER_END + call :warn Missing configuration for profiler '%KARAF_PROFILER%': %KARAF_PROFILER_SCRIPT% + goto END +:KARAF_PROFILER_END + +rem Setup the classpath +pushd "%KARAF_HOME%\lib" +for %%G in (karaf*.jar) do call:APPEND_TO_CLASSPATH %%G +popd +goto CLASSPATH_END + +: APPEND_TO_CLASSPATH +set filename=%~1 +set suffix=%filename:~-4% +if %suffix% equ .jar set CLASSPATH=%CLASSPATH%;%KARAF_HOME%\lib\%filename% +goto :EOF + +:CLASSPATH_END + +rem Execute the JVM or the load the profiler +if "%KARAF_PROFILER%" == "" goto :RUN + rem Execute the profiler if it has been configured + call :warn Loading profiler script: %KARAF_PROFILER_SCRIPT% + call %KARAF_PROFILER_SCRIPT% + +:RUN + SET OPTS=-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=true + SET MAIN=org.apache.karaf.main.Main + SET SHIFT=false + +:RUN_LOOP + if "%1" == "stop" goto :EXECUTE_STOP + if "%1" == "status" goto :EXECUTE_STATUS + if "%1" == "console" goto :EXECUTE_CONSOLE + if "%1" == "server" goto :EXECUTE_SERVER + if "%1" == "client" goto :EXECUTE_CLIENT + if "%1" == "clean" goto :EXECUTE_CLEAN + if "%1" == "debug" goto :EXECUTE_DEBUG + goto :EXECUTE + +:EXECUTE_STOP + SET MAIN=org.apache.karaf.main.Stop + shift + goto :RUN_LOOP + +:EXECUTE_STATUS + SET MAIN=org.apache.karaf.main.Status + shift + goto :RUN_LOOP + +:EXECUTE_CONSOLE + shift + goto :RUN_LOOP + +:EXECUTE_SERVER + SET OPTS=-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true + shift + goto :RUN_LOOP + +:EXECUTE_CLIENT + SET OPTS=-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=false + shift + goto :RUN_LOOP + +:EXECUTE_CLEAN + rmdir /S /Q "%KARAF_DATA%" + shift + goto :RUN_LOOP + +:EXECUTE_DEBUG + if "%JAVA_DEBUG_OPTS%" == "" set JAVA_DEBUG_OPTS=%DEFAULT_JAVA_DEBUG_OPTS% + set JAVA_OPTS=%JAVA_DEBUG_OPTS% %JAVA_OPTS% + shift + goto :RUN_LOOP + +:EXECUTE + SET ARGS=%1 %2 %3 %4 %5 %6 %7 %8 + rem Execute the Java Virtual Machine + cd "%KARAF_BASE%" + rem SONATYPE: removed -Djavax.management.builder.initial + "%JAVA%" %JAVA_OPTS% %OPTS% -classpath "%CLASSPATH%" -Djava.endorsed.dirs="%JAVA_HOME%\jre\lib\endorsed;%JAVA_HOME%\lib\endorsed;%KARAF_HOME%\lib\endorsed" -Djava.ext.dirs="%JAVA_HOME%\jre\lib\ext;%JAVA_HOME%\lib\ext;%KARAF_HOME%\lib\ext" -Dkaraf.instances="%KARAF_HOME%\instances" -Dkaraf.home="%KARAF_HOME%" -Dkaraf.base="%KARAF_BASE%" -Dkaraf.etc="%KARAF_ETC%" -Djava.io.tmpdir="%KARAF_DATA%\tmp" -Dkaraf.data="%KARAF_DATA%" -Djava.util.logging.config.file="%KARAF_BASE%\etc\java.util.logging.properties" %KARAF_OPTS% %MAIN% %ARGS% + +rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +:END + +endlocal + +if not "%PAUSE%" == "" pause + +:END_NO_PAUSE + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus b/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus new file mode 100644 index 0000000000000000000000000000000000000000..4c790072c4f0dfe3c95067e7a6b1206664ade16d --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus @@ -0,0 +1,43 @@ +#!/bin/sh +DIRNAME=`dirname "$0"` +PROGNAME=`basename "$0"` + +usage() { + echo "${PROGNAME} { console | start | stop | restart | status }" + exit 1 +} + +if [ "x$1" = "x" ]; then + usage +fi + +run() { + case "$1" in + 'console') + shift + exec "${DIRNAME}/karaf" "$@" + ;; + 'start') + shift + exec "${DIRNAME}/start" "$@" + ;; + 'stop') + shift + exec "${DIRNAME}/stop" "$@" + ;; + 'restart') + shift + "${DIRNAME}/stop" 2>/dev/null + exec "${DIRNAME}/start" "$@" + ;; + 'status') + shift + exec "${DIRNAME}/status" "$@" + ;; + *) + usage + ;; + esac +} + +run "$@" diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus.bat b/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus.bat new file mode 100644 index 0000000000000000000000000000000000000000..f631c455a360f6f92640c76da67dffc7951068c0 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/bin/nexus.bat @@ -0,0 +1,64 @@ +@echo off +if not "%ECHO%" == "" echo %ECHO% + +setlocal +set DIRNAME=%~dp0% +set PROGNAME=%~nx0% + +SET KARAF_TITLE=Sonatype Nexus + +goto BEGIN + +:USAGE + echo "%PROGNAME% { console | start | stop | restart | status }" +goto :EOF + +:BEGIN + +rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +:RUN + SET SHIFT=false + if "%1" == "console" goto :EXECUTE_CONSOLE + if "%1" == "start" goto :EXECUTE_START + if "%1" == "stop" goto :EXECUTE_STOP + if "%1" == "restart" goto :EXECUTE_RESTART + if "%1" == "status" goto :EXECUTE_STATUS + goto :USAGE + +:EXECUTE_CONSOLE + shift + "%DIRNAME%karaf.bat" %1 %2 %3 %4 %5 %6 %7 %8 + goto :EOF + +:EXECUTE_START + shift + "%DIRNAME%start.bat" %1 %2 %3 %4 %5 %6 %7 %8 + goto :EOF + +:EXECUTE_STOP + shift + "%DIRNAME%stop.bat" %1 %2 %3 %4 %5 %6 %7 %8 + goto :EOF + +:EXECUTE_RESTART + shift + call "%DIRNAME%stop.bat" 2>NUL + "%DIRNAME%start.bat" %1 %2 %3 %4 %5 %6 %7 %8 + goto :EOF + +:EXECUTE_STATUS + shift + "%DIRNAME%status.bat" %1 %2 %3 %4 %5 %6 %7 %8 + goto :EOF + +rem # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +:END + +endlocal + +if not "%PAUSE%" == "" pause + +:END_NO_PAUSE + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv b/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv new file mode 100644 index 0000000000000000000000000000000000000000..0a5cf8efc6c21c73e7425762bea6fd7c3073f40a --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv @@ -0,0 +1,44 @@ +#!/bin/sh +# handle specific scripts; the SCRIPT_NAME is exactly the name of the Karaf +# script; for example karaf, start, stop, admin, client, ... +# +# if [ "$KARAF_SCRIPT" = "SCRIPT_NAME" ]; then +# Actions go here... +# fi + +# +# general settings which should be applied for all scripts go here; please keep +# in mind that it is possible that scripts might be executed more than once, e.g. +# in example of the start script where the start script is executed first and the +# karaf script afterwards. +# + +# +# The following section shows the possible configuration options for the default +# karaf scripts +# +# JAVA_HOME # Location of Java installation +# JAVA_MIN_MEM # Minimum memory for the JVM +# JAVA_MAX_MEM # Maximum memory for the JVM +# JAVA_PERM_MEM # Minimum perm memory for the JVM +# JAVA_MAX_PERM_MEM # Maximum perm memory for the JVM +# EXTRA_JAVA_OPTS # Additional JVM options +# KARAF_HOME # Karaf home folder +# KARAF_DATA # Karaf data folder +# KARAF_BASE # Karaf base folder +# KARAF_ETC # Karaf etc folder +# KARAF_OPTS # Additional available Karaf options +# KARAF_DEBUG # Enable debug mode +# KARAF_REDIRECT # Enable/set the std/err redirection when using bin/start + +JAVA_MIN_MEM="256M" +export JAVA_MIN_MEM + +JAVA_MAX_MEM="768M" +export JAVA_MAX_MEM + +JAVA_MAX_PERM_MEM="256M" +export JAVA_MAX_PERM_MEM + +KARAF_OPTS="-Djava.net.preferIPv4Stack=true" +export KARAF_OPTS diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv.bat b/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv.bat new file mode 100644 index 0000000000000000000000000000000000000000..741ac8ef33b0d65a3536923c466f027662278dd4 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/bin/setenv.bat @@ -0,0 +1,52 @@ +@echo off +rem +rem handle specific scripts; the SCRIPT_NAME is exactly the name of the Karaf +rem script; for example karaf.bat, start.bat, stop.bat, admin.bat, client.bat, ... +rem +rem if "%KARAF_SCRIPT%" == "SCRIPT_NAME" ( +rem Actions go here... +rem ) + +rem +rem general settings which should be applied for all scripts go here; please keep +rem in mind that it is possible that scripts might be executed more than once, e.g. +rem in example of the start script where the start script is executed first and the +rem karaf script afterwards. +rem + +rem +rem The following section shows the possible configuration options for the default +rem karaf scripts +rem +rem Window name of the windows console +rem SET KARAF_TITLE +rem Location of Java installation +rem SET JAVA_HOME +rem Minimum memory for the JVM +rem SET JAVA_MIN_MEM +rem Maximum memory for the JVM +rem SET JAVA_MAX_MEM +rem Minimum perm memory for the JVM +rem SET JAVA_PERM_MEM +rem Maximum perm memory for the JVM +rem SET JAVA_MAX_PERM_MEM +rem Additional JVM options +rem SET EXTRA_JAVA_OPTS +rem Karaf home folder +rem SET KARAF_HOME +rem Karaf data folder +rem SET KARAF_DATA +rem Karaf base folder +rem SET KARAF_BASE +rem Karaf etc folder +rem SET KARAF_ETC +rem Additional available Karaf options +rem SET KARAF_OPTS +rem Enable debug mode +rem SET KARAF_DEBUG + +SET JAVA_MIN_MEM=256M +SET JAVA_MAX_MEM=768M +SET JAVA_MAX_PERM_MEM=256M + +SET KARAF_OPTS=-Djava.net.preferIPv4Stack=true diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/data/tmp/.placeholder b/assemblies/nexus-base-template/src/main/resources/overlay/data/tmp/.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/deploy/.placeholder b/assemblies/nexus-base-template/src/main/resources/overlay/deploy/.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/config.properties b/assemblies/nexus-base-template/src/main/resources/overlay/etc/config.properties new file mode 100644 index 0000000000000000000000000000000000000000..eef676af39b339c6b0c103c05f8c4fed7df389b5 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/config.properties @@ -0,0 +1,172 @@ +# +# This file lists Karaf default settings for this particular version of Karaf. +# For easier maintenance when upgrading Karaf and to better document which +# default values have changed, it is recommended to place any changes to +# these values in a custom.properties file in the same folder as this file. +# Each value specified in custom.properties will override the default value +# here. +# + +# +# Properties file inclusions (as a space separated list of relative paths) +# Included files will override the values specified in this file +# NB: ${includes} properties files are mandatory, it means that Karaf will not start +# if the include file is not found +# +${includes} = jre.properties custom.properties + +# +# Properties file inclusions (as a space separated list of relative paths) +# Included files will override the values specified in this file +# NB: ${optionals} properties files are optionals, it means that Karaf will just +# display a warning message but the bootstrap will be performed +# +# ${optionals} = my.properties + +# +# Framework selection properties +# +karaf.framework=felix + +# +# Location of the OSGi frameworks +# +karaf.framework.equinox=mvn\:org.eclipse/org.eclipse.osgi/3.8.2.v20130124-134944 +karaf.framework.felix=mvn\:org.apache.felix/org.apache.felix.framework/4.2.1 + +# +# Framework config properties. +# +org.osgi.framework.system.packages= \ + org.osgi.framework.startlevel;uses:="org.osgi.framework";version="1.0", \ + org.osgi.framework.wiring;uses:="org.osgi.framework";version="1.1", \ + org.osgi.framework.hooks.bundle;uses:="org.osgi.framework";version="1.1", \ + org.osgi.framework.hooks.service;uses:="org.osgi.framework";version="1.1", \ + org.osgi.framework.hooks.resolver;uses:="org.osgi.framework.wiring";version="1.0", \ + org.osgi.framework.launch;uses:="org.osgi.framework";version="1.1", \ + org.osgi.framework.namespace;uses:="org.osgi.resource";version="1.0", \ + org.osgi.framework;version="1.7", \ + org.osgi.framework.hooks.weaving;uses:="org.osgi.framework.wiring";version="1.0",\ + org.osgi.resource;version="1.0",org.osgi.service.url;version="1.0",\ + org.osgi.service.startlevel;uses:="org.osgi.framework";version="1.1",\ + org.osgi.service.packageadmin;uses:="org.osgi.framework";version="1.2",\ + org.osgi.service.url;version="1.0", \ + org.osgi.util.tracker;uses:="org.osgi.framework";version="1.5.1", \ + org.apache.karaf.jaas.boot;version="3.0.4", \ + org.apache.karaf.jaas.boot.principal;version="3.0.4", \ + org.apache.karaf.version;version="3.0.4", \ + ${jre-${java.specification.version}} + +# +# Extra packages appended after standard packages +# +org.osgi.framework.system.packages.extra= \ + sun.security.ssl, \ + com.sun.net.httpserver, \ + javax.annotation;version=1.2, \ + javax.annotation.security;version=1.2, \ + org.xerial.snappy, \ + org.apache.karaf.branding, \ + sun.misc + +org.osgi.framework.system.capabilities= \ + ${eecap-${java.specification.version}}, \ + service-reference;effective:=active;objectClass=org.osgi.service.packageadmin.PackageAdmin, \ + service-reference;effective:=active;objectClass=org.osgi.service.startlevel.StartLevel, \ + service-reference;effective:=active;objectClass=org.osgi.service.url.URLHandlers + +eecap-1.8= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8" +eecap-1.7= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7" +eecap-1.6= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6" +eecap-1.5= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5" +eecap-1.4= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4" +eecap-1.3= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3" +eecap-1.2= osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2" + +# +# javax.transaction is needed to avoid class loader constraint violation when using javax.sql +# +org.osgi.framework.bootdelegation=org.apache.karaf.jaas.boot,org.apache.karaf.jaas.boot.principal,sun.*,com.sun.*,javax.transaction,javax.transaction.*,javax.xml.crypto,javax.xml.crypto.* + +# jVisualVM support +# in order to use Karaf with jvisualvm, the org.osgi.framework.bootdelegation property has to contain the org.netbeans.lib.profiler.server package +# and, so, it should look like: +# +# org.osgi.framework.bootdelegation=org.apache.karaf.jaas.boot,org.apache.karaf.jaas.boot.principal,sun.*,com.sun.*,javax.transaction,javax.transaction.*,javax.xml.crypto,javax.xml.crypto.*,org.netbeans.lib.profiler.server +# +# YourKit support +# in order to use Karaf with YourKit, the org.osgi.framework.bootdelegation property has to contain the com.yourkit.* packages +# and, so, it should look like: +# +# org.osgi.framework.bootdelegation=org.apache.karaf.jaas.boot,org.apache.karaf.jaas.boot.principal,sun.*,com.sun.*,javax.transaction,javax.transaction.*,javax.xml.crypto,javax.xml.crypto.*,com.yourkit.* +# + +# +# OSGi Execution Environment +# +org.osgi.framework.executionenvironment=J2SE-1.8,JavaSE-1.8,J2SE-1.7,JavaSE-1.7,J2SE-1.6,JavaSE-1.6,J2SE-1.5,JavaSE-1.5,J2SE-1.4,JavaSE-1.4,J2SE-1.3,JavaSE-1.3,J2SE-1.2,,JavaSE-1.2,CDC-1.1/Foundation-1.1,CDC-1.0/Foundation-1.0,J2ME,OSGi/Minimum-1.1,OSGi/Minimum-1.0 + +# +# Set the parent classloader for the bundle to the classloader that loads the Framework (i.e. everything in lib/*.jar) +# +org.osgi.framework.bundle.parent=framework + +# +# Definition of the default bundle start level +# +org.osgi.framework.startlevel.beginning=100 +karaf.startlevel.bundle=80 + +# +# The location of the Karaf shutdown port file +# +karaf.shutdown.port.file=${karaf.data}/port + +# +# Configuration FileMonitor properties +# +felix.fileinstall.enableConfigSave = true +felix.fileinstall.dir = ${karaf.etc} +felix.fileinstall.filter = .*\\.cfg +felix.fileinstall.poll = 1000 +felix.fileinstall.noInitialDelay = true +felix.fileinstall.log.level = 3 + +# +# Delay for writing the framework state to disk in equinox +# must be >= 1000 and <= 1800000 +# +eclipse.stateSaveDelayInterval = 1000 + +# +# OBR Repository list +# This property will be modified by the obr:addUrl and obr:removeUrl commands. +# +obr.repository.url = + +# +# Start blueprint bundles synchronously when possible +# +org.apache.aries.blueprint.synchronous=true + +# +# Do not weave all any classes by default +# +org.apache.aries.proxy.weaving.enabled= + +# +# mvn url handler requires config instance configuration +# +#org.ops4j.pax.url.mvn.requireConfigAdminConfig=true + +# +# Don't delay the console startup. Set to true if you want the console to start after all other bundles +# +karaf.delay.console=false diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/custom.properties b/assemblies/nexus-base-template/src/main/resources/overlay/etc/custom.properties new file mode 100644 index 0000000000000000000000000000000000000000..5a0e94524c23e3984b4a2fe9591dbd0891aafe6c --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/custom.properties @@ -0,0 +1,18 @@ +# +# All the values specified here will override the default values given +# in config.properties. +# + +karaf.systemBundlesStartLevel=50 + +# +# You can place any customized configuration here. +# + +# early load of mvn: protocol config +${includes}=org.ops4j.pax.url.mvn.cfg + +karaf.lock.class=org.sonatype.nexus.karaf.NexusFileLock + +nexus-base=${karaf.base} +nexus-args=${karaf.base}/etc/jetty.xml,${karaf.base}/etc/jetty-requestlog.xml diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/ehcache.xml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/ehcache.xml new file mode 100644 index 0000000000000000000000000000000000000000..e9e4c0ad25c1b5b9748d9fdcce5b6c2f9668a1eb --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/ehcache.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/elasticsearch.yml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/elasticsearch.yml new file mode 100644 index 0000000000000000000000000000000000000000..7d1ef1eb55eddbbadb3f28caf7bb16f8fde9a2d7 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/elasticsearch.yml @@ -0,0 +1,404 @@ +cluster.name: nexus + +path: + data: ${nexus-work}/elasticsearch + logs: ${nexus-work}/log + work: ${nexus-work}/tmp/elasticsearch + +node.data: true +node.master: true + +# Disable groovy script integration +script.groovy.sandbox.enabled: false + +# Disable networking +node.local: true +http.enabled: false +discovery.zen.ping.multicast.enabled: false + + +##################### Elasticsearch Configuration Example ##################### + +# This file contains an overview of various configuration settings, +# targeted at operations staff. Application developers should +# consult the guide at . +# +# The installation procedure is covered at +# . +# +# Elasticsearch comes with reasonable defaults for most settings, +# so you can try it out without bothering with configuration. +# +# Most of the time, these defaults are just fine for running a production +# cluster. If you're fine-tuning your cluster, or wondering about the +# effect of certain configuration option, please _do ask_ on the +# mailing list or IRC channel [http://elasticsearch.org/community]. + +# Any element in the configuration can be replaced with environment variables +# by placing them in ${...} notation. For example: +# +#node.rack: ${RACK_ENV_VAR} + +# For information on supported formats and syntax for the config file, see +# + + +################################### Cluster ################################### + +# Cluster name identifies your cluster for auto-discovery. If you're running +# multiple clusters on the same network, make sure you're using unique names. +# +#cluster.name: elasticsearch + + +#################################### Node ##################################### + +# Node names are generated dynamically on startup, so you're relieved +# from configuring them manually. You can tie this node to a specific name: +# +#node.name: "Franz Kafka" + +# Every node can be configured to allow or deny being eligible as the master, +# and to allow or deny to store the data. +# +# Allow this node to be eligible as a master node (enabled by default): +# +#node.master: true +# +# Allow this node to store data (enabled by default): +# +#node.data: true + +# You can exploit these settings to design advanced cluster topologies. +# +# 1. You want this node to never become a master node, only to hold data. +# This will be the "workhorse" of your cluster. +# +#node.master: false +#node.data: true +# +# 2. You want this node to only serve as a master: to not store any data and +# to have free resources. This will be the "coordinator" of your cluster. +# +#node.master: true +#node.data: false +# +# 3. You want this node to be neither master nor data node, but +# to act as a "search load balancer" (fetching data from nodes, +# aggregating results, etc.) +# +#node.master: false +#node.data: false + +# Use the Cluster Health API [http://localhost:9200/_cluster/health], the +# Node Info API [http://localhost:9200/_nodes] or GUI tools +# such as , +# , +# and +# to inspect the cluster state. + +# A node can have generic attributes associated with it, which can later be used +# for customized shard allocation filtering, or allocation awareness. An attribute +# is a simple key value pair, similar to node.key: value, here is an example: +# +#node.rack: rack314 + +# By default, multiple nodes are allowed to start from the same installation location +# to disable it, set the following: +#node.max_local_storage_nodes: 1 + + +#################################### Index #################################### + +# You can set a number of options (such as shard/replica options, mapping +# or analyzer definitions, translog settings, ...) for indices globally, +# in this file. +# +# Note, that it makes more sense to configure index settings specifically for +# a certain index, either when creating it or by using the index templates API. +# +# See and +# +# for more information. + +# Set the number of shards (splits) of an index (5 by default): +# +#index.number_of_shards: 5 + +# Set the number of replicas (additional copies) of an index (1 by default): +# +#index.number_of_replicas: 1 + +# Note, that for development on a local machine, with small indices, it usually +# makes sense to "disable" the distributed features: +# +#index.number_of_shards: 1 +#index.number_of_replicas: 0 + +# These settings directly affect the performance of index and search operations +# in your cluster. Assuming you have enough machines to hold shards and +# replicas, the rule of thumb is: +# +# 1. Having more *shards* enhances the _indexing_ performance and allows to +# _distribute_ a big index across machines. +# 2. Having more *replicas* enhances the _search_ performance and improves the +# cluster _availability_. +# +# The "number_of_shards" is a one-time setting for an index. +# +# The "number_of_replicas" can be increased or decreased anytime, +# by using the Index Update Settings API. +# +# Elasticsearch takes care about load balancing, relocating, gathering the +# results from nodes, etc. Experiment with different settings to fine-tune +# your setup. + +# Use the Index Status API () to inspect +# the index status. + + +#################################### Paths #################################### + +# Path to directory containing configuration (this file and logging.yml): +# +#path.conf: /path/to/conf + +# Path to directory where to store index data allocated for this node. +# +#path.data: /path/to/data +# +# Can optionally include more than one location, causing data to be striped across +# the locations (a la RAID 0) on a file level, favouring locations with most free +# space on creation. For example: +# +#path.data: /path/to/data1,/path/to/data2 + +# Path to temporary files: +# +#path.work: /path/to/work + +# Path to log files: +# +#path.logs: /path/to/logs + +# Path to where plugins are installed: +# +#path.plugins: /path/to/plugins + + +#################################### Plugin ################################### + +# If a plugin listed here is not installed for current node, the node will not start. +# +#plugin.mandatory: mapper-attachments,lang-groovy + + +################################### Memory #################################### + +# Elasticsearch performs poorly when JVM starts swapping: you should ensure that +# it _never_ swaps. +# +# Set this property to true to lock the memory: +# +#bootstrap.mlockall: true + +# Make sure that the ES_MIN_MEM and ES_MAX_MEM environment variables are set +# to the same value, and that the machine has enough memory to allocate +# for Elasticsearch, leaving enough memory for the operating system itself. +# +# You should also make sure that the Elasticsearch process is allowed to lock +# the memory, eg. by using `ulimit -l unlimited`. + + +############################## Network And HTTP ############################### + +# Elasticsearch, by default, binds itself to the 0.0.0.0 address, and listens +# on port [9200-9300] for HTTP traffic and on port [9300-9400] for node-to-node +# communication. (the range means that if the port is busy, it will automatically +# try the next port). + +# Set the bind address specifically (IPv4 or IPv6): +# +#network.bind_host: 192.168.0.1 + +# Set the address other nodes will use to communicate with this node. If not +# set, it is automatically derived. It must point to an actual IP address. +# +#network.publish_host: 192.168.0.1 + +# Set both 'bind_host' and 'publish_host': +# +#network.host: 192.168.0.1 + +# Set a custom port for the node to node communication (9300 by default): +# +#transport.tcp.port: 9300 + +# Enable compression for all communication between nodes (disabled by default): +# +#transport.tcp.compress: true + +# Set a custom port to listen for HTTP traffic: +# +#http.port: 9200 + +# Set a custom allowed content length: +# +#http.max_content_length: 100mb + +# Disable HTTP completely: +# +#http.enabled: false + + +################################### Gateway ################################### + +# The gateway allows for persisting the cluster state between full cluster +# restarts. Every change to the state (such as adding an index) will be stored +# in the gateway, and when the cluster starts up for the first time, +# it will read its state from the gateway. + +# There are several types of gateway implementations. For more information, see +# . + +# The default gateway type is the "local" gateway (recommended): +# +#gateway.type: local + +# Settings below control how and when to start the initial recovery process on +# a full cluster restart (to reuse as much local data as possible when using shared +# gateway). + +# Allow recovery process after N nodes in a cluster are up: +# +#gateway.recover_after_nodes: 1 + +# Set the timeout to initiate the recovery process, once the N nodes +# from previous setting are up (accepts time value): +# +#gateway.recover_after_time: 5m + +# Set how many nodes are expected in this cluster. Once these N nodes +# are up (and recover_after_nodes is met), begin recovery process immediately +# (without waiting for recover_after_time to expire): +# +#gateway.expected_nodes: 2 + + +############################# Recovery Throttling ############################# + +# These settings allow to control the process of shards allocation between +# nodes during initial recovery, replica allocation, rebalancing, +# or when adding and removing nodes. + +# Set the number of concurrent recoveries happening on a node: +# +# 1. During the initial recovery +# +#cluster.routing.allocation.node_initial_primaries_recoveries: 4 +# +# 2. During adding/removing nodes, rebalancing, etc +# +#cluster.routing.allocation.node_concurrent_recoveries: 2 + +# Set to throttle throughput when recovering (eg. 100mb, by default 20mb): +# +#indices.recovery.max_bytes_per_sec: 20mb + +# Set to limit the number of open concurrent streams when +# recovering a shard from a peer: +# +#indices.recovery.concurrent_streams: 5 + + +################################## Discovery ################################## + +# Discovery infrastructure ensures nodes can be found within a cluster +# and master node is elected. Multicast discovery is the default. + +# Set to ensure a node sees N other master eligible nodes to be considered +# operational within the cluster. This should be set to a quorum/majority of +# the master-eligible nodes in the cluster. +# +#discovery.zen.minimum_master_nodes: 1 + +# Set the time to wait for ping responses from other nodes when discovering. +# Set this option to a higher value on a slow or congested network +# to minimize discovery failures: +# +#discovery.zen.ping.timeout: 3s + +# For more information, see +# + +# Unicast discovery allows to explicitly control which nodes will be used +# to discover the cluster. It can be used when multicast is not present, +# or to restrict the cluster communication-wise. +# +# 1. Disable multicast discovery (enabled by default): +# +#discovery.zen.ping.multicast.enabled: false +# +# 2. Configure an initial list of master nodes in the cluster +# to perform discovery when new nodes (master or data) are started: +# +#discovery.zen.ping.unicast.hosts: ["host1", "host2:port"] + +# EC2 discovery allows to use AWS EC2 API in order to perform discovery. +# +# You have to install the cloud-aws plugin for enabling the EC2 discovery. +# +# For more information, see +# +# +# See +# for a step-by-step tutorial. + +# GCE discovery allows to use Google Compute Engine API in order to perform discovery. +# +# You have to install the cloud-gce plugin for enabling the GCE discovery. +# +# For more information, see . + +# Azure discovery allows to use Azure API in order to perform discovery. +# +# You have to install the cloud-azure plugin for enabling the Azure discovery. +# +# For more information, see . + +################################## Slow Log ################################## + +# Shard level query and fetch threshold logging. + +#index.search.slowlog.threshold.query.warn: 10s +#index.search.slowlog.threshold.query.info: 5s +#index.search.slowlog.threshold.query.debug: 2s +#index.search.slowlog.threshold.query.trace: 500ms + +#index.search.slowlog.threshold.fetch.warn: 1s +#index.search.slowlog.threshold.fetch.info: 800ms +#index.search.slowlog.threshold.fetch.debug: 500ms +#index.search.slowlog.threshold.fetch.trace: 200ms + +#index.indexing.slowlog.threshold.index.warn: 10s +#index.indexing.slowlog.threshold.index.info: 5s +#index.indexing.slowlog.threshold.index.debug: 2s +#index.indexing.slowlog.threshold.index.trace: 500ms + +################################## GC Logging ################################ + +#monitor.jvm.gc.young.warn: 1000ms +#monitor.jvm.gc.young.info: 700ms +#monitor.jvm.gc.young.debug: 400ms + +#monitor.jvm.gc.old.warn: 10s +#monitor.jvm.gc.old.info: 5s +#monitor.jvm.gc.old.debug: 2s + +################################## Security ################################ + +# Uncomment if you want to enable JSONP as a valid return transport on the +# http server. With this enabled, it may pose a security risk, so disabling +# it unless you need it is recommended (it is disabled by default). +# +#http.jsonp.enable: true \ No newline at end of file diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/java.util.logging.properties b/assemblies/nexus-base-template/src/main/resources/overlay/etc/java.util.logging.properties new file mode 100644 index 0000000000000000000000000000000000000000..c8d5d5f4b9ab0a98c9e31c853a18dcc1a9eaba62 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/java.util.logging.properties @@ -0,0 +1,4 @@ +# Empty java.util.logging.properties to prevent the log to stderr, so that +# all logs will be delegated to pax logging JUL handler only + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-http-redirect-to-https.xml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-http-redirect-to-https.xml new file mode 100644 index 0000000000000000000000000000000000000000..0c1ab9e3fdd0f8edee00578a282b44edc786e8e0 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-http-redirect-to-https.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + /* + + + + 2 + + + + + + + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-https.xml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-https.xml new file mode 100644 index 0000000000000000000000000000000000000000..3feee264b8aae92000f89264e1b4559534d9fcfe --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-https.xml @@ -0,0 +1,82 @@ + + + + + + + + https + + + + + + + + + + + + /etc/ssl/keystore.jks + OBF:1v2j1uum1xtv1zej1zer1xtn1uvk1v1v + OBF:1v2j1uum1xtv1zej1zer1xtn1uvk1v1v + /etc/ssl/keystore.jks + OBF:1v2j1uum1xtv1zej1zer1xtn1uvk1v1v + + + + + + SSL_RSA_WITH_DES_CBC_SHA + SSL_DHE_RSA_WITH_DES_CBC_SHA + SSL_DHE_DSS_WITH_DES_CBC_SHA + SSL_RSA_EXPORT_WITH_RC4_40_MD5 + SSL_RSA_EXPORT_WITH_DES40_CBC_SHA + SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA + SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA + + + + + + + + + + + + + + + + + http/1.1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-requestlog.xml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-requestlog.xml new file mode 100644 index 0000000000000000000000000000000000000000..a12866694b0941dd0bf2fc5374e8ddaa604ab5b9 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty-requestlog.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + /etc/logback-access.xml + true + + + + + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty.xml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty.xml new file mode 100644 index 0000000000000000000000000000000000000000..ae825278deade74a1acdf4414442ca5ba80f124d --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jetty.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + /etc/nexus-web.xml + /public + + true + + + org.eclipse.jetty.webapp.WebXmlConfiguration + + + + + + + + + + + + + + + + + + + + + + true + 5000 + + + + + + + + + + + + 512 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/jmx.acl.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jmx.acl.cfg new file mode 100644 index 0000000000000000000000000000000000000000..f74b37a8077af4cda7d12623cea924c6abdc2205 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/jmx.acl.cfg @@ -0,0 +1,54 @@ +# +# Generic JMX ACL +# +# This file defines the roles required for MBean operations for MBeans that +# do not have this defined explicitly. +# +# The definition of ACLs for JMX operations works as follows: +# +# The required roles for JMX operations are defined in configuration files +# read via OSGi ConfigAdmin. +# +# JMX RBAC-related configuration is prefixed with jmx.acl and based on the +# JMX ObjectName that it applies to. For example specific configuration for +# an MBean with the following objectName: foo.bar:type=Test can be placed in +# a configuration file called jmx.acl.foo.bar.Test.cfg. More generic +# configuration can be placed in the domain (e.g. jmx.acl.foo.bar.cfg) or +# at the top level (jmx.acl.cfg). A simple configuration file looks like +# this: +# test : admin +# getVal : manager, viewer +# +# The system looks for required roles using the following process: +# The most specific configuration file/pid is tried first. E.g. in the +# above example the jmx.acl.foo.bar.Test.cfg is looked at first. In this +# configuration, the system looks for a: +# 1. Specific match for the current invocation, e.g. test(int)["17"] : role1 +# 2. Reg exp match for the current invocation, e.g. test(int)[/[0-9]/] : role2 +# In both cases the passed argument is converted to a String for the +# comparison. +# If any of the above match all the roles with matching definitions +# are collected and allowed. If no matches are found the following is tried: +# 3. Signature match for the invocation, e.g. test(int) : role3. If +# matched the associated roles are used. +# 4. Method name match for the invocation, e.g. test : role4. If matched +# the associated roles are used. +# 5. A method name wildcard match, e.g. te* : role5. For all the +# wildcard matches found in the current configuration file, the roles +# associated with the longest match are used. So if you have te* and * and +# the method invoked is 'test', then the roles defined with te* are used, +# not the ones defined with *. +# If no matching definition is found in the current configuration file, a +# more general configuration file is looked for. So jmx.acl.foo.bar.cfg is +# tried next, this matches the domain of the MBean. If there is no match +# found in the domain the most generic configuration file is consulted +# (jmx.acl.cfg). +# If a matching definition is found, this is used and the process will not +# look for any other matching definitions. So the most specific definition +# always takes precedence. +# +list* = viewer +get* = viewer +is* = viewer +set* = admin +* = admin \ No newline at end of file diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/keys.properties b/assemblies/nexus-base-template/src/main/resources/overlay/etc/keys.properties new file mode 100644 index 0000000000000000000000000000000000000000..7eb9d0596e9a5e04839d5e264f72c42d609c473f --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/keys.properties @@ -0,0 +1,17 @@ +# +# This file contains the valid users who can log into Karaf. Each line have to be of +# the format: +# +# USER=KEY,ROLE1,ROLE2,... +# +# All users and roles entered in this file are available after Karaf startup +# and modifiable via the JAAS command group. These users reside in a JAAS domain +# with the name "karaf".. +# + +# +# For security reason, the default auto-signed key is disabled. +# The user guide describes how to generate/update the key. +# +#karaf=AAAAB3NzaC1kc3MAAACBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAAAAFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QAAAIEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoAAACBAKKSU2PFl/qOLxIwmBZPPIcJshVe7bVUpFvyl3BbJDow8rXfskl8wO63OzP/qLmcJM0+JbcRU/53JjTuyk31drV2qxhIOsLDC9dGCWj47Y7TyhPdXh/0dthTRBy6bqGtRPxGa7gJov1xm/UuYYXPIUR/3x9MAZvZ5xvE0kYXO+rx,_g_:admingroup +_g_\:admingroup = group,admin,manager,viewer,webconsole diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback-access.xml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback-access.xml new file mode 100644 index 0000000000000000000000000000000000000000..11d342caa03975bb938497636517e219983a4de6 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback-access.xml @@ -0,0 +1,16 @@ + + + + ${nexus-work}/log/request.log + true + + %clientHost %l %user [%date] "%requestURL" %statusCode %bytesSent %elapsedTime + + + ${nexus-work}/log/request-%d{yyyy-MM-dd}.log.gz + 90 + + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback.xml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback.xml new file mode 100644 index 0000000000000000000000000000000000000000..ec495fd8ad508bf58030f9617410aa9fa4c59a02 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/logback.xml @@ -0,0 +1,35 @@ + + + + + + + %d{"yyyy-MM-dd HH:mm:ss,SSSZ"} %-5p [%thread] %X{userId} %c - %m%n + + + + + ${karaf.data}/log/karaf.log + true + + %d{"yyyy-MM-dd HH:mm:ss,SSSZ"} %-5p [%thread] %X{userId} %c - %m%n + + + ${karaf.data}/log/karaf-%d{yyyy-MM-dd}.log.gz + 90 + + + + + + + + + + + + + + + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/nexus-web.xml b/assemblies/nexus-base-template/src/main/resources/overlay/etc/nexus-web.xml new file mode 100644 index 0000000000000000000000000000000000000000..6d5a48980471d3685e9a9a999c5dc90fb7bb76e3 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/nexus-web.xml @@ -0,0 +1,32 @@ + + + + Sonatype Nexus + + + org.sonatype.nexus.bootstrap.osgi.BootstrapListener + + + + nexusFilter + org.sonatype.nexus.bootstrap.osgi.DelegatingFilter + + + + nexusFilter + /* + REQUEST + ERROR + + + + index.html + + + + /error.html + + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.felix.fileinstall-deploy.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.felix.fileinstall-deploy.cfg new file mode 100644 index 0000000000000000000000000000000000000000..3a13eb8c8bbe772a8f9cce448b3f88b2f572b31a --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.felix.fileinstall-deploy.cfg @@ -0,0 +1,10 @@ +# +# Hot deployment of bundles +# + +felix.fileinstall.dir=${karaf.base}/deploy +felix.fileinstall.tmpdir=${karaf.data}/generated-bundles +felix.fileinstall.poll=1000 +felix.fileinstall.active.level=80 +felix.fileinstall.start.level=200 +felix.fileinstall.log.level=3 diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.jaas.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.jaas.cfg new file mode 100644 index 0000000000000000000000000000000000000000..5551593637aed0f3b14b91cb9dc594973bc64eaf --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.jaas.cfg @@ -0,0 +1,42 @@ +# +# Boolean enabling / disabling encrypted passwords +# +encryption.enabled = false + +# +# Encryption Service name +# the default one is 'basic' +# a more powerful one named 'jasypt' is available +# when installing the encryption feature +# +encryption.name = + +# +# Encryption prefix +# +encryption.prefix = {CRYPT} + +# +# Encryption suffix +# +encryption.suffix = {CRYPT} + +# +# Set the encryption algorithm to use in Karaf JAAS login module +# Supported encryption algorithms follow: +# MD2 +# MD5 +# SHA-1 +# SHA-256 +# SHA-384 +# SHA-512 +# +encryption.algorithm = MD5 + +# +# Encoding of the encrypted password. +# Can be: +# hexadecimal +# base64 +# +encryption.encoding = hexadecimal diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.kar.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.kar.cfg new file mode 100644 index 0000000000000000000000000000000000000000..cd34c462c7fafb2e2cfe8bbd84bfd6cce6fd6a6e --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.kar.cfg @@ -0,0 +1,5 @@ +# +# Enable or disable the refresh of the bundles when installing +# the features contained in a KAR file +# +noAutoRefreshBundles=false \ No newline at end of file diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.log.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.log.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e28079c8ce59ddcadf0f4e996d07b8b6d8f4c371 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.log.cfg @@ -0,0 +1,12 @@ +# +# This configuration file is used to configure the default values for the log:display +# and log:exception-display commands. +# + +# The number of log statements to be displayed using log:display +size=500 + +# this matches the pattern in logback.xml with a few caveats +# the date %d parameter is split up to avoid quoting in output +# the %n parameter is omitted to avoid duplicate new lines +pattern=%d{yyyy-MM-dd} %d{HH:mm:ss,SSSZ} %-5p [%thread] %X{userId} %c - %m diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.management.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.management.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e04e22be190d4fa4313116c871c850e2d0961b9c --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.management.cfg @@ -0,0 +1,48 @@ +# +# The properties in this file define the configuration of Apache Karaf's JMX Management +# + +# +# Port number for RMI registry connection +# +rmiRegistryPort = 8099 + +# +# Host for RMI registry +# +rmiRegistryHost = 0.0.0.0 + +# +# Port number for RMI server connection +# +rmiServerPort = 8044 + +# +# Host for RMI server +# +rmiServerHost = 0.0.0.0 + +# +# Name of the JAAS realm used for authentication +# +jmxRealm = karaf + +# +# The service URL for the JMXConnectorServer +# +serviceUrl = service:jmx:rmi://${rmiServerHost}:${rmiServerPort}/jndi/rmi://${rmiRegistryHost}:${rmiRegistryPort}/karaf-${karaf.name} + +# +# Whether any threads started for the JMXConnectorServer should be started as daemon threads +# +daemon = true + +# +# Whether the JMXConnectorServer should be started in a separate thread +# +threaded = true + +# +# The ObjectName used to register the JMXConnectorServer +# +objectName = connector:name=rmi diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.shell.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.shell.cfg new file mode 100644 index 0000000000000000000000000000000000000000..1460b47bb119b817e37cfee233888063702d1e10 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.apache.karaf.shell.cfg @@ -0,0 +1,54 @@ +# +# These properties are used to configure Karaf's ssh shell. +# + +# +# Via sshPort and sshHost you define the address you can login into Karaf. +# +sshPort = 8022 +sshHost = 0.0.0.0 + +# +# The sshIdleTimeout defines the inactivity timeout to logout the SSH session. +# The sshIdleTimeout is in milliseconds, and the default is set to 30 minutes. +# +sshIdleTimeout = 1800000 + +# +# sshRealm defines which JAAS domain to use for password authentication. +# +sshRealm = karaf + +# +# The location of the hostKey file defines where the private/public key of the server +# is located. If no file is at the defined location it will be ignored. +# +hostKey = ${karaf.etc}/host.key + +# +# Self defined key size in 1024, 2048, 3072, or 4096 +# If not set, this defaults to 4096. +# +# keySize = 4096 + +# +# Specify host key algorithm, defaults to RSA +# +# algorithm = RSA + +# Specify an additional welcome banner to be displayed when a user logs into the server. +# +# welcomeBanner = + +# +# Defines the completion mode on the Karaf shell console. The possible values are: +# - GLOBAL: it's the same behavior as in previous Karaf releases. The completion displays all commands and all aliases +# ignoring if you are in a subshell or not. +# - FIRST: the completion displays all commands and all aliases only when you are not in a subshell. When you are +# in a subshell, the completion displays only the commands local to the subshell. +# - SUBSHELL: the completion displays only the subshells on the root level. When you are in a subshell, the completion +# displays only the commands local to the subshell. +# This property define the default value when you use the Karaf shell console. +# You can change the completion mode directly in the shell console, using shell:completion command. +# +completionMode = GLOBAL diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.ops4j.pax.logging.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.ops4j.pax.logging.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.ops4j.pax.url.mvn.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.ops4j.pax.url.mvn.cfg new file mode 100644 index 0000000000000000000000000000000000000000..fe251ae31e0b87b22e08c10039f29a5894ae9fc0 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.ops4j.pax.url.mvn.cfg @@ -0,0 +1,78 @@ +########################################################################## +# customized to only lookup bundles from the assembled system repository # +########################################################################## + +# +# If set to true, the following property will not allow any certificate to be used +# when accessing Maven repositories through SSL +# +#org.ops4j.pax.url.mvn.certificateCheck= + +# +# Path to the local Maven settings file. +# The repositories defined in this file will be automatically added to the list +# of default repositories if the 'org.ops4j.pax.url.mvn.repositories' property +# below is not set. +# The following locations are checked for the existence of the settings.xml file +# * 1. looks for the specified url +# * 2. if not found looks for ${user.home}/.m2/settings.xml +# * 3. if not found looks for ${maven.home}/conf/settings.xml +# * 4. if not found looks for ${M2_HOME}/conf/settings.xml +# +# Default to empty settings file, comment this out to pick up your local Maven settings +org.ops4j.pax.url.mvn.settings=${karaf.home}/${karaf.default.repository}/settings.xml + +# +# Path to the local Maven repository which is used to avoid downloading +# artifacts when they already exist locally. +# The value of this property will be extracted from the settings.xml file +# above, or defaulted to: +# System.getProperty( "user.home" ) + "/.m2/repository" +# +# Default to Karaf's system repository, comment this out to use your local repository +org.ops4j.pax.url.mvn.localRepository=file:${karaf.home}/${karaf.default.repository} + +# +# Default this to false. It's just weird to use undocumented repos +# +org.ops4j.pax.url.mvn.useFallbackRepositories=false + +# +# Uncomment if you don't wanna use the proxy settings +# from the Maven conf/settings.xml file +# +org.ops4j.pax.url.mvn.proxySupport=false + +# +# Comma separated list of repositories scanned when resolving an artifact. +# Those repositories will be checked before iterating through the +# below list of repositories and even before the local repository +# A repository url can be appended with zero or more of the following flags: +# @snapshots : the repository contains snapshots +# @noreleases : the repository does not contain any released artifacts +# +# The following property value will add the system folder as a repo. +# +org.ops4j.pax.url.mvn.defaultRepositories=\ + file:${karaf.home}/${karaf.default.repository}@id=system.repository@snapshots + +# Use the default local repo (e.g.~/.m2/repository) as a "remote" repo +#org.ops4j.pax.url.mvn.defaultLocalRepoAsRemote= + +# +# Comma separated list of repositories scanned when resolving an artifact. +# A repository url can be appended with zero or more of the following flags: +# @snapshots : the repository contains snapshots +# @noreleases : the repository does not contain any released artifacts +# @id=repoid : the id for the repository, just like in the settings.xml this is optional but recommended +# +# The default list doesn't contain any snapshot repositories as it can impact artifact resolution. +# During development you may want to add the following repositories containing snapshots: +# https://repository.apache.org/content/groups/snapshots-group@id=apache@snapshots@noreleases +# https://oss.sonatype.org/content/repositories/snapshots@id=sonatype@snapshots@norelease +# https://oss.sonatype.org/content/repositories/ops4j-snapshots@id=ops4j@snapshots@noreleases +# +# If you want to install additional bundles from central add this entry to the list: +# https://repo1.maven.org/maven2@id=central +# +org.ops4j.pax.url.mvn.repositories= diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.sonatype.nexus.cfg b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.sonatype.nexus.cfg new file mode 100644 index 0000000000000000000000000000000000000000..387ac80702bd77c0fcc24bb69ea9a326d5b30903 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/org.sonatype.nexus.cfg @@ -0,0 +1,19 @@ +# Jetty section +application-port=8081 +application-host=0.0.0.0 +nexus-context-path=/ + +# Nexus section +nexus-work=${karaf.data} +nexus-edition=nexus-oss-edition +nexus-features=\ + nexus-quartz-plugin,\ + nexus-siesta-plugin,\ + nexus-ssl-plugin,\ + nexus-rapture-plugin,\ + nexus-extdirect-plugin,\ + nexus-coreui-plugin,\ + nexus-repository-httpbridge,\ + nexus-repository-maven,\ + nexus-repository-raw + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/shell.init.script b/assemblies/nexus-base-template/src/main/resources/overlay/etc/shell.init.script new file mode 100644 index 0000000000000000000000000000000000000000..a237abdfe7e75bae91538ecca073dd40b9044ae5 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/shell.init.script @@ -0,0 +1,14 @@ +// +// This script is run each time a shell is created. +// You can define here closures or variables that will be available +// in each session. +// +ld = { log:display $args } ; +lde = { log:exception-display $args } ; +la = { bundle:list -t 0 $args } ; +ls = { service:list $args } ; +cl = { config:list "(service.pid=$args)" } ; +halt = { system:shutdown -h -f $args } ; +help = { *:help $args | more } ; +man = { help $args } ; +log:list = { log:get ALL } ; \ No newline at end of file diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/system.properties b/assemblies/nexus-base-template/src/main/resources/overlay/etc/system.properties new file mode 100644 index 0000000000000000000000000000000000000000..0745aa4d43477f4658f71307f8b414d00009127a --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/system.properties @@ -0,0 +1,100 @@ +# +# The properties defined in this file will be made available through system +# properties at the very beginning of the Karaf's boot process. +# + + +# Log level when the pax-logging service is not available +# This level will only be used while the pax-logging service bundle +# is not fully available. +# To change log levels, please refer to the org.ops4j.pax.logging.cfg file +# instead. +org.ops4j.pax.logging.DefaultServiceLog.level = ERROR + +# +# Name of this Karaf instance. +# +karaf.name = root + +# +# Default repository where bundles will be loaded from before using +# other Maven repositories. For the full Maven configuration, see +# the org.ops4j.pax.url.mvn.cfg file. +# +karaf.default.repository = system + +# +# Location of a shell script that will be run when starting a shell +# session. This script can be used to create aliases and define +# additional commands. +# +karaf.shell.init.script = ${karaf.etc}/shell.init.script + +# +# Sets the maximum size of the shell command history. If not set, +# defaults to 500 entries. Setting to 0 will disable history. +# +# karaf.shell.history.maxSize = 0 + +# +# Deletes the entire karaf.data directory at every start +# +karaf.clean.all = false + +# +# Deletes the karaf.data/cache directory at every start +# +karaf.clean.cache = false + +# +# Roles to use when logging into a local Karaf console. +# +# The syntax is the following: +# [classname:]principal +# where classname is the class name of the principal object +# (defaults to org.apache.karaf.jaas.modules.RolePrincipal) +# and principal is the name of the principal of that class +# (defaults to instance). +# +karaf.local.roles = admin,manager,viewer + +# +# Set this empty property to avoid errors when validating xml documents. +# +xml.catalog.files = + +# +# Suppress the bell in the console when hitting backspace too many times +# for example +# +jline.nobell = true + +# +# ServiceMix specs options +# +org.apache.servicemix.specs.debug = false +org.apache.servicemix.specs.timeout = 0 + +# +# Settings for the OSGi 4.3 Weaving +# By default, we will not weave any classes. Change this setting to include classes +# that you application needs to have woven. +# +org.apache.aries.proxy.weaving.enabled = none +# Classes not to weave - Aries default + Xerces which is known to have issues. +org.apache.aries.proxy.weaving.disabled = org.objectweb.asm.*,org.slf4j.*,org.apache.log4j.*,javax.*,org.apache.xerces.* + +# +# By default, only Karaf shell commands are secured, but additional services can be +# secured by expanding this filter +# +karaf.secured.services = (&(osgi.command.scope=*)(osgi.command.function=*)) + +karaf.history=${user.home}/.nexus/karaf.history + +com.sun.jndi.ldap.connect.pool.protocol="plain ssl" + +org.ops4j.pax.logging.StaticLogbackContext=true +org.ops4j.pax.logging.StaticLogbackFile=${karaf.base}/etc/logback.xml + +h2.useThreadContextClassLoader=true diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/etc/users.properties b/assemblies/nexus-base-template/src/main/resources/overlay/etc/users.properties new file mode 100644 index 0000000000000000000000000000000000000000..5fc2db1ede5b4806e204be8f22a4c794b0cf96c0 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/etc/users.properties @@ -0,0 +1,14 @@ +# +# This file contains the users, groups, and roles. +# Each line has to be of the format: +# +# USER=PASSWORD,ROLE1,ROLE2,... +# USER=PASSWORD,_g_:GROUP,... +# _g_\:GROUP=ROLE1,ROLE2,... +# +# All users, groups, and roles entered in this file are available after Karaf startup +# and modifiable via the JAAS command group. These users reside in a JAAS domain +# with the name "karaf". +# +karaf = karaf,_g_:admingroup +_g_\:admingroup = group,admin,manager,viewer,webconsole diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/lib/endorsed/.placeholder b/assemblies/nexus-base-template/src/main/resources/overlay/lib/endorsed/.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/lib/ext/.placeholder b/assemblies/nexus-base-template/src/main/resources/overlay/lib/ext/.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/COPYRIGHT.html b/assemblies/nexus-base-template/src/main/resources/overlay/public/COPYRIGHT.html new file mode 100644 index 0000000000000000000000000000000000000000..661315bbc3abd1d0a296731e294e28db3c0d1da8 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/public/COPYRIGHT.html @@ -0,0 +1,33 @@ + + + + + Copyright + + + +

+ Copyright © 2008-present Sonatype, Inc. +

+ +

+ All rights reserved. Includes the third-party code listed at + http://links.sonatype.com/products/nexus/oss/attributions +

+ +

+ Sonatype and Sonatype Nexus are trademarks of Sonatype, Inc. +
+ Apache Maven is a trademark of the Apache Foundation. +
+ M2Eclipse is a trademark of the Eclipse Foundation. +
+ All other trademarks are the property of their respective owners. +

+ + diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/LICENSE.html b/assemblies/nexus-base-template/src/main/resources/overlay/public/LICENSE.html new file mode 100644 index 0000000000000000000000000000000000000000..3998fcebeebe4d34f47c8777b4db160be1d1dec1 --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/public/LICENSE.html @@ -0,0 +1,261 @@ + + + + + + +Eclipse Public License - Version 1.0 + + + + + + +

Eclipse Public License - v 1.0

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR +DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS +AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) in the case of the initial Contributor, the initial +code and documentation distributed under this Agreement, and

+

b) in the case of each subsequent Contributor:

+

i) changes to the Program, and

+

ii) additions to the Program;

+

where such changes and/or additions to the Program +originate from and are distributed by that particular Contributor. A +Contribution 'originates' from a Contributor if it was added to the +Program by such Contributor itself or anyone acting on such +Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) +are not derivative works of the Program.

+ +

"Contributor" means any person or entity that distributes +the Program.

+ +

"Licensed Patents" mean patent claims licensable by a +Contributor which are necessarily infringed by the use or sale of its +Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions distributed in accordance +with this Agreement.

+ +

"Recipient" means anyone who receives the Program under +this Agreement, including all Contributors.

+ +

2. GRANT OF RIGHTS

+ +

a) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free copyright license to reproduce, prepare derivative works +of, publicly display, publicly perform, distribute and sublicense the +Contribution of such Contributor, if any, and such derivative works, in +source code and object code form.

+ +

b) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, +offer to sell, import and otherwise transfer the Contribution of such +Contributor, if any, in source code and object code form. This patent +license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, +such addition of the Contribution causes such combination to be covered +by the Licensed Patents. The patent license shall not apply to any other +combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) Recipient understands that although each Contributor +grants the licenses to its Contributions set forth herein, no assurances +are provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program.

+ +

d) Each Contributor represents that to its knowledge it +has sufficient copyright rights in its Contribution, if any, to grant +the copyright license set forth in this Agreement.

+ +

3. REQUIREMENTS

+ +

A Contributor may choose to distribute the Program in object code +form under its own license agreement, provided that:

+ +

a) it complies with the terms and conditions of this +Agreement; and

+ +

b) its license agreement:

+ +

i) effectively disclaims on behalf of all Contributors +all warranties and conditions, express and implied, including warranties +or conditions of title and non-infringement, and implied warranties or +conditions of merchantability and fitness for a particular purpose;

+ +

ii) effectively excludes on behalf of all Contributors +all liability for damages, including direct, indirect, special, +incidental and consequential damages, such as lost profits;

+ +

iii) states that any provisions which differ from this +Agreement are offered by that Contributor alone and not by any other +party; and

+ +

iv) states that source code for the Program is available +from such Contributor, and informs licensees how to obtain it in a +reasonable manner on or through a medium customarily used for software +exchange.

+ +

When the Program is made available in source code form:

+ +

a) it must be made available under this Agreement; and

+ +

b) a copy of this Agreement must be included with each +copy of the Program.

+ +

Contributors may not remove or alter any copyright notices contained +within the Program.

+ +

Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

Commercial distributors of software may accept certain +responsibilities with respect to end users, business partners and the +like. While this license is intended to facilitate the commercial use of +the Program, the Contributor who includes the Program in a commercial +product offering should do so in a manner which does not create +potential liability for other Contributors. Therefore, if a Contributor +includes the Program in a commercial product offering, such Contributor +("Commercial Contributor") hereby agrees to defend and +indemnify every other Contributor ("Indemnified Contributor") +against any losses, damages and costs (collectively "Losses") +arising from claims, lawsuits and other legal actions brought by a third +party against the Indemnified Contributor to the extent caused by the +acts or omissions of such Commercial Contributor in connection with its +distribution of the Program in a commercial product offering. The +obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In +order to qualify, an Indemnified Contributor must: a) promptly notify +the Commercial Contributor in writing of such claim, and b) allow the +Commercial Contributor to control, and cooperate with the Commercial +Contributor in, the defense and any related settlement negotiations. The +Indemnified Contributor may participate in any such claim at its own +expense.

+ +

For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to +the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT +NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR +DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED +HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other +software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the +date such litigation is filed.

+ +

All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of time +after becoming aware of such noncompliance. If all Recipient's rights +under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute copies of this +Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The +Agreement Steward reserves the right to publish new versions (including +revisions) of this Agreement from time to time. No one other than the +Agreement Steward has the right to modify this Agreement. The Eclipse +Foundation is the initial Agreement Steward. The Eclipse Foundation may +assign the responsibility to serve as the Agreement Steward to a +suitable separate entity. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version +of the Agreement is published, Contributor may elect to distribute the +Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or +otherwise. All rights in the Program not expressly granted under this +Agreement are reserved.

+ +

This Agreement is governed by the laws of the State of New York and +the intellectual property laws of the United States of America. No party +to this Agreement will bring a legal action under this Agreement more +than one year after the cause of action arose. Each party waives its +rights to a jury trial in any resulting litigation.

+ + + + \ No newline at end of file diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.ico b/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..fafe89df18be374ff24fbeae32f40c347b8662b2 Binary files /dev/null and b/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.ico differ diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.png b/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..a5c3e57bfa9139b0217618c7024d5710dea7934f Binary files /dev/null and b/assemblies/nexus-base-template/src/main/resources/overlay/public/favicon.png differ diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/public/robots.txt b/assemblies/nexus-base-template/src/main/resources/overlay/public/robots.txt new file mode 100644 index 0000000000000000000000000000000000000000..36ad3aa1347abc85187695a4728d091df319232f --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Disallow: /repository/ +Disallow: /service/ +Allow: / diff --git a/assemblies/nexus-base-template/src/main/resources/overlay/system/settings.xml b/assemblies/nexus-base-template/src/main/resources/overlay/system/settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..f0a1b0b121a3f3e4d66de0593da6894b50b24a5f --- /dev/null +++ b/assemblies/nexus-base-template/src/main/resources/overlay/system/settings.xml @@ -0,0 +1 @@ + diff --git a/assemblies/nexus-boot-feature/pom.xml b/assemblies/nexus-boot-feature/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..601cda86115eaf545c4386de694a60c0dd7f1897 --- /dev/null +++ b/assemblies/nexus-boot-feature/pom.xml @@ -0,0 +1,151 @@ + + + 4.0.0 + + + org.sonatype.nexus.assemblies + nexus-assemblies + 3.0.0-SNAPSHOT + + + nexus-boot-feature + ${project.groupId}:${project.artifactId} + feature + + + + org.ops4j.pax.logging + pax-logging-logback + + + + org.sonatype.nexus + pax-logging-metrics + + + + org.sonatype.nexus + pax-logging-access + + + + ch.qos.logback + logback-access + + + + + javax.servlet + javax.servlet-api + + + + org.eclipse.jetty + jetty-webapp + + + + org.eclipse.jetty + jetty-continuation + + + + org.eclipse.jetty + jetty-servlet + + + + org.eclipse.jetty + jetty-security + + + + org.eclipse.jetty + jetty-server + + + + org.eclipse.jetty + jetty-deploy + + + + org.eclipse.jetty + jetty-util + + + + org.eclipse.jetty + jetty-util-ajax + + + + org.eclipse.jetty + jetty-servlets + + + + org.eclipse.jetty + jetty-proxy + + + + org.eclipse.jetty + jetty-http + + + + org.eclipse.jetty + jetty-io + + + + org.eclipse.jetty + jetty-xml + + + + org.eclipse.jetty + jetty-jmx + + + + org.eclipse.jetty + jetty-rewrite + + + + com.codahale.metrics + metrics-jetty9 + + + + org.sonatype.nexus + nexus-bootstrap + + + + + + + org.apache.karaf.tooling + karaf-maven-plugin + + + + + diff --git a/assemblies/pom.xml b/assemblies/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..f2374a9d692f5e92bb723075c0dbadeabb9ba35c --- /dev/null +++ b/assemblies/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-parent + 3.0.0-SNAPSHOT + + + org.sonatype.nexus.assemblies + nexus-assemblies + ${project.groupId}:${project.artifactId} + pom + + + karaf-nexus-branding + nexus-boot-feature + nexus-base-edition + nexus-base-template + + + + + + + + + org.sonatype.nexus + nexus-components + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.plugins + nexus-plugins + pom + 3.0.0-SNAPSHOT + import + + + + + + diff --git a/buildsupport/README.md b/buildsupport/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7e332743eccf960f43ba2e0f7aff0e054076e909 --- /dev/null +++ b/buildsupport/README.md @@ -0,0 +1,19 @@ + +# Build Support + +Modules which provide `dependencyManagement` configuration in small(ish) groups. + +The `all` module aggregates to allow a single import to pick up all the configuration. diff --git a/buildsupport/all/pom.xml b/buildsupport/all/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..74c1249b5090eaff7efb3df34abefae969b885b8 --- /dev/null +++ b/buildsupport/all/pom.xml @@ -0,0 +1,172 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-all + ${project.groupId}:${project.artifactId} + pom + + + + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-commons + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-db + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-goodies + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-groovy + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-guice + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-httpclient + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-internal + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-jetty + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-logging + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-maven + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-metrics + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-osgi + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-other + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-rest + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-security + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-testing + pom + 3.0.0-SNAPSHOT + import + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-ui + pom + 3.0.0-SNAPSHOT + import + + + + + diff --git a/buildsupport/commons/pom.xml b/buildsupport/commons/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..323c3611d133da363e7f777685640831b7fa74c9 --- /dev/null +++ b/buildsupport/commons/pom.xml @@ -0,0 +1,102 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-commons + ${project.groupId}:${project.artifactId} + pom + + + + + + commons-io + commons-io + 2.4 + + + + commons-codec + commons-codec + 1.10 + + + + commons-lang + commons-lang + 2.6 + + + + org.apache.commons + commons-lang3 + 3.4 + + + + commons-fileupload + commons-fileupload + 1.3.1 + + + + commons-beanutils + commons-beanutils + 1.9.2 + + + commons-logging + commons-logging + + + + + + commons-collections + commons-collections + 3.2.1 + + + + commons-configuration + commons-configuration + 1.10 + + + + org.apache.commons + commons-compress + 1.10 + + + + org.apache.commons + commons-email + 1.4 + + + + + + diff --git a/buildsupport/db/pom.xml b/buildsupport/db/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..23190e000ae842010f6656d65a30f6cb57b6cbf1 --- /dev/null +++ b/buildsupport/db/pom.xml @@ -0,0 +1,150 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-db + ${project.groupId}:${project.artifactId} + pom + + + 2.1.2 + + + + + + + com.h2database + h2 + 1.4.188 + + + + org.mapdb + mapdb + 1.0.8 + + + + com.orientechnologies + orientdb-core + ${orientdb.version} + + + org.xerial.snappy + snappy-java + + + + + + com.orientechnologies + orientdb-enterprise + ${orientdb.version} + + + + com.orientechnologies + orientdb-tools + ${orientdb.version} + + + com.orientechnologies + orientdb-core + + + com.orientechnologies + orientdb-client + + + com.orientechnologies + orientdb-object + + + + + + com.orientechnologies + orientdb-object + ${orientdb.version} + + + + com.orientechnologies + orientdb-server + ${orientdb.version} + + + + com.orientechnologies + orientdb-distributed + ${orientdb.version} + + + com.hazelcast + hazelcast-all + + + com.orientechnologies + orientdb-graphdb + + + + + + com.orientechnologies + orientdb-client + ${orientdb.version} + + + + com.orientechnologies + orientdb-jdbc + ${orientdb.version} + + + + com.orientechnologies + orientdb-community + distribution + zip + ${orientdb.version} + + + + mysql + mysql-connector-java + 5.1.36 + + + + com.hazelcast + hazelcast + 3.3.5 + + + + + + diff --git a/buildsupport/extjs-maven-plugin/pom.xml b/buildsupport/extjs-maven-plugin/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..44a7d6a00f3337e7c719316c4bd0135e4e67fd23 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/pom.xml @@ -0,0 +1,116 @@ + + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + extjs-maven-plugin + ${project.groupId}:${project.artifactId} + maven-plugin + + + + + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-all + pom + 3.0.0-SNAPSHOT + import + + + + + + + + org.apache.maven + maven-plugin-api + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + + + + com.google.guava + guava + + + + com.google.code.findbugs + jsr305 + true + + + + + org.codehaus.plexus + plexus-utils + + + + org.mozilla + rhino + + + + org.sonatype.goodies + goodies-testsupport + test + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.1 + + + + descriptor + helpmojo + + + + true + + + + + + + + diff --git a/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/AggregateJsMojo.java b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/AggregateJsMojo.java new file mode 100644 index 0000000000000000000000000000000000000000..a23f7543b5456baf52549bf103c9bc650b51c0a5 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/AggregateJsMojo.java @@ -0,0 +1,129 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.Writer; +import java.util.List; +import java.util.Map; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.codehaus.plexus.util.DirectoryScanner; + +import static org.apache.maven.plugins.annotations.LifecyclePhase.PROCESS_RESOURCES; + +/** + * Aggregate javascript sources in order as required by ExtJS. + * + * @since 3.0 + */ +@Mojo(name="aggregate-js", defaultPhase = PROCESS_RESOURCES) +public class AggregateJsMojo + extends AbstractMojo +{ + public static final String[] DEFAULT_INCLUDES = { "**/*.js" }; + + @Parameter(required = true) + private File sourceDirectory; + + @Parameter + private String[] includes; + + @Parameter + private String[] excludes; + + @Parameter(required = true) + private File outputFile; + + @Parameter + private String namespace; + + /** + * Enables extra warnings which may hint at problems. + */ + @Parameter(property = "extjs.warnings", defaultValue = "false") + private boolean warnings; + + /** + * Enable omission (auto-commenting) of lines surrounded by {@code //} and {@code //}. + */ + @Parameter(property = "extjs.omit", defaultValue = "false") + private boolean omit; + + /** + * Omission flags. When named flag is enabled, sections matching those omission tokens will be commented out. + */ + @Parameter + private Map omitFlags; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + try { + doExecute(); + } + catch (Exception e) { + throw new MojoExecutionException(e.toString(), e); + } + } + + private void doExecute() throws Exception { + // build scanner to find files to aggregate + DirectoryScanner files = new DirectoryScanner(); + files.setBasedir(sourceDirectory); + if (includes == null || includes.length == 0) { + files.setIncludes(DEFAULT_INCLUDES); + } + else { + files.setIncludes(includes); + } + files.setExcludes(excludes); + files.addDefaultExcludes(); + + // build the list of ordered classes to include + ClassDefScanner scanner = new ClassDefScanner(getLog()); + scanner.setWarnings(warnings); + if (namespace == null) { + getLog().warn("Namespace is not configured; will be unable to detect and resolve MVC references"); + } + else { + scanner.setNamespace(namespace); + } + List classes = scanner.scan(files); + + // aggregate all class sources + getLog().info("Writing: " + outputFile); + Writer output = new BufferedWriter(new FileWriter(outputFile)); + try { + FileAppender appender; + if (omit) { + appender = new OmissionFileAppender(getLog(), output, omitFlags); + } + else { + appender = new FileAppender(getLog(), output); + } + + for (ClassDef def : classes) { + appender.append(def.getSource()); + } + } + finally { + output.close(); + } + } +} diff --git a/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDef.java b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDef.java new file mode 100644 index 0000000000000000000000000000000000000000..b7e14228ca1bc11cffa663d72bd36c94f3c183b5 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDef.java @@ -0,0 +1,192 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * ExtJS class definition record. + * + * @since 3.0 + */ +public class ClassDef +{ + private final String name; + + private final File source; + + private final Set dependencies = new HashSet<>(); + + private final List alternateClassName = new ArrayList<>(); + + private final List alias = new ArrayList<>(); + + private double priority = 0; + + private String extend; + + private String override; + + private List requires; + + private List mixins; + + private List uses; + + private List mvcControllers; + + private List mvcModels; + + private List mvcStores; + + private List mvcViews; + + public ClassDef(final String name, final File source) { + this.name = checkNotNull(name); + this.source = checkNotNull(source); + } + + public String getName() { + return name; + } + + public File getSource() { + return source; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "name='" + name + '\'' + + ", source=" + source + + '}'; + } + + public Set getDependencies() { + return dependencies; + } + + public List getAlternateClassName() { + return alternateClassName; + } + + public List getAlias() { + return alias; + } + + public double getPriority() { + return priority; + } + + public void setPriority(final double priority) { + this.priority = priority; + } + + @Nullable + public String getExtend() { + return extend; + } + + public void setExtend(final String extend) { + this.extend = checkNotNull(extend); + dependencies.add(extend); + } + + @Nullable + public String getOverride() { + return override; + } + + public void setOverride(final String override) { + this.override = checkNotNull(override); + dependencies.add(override); + } + + @Nullable + public List getRequires() { + return requires; + } + + public void setRequires(final List requires) { + this.requires = checkNotNull(requires); + dependencies.addAll(requires); + } + + @Nullable + public List getMixins() { + return mixins; + } + + public void setMixins(final List mixins) { + this.mixins = checkNotNull(mixins); + dependencies.addAll(mixins); + } + + @Nullable + public List getUses() { + return uses; + } + + public void setUses(final List uses) { + this.uses = checkNotNull(uses); + dependencies.addAll(uses); + } + + // + // Custom handling for MVC support + // + + @Nullable + public List getMvcControllers() { + return mvcControllers; + } + + public void setMvcControllers(final List mvcControllers) { + this.mvcControllers = checkNotNull(mvcControllers); + } + + @Nullable + public List getMvcModels() { + return mvcModels; + } + + public void setMvcModels(final List mvcModels) { + this.mvcModels = checkNotNull(mvcModels); + } + + @Nullable + public List getMvcStores() { + return mvcStores; + } + + public void setMvcStores(final List mvcStores) { + this.mvcStores = checkNotNull(mvcStores); + } + + @Nullable + public List getMvcViews() { + return mvcViews; + } + + public void setMvcViews(final List mvcViews) { + this.mvcViews = checkNotNull(mvcViews); + } +} diff --git a/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDefScanner.java b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDefScanner.java new file mode 100644 index 0000000000000000000000000000000000000000..60012debeb7994062cf294ac1253f25bd3490ccc --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/ClassDefScanner.java @@ -0,0 +1,617 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +import org.apache.maven.plugin.logging.Log; +import org.codehaus.plexus.util.Scanner; +import org.codehaus.plexus.util.dag.DAG; +import org.codehaus.plexus.util.dag.TopologicalSorter; +import org.codehaus.plexus.util.dag.Vertex; +import org.mozilla.javascript.CompilerEnvirons; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ErrorReporter; +import org.mozilla.javascript.Parser; +import org.mozilla.javascript.ast.ArrayLiteral; +import org.mozilla.javascript.ast.AstNode; +import org.mozilla.javascript.ast.AstRoot; +import org.mozilla.javascript.ast.FunctionCall; +import org.mozilla.javascript.ast.Name; +import org.mozilla.javascript.ast.NewExpression; +import org.mozilla.javascript.ast.NodeVisitor; +import org.mozilla.javascript.ast.NumberLiteral; +import org.mozilla.javascript.ast.ObjectLiteral; +import org.mozilla.javascript.ast.ObjectProperty; +import org.mozilla.javascript.ast.PropertyGet; +import org.mozilla.javascript.ast.StringLiteral; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.mozilla.javascript.Token.CALL; +import static org.mozilla.javascript.Token.NEW; + +/** + * ExtJS 4+ class definition scanner. + * + * @since 3.0 + */ +public class ClassDefScanner +{ + private final Map classes = new HashMap<>(); + + private final Log log; + + private boolean warnings = false; + + private String namespace; + + public ClassDefScanner(final Log log) { + this.log = checkNotNull(log); + } + + public boolean isWarnings() { + return warnings; + } + + public void setWarnings(final boolean warnings) { + this.warnings = warnings; + } + + @Nullable + public String getNamespace() { + return namespace; + } + + public void setNamespace(final @Nullable String namespace) { + this.namespace = namespace; + } + + /** + * Scan a set of files and return a list of class definitions in dependency order. + * ie. Foo extends Bar, Bar will be before Foo. + */ + public List scan(final Scanner files) throws Exception { + checkNotNull(files); + + final boolean debug = log.isDebugEnabled(); + + // reset and scan for files + classes.clear(); + + log.debug("Finding files to scan..."); + files.scan(); + + // scan each file + String[] included = files.getIncludedFiles(); + log.debug("Scanning " + included.length + " files..."); + for (String path : included) { + File file = new File(files.getBasedir(), path); + scan(file); + } + + // TODO: Sort out how/if we want to resolve aliases, ATM this data is collected by not used + + // Resolve all references and build map with all class defs (including aliases/alts) + Map classNames = new HashMap<>(); + List allClasses = new ArrayList<>(); + for (ClassDef def : classes.values()) { + log.debug("Processing: " + def); + + classNames.put(def.getName(), def); + for (String alt : def.getAlternateClassName()) { + classNames.put(alt, def); + } + + resolve(def); + + allClasses.add(def); + } + + // Sort all classes by priority, so that higher-priority value is closer to the start and processed sooner + // ie. priority=10 is closer to the start than priority=1 + Collections.sort(allClasses, new Comparator() + { + @Override + public int compare(final ClassDef a, final ClassDef b) { + return Double.compare(b.getPriority(), a.getPriority()); + } + }); + + if (debug) { + log.debug("All classes:"); + for (ClassDef def : allClasses) { + log.debug(" " + def.getName()); + } + } + + // build the graph + DAG graph = new DAG(); + for (ClassDef def : allClasses) { + graph.addVertex(def.getName()); + for (String name : def.getDependencies()) { + // resolve dependencies which have class defs to primary class name + ClassDef dep = classNames.get(name); + if (dep != null) { + name = dep.getName(); + } + graph.addEdge(def.getName(), name); + } + } + + // display some debug information about the graph + if (debug) { + log.debug("Vertices:"); + for (Vertex v : graph.getVerticies()) { + log.debug(" " + v.getLabel()); + if (!v.getParents().isEmpty()) { + log.debug(" parents:"); + for (Vertex parent : v.getParents()) { + log.debug(" " + parent.getLabel()); + } + } + if (!v.getChildren().isEmpty()) { + log.debug(" children:"); + for (Vertex child : v.getChildren()) { + log.debug(" " + child.getLabel()); + } + } + } + } + + // result sorted list of class definitions + Set results = new LinkedHashSet<>(); + log.debug("Ordered classes:"); + // the graph contains many references, only include those which are class defs + for (String className : TopologicalSorter.sort(graph)) { + ClassDef def = classNames.get(className); + // skip duplicates (due to alt/aliases) + if (def != null && !results.contains(def)) { + log.debug(" " + def.getName()); + results.add(def); + } + } + + log.debug("Found " + results.size() + " classes"); + return new ArrayList<>(results); + } + + /** + * Resolve all references. + */ + private void resolve(final ClassDef def) { + // resolve mvc references if namespace is set + if (namespace != null) { + if (def.getMvcControllers() != null) { + for (String name : def.getMvcControllers()) { + appendMvcDependency(def, "controller", name); + } + } + if (def.getMvcModels() != null) { + for (String name : def.getMvcModels()) { + appendMvcDependency(def, "model", name); + } + } + if (def.getMvcStores() != null) { + for (String name : def.getMvcStores()) { + appendMvcDependency(def, "store", name); + } + } + if (def.getMvcViews() != null) { + for (String name : def.getMvcViews()) { + appendMvcDependency(def, "view", name); + } + } + } + } + + private void appendMvcDependency(final ClassDef def, final String type, final String name) { + if (classes.containsKey(name)) { + // already qualified name + def.getDependencies().add(name); + } + else { + if (name.contains("@")) { + // namespace is embedded: @ + String[] parts = name.split("@", 2); + def.getDependencies().add(String.format("%s.%s", parts[1], parts[0])); + } + else { + def.getDependencies().add(String.format("%s.%s.%s", namespace, type, name)); + } + } + } + + /** + * Scan the given file for class definitions and accumulate dependencies. + */ + private void scan(final File source) throws IOException { + log.debug("Scanning: " + source); + + ErrorReporter errorReporter = new LogErrorReporter(log); + + CompilerEnvirons env = new CompilerEnvirons(); + env.setErrorReporter(errorReporter); + + Parser parser = new Parser(env, errorReporter); + Reader reader = new BufferedReader(new FileReader(source)); + try { + AstRoot root = parser.parse(reader, source.getAbsolutePath(), 0); + DependencyAccumulator visitor = new DependencyAccumulator(source); + root.visit(visitor); + + // complain if no def was found in this source + if (visitor.current == null) { + log.warn("No class definition was found while processing: " + source); + } + } + finally { + reader.close(); + } + } + + private class DependencyAccumulator + implements NodeVisitor + { + private final File source; + + private ClassDef current; + + private DependencyAccumulator(final File source) { + this.source = source; + } + + /** + * Helper to report error for given node position in ast tree. + */ + private RuntimeException reportError(final AstNode node, final String message, Object... params) { + throw Context.reportRuntimeError(String.format(message, params), + source.getAbsolutePath(), + node.getLineno(), // seems to always be the line before? + node.debugPrint(), + 0 // line-offset is unknown? + ); + } + + /** + * Helper to check state and report error for given node position in ast tree. + */ + private void checkState(boolean expression, final AstNode node, final String message, Object... params) { + if (!expression) { + throw reportError(node, message, params); + } + } + + @Override + public boolean visit(final AstNode node) { + int type = node.getType(); + + switch (type) { + case CALL: { + FunctionCall call = (FunctionCall) node; + String name = nameOf(call.getTarget()); + + // if we can not determine the function name, skip + if (name == null) { + break; + } + + if (name.equals("Ext.define")) { + // complain if we found more than one class + if (current != null) { + log.warn("Found duplicate class definition in source: " + source); + } + processClassDef(call); + return true; // process children + } + else if (name.equals("Ext.create")) { + // complain if we have references to classes with Ext.create() and missing requires + if (!call.getArguments().isEmpty() && call.getArguments().get(0) instanceof StringLiteral) { + String className = nameOf(call.getArguments().get(0)); + maybeWarnMissingRequires(className, "Ext.create"); + } + return false; // ignore children + } + break; + } + + case NEW: { + // complain if we have references to classes with 'new' and missing requires + NewExpression expr = (NewExpression) node; + String className = nameOf(expr.getTarget()); + maybeWarnMissingRequires(className, "new"); + break; + } + } + + return true; // process children + } + + /** + * Find the textual name of the given node. + */ + @Nullable + private String nameOf(final AstNode node) { + if (node instanceof Name) { + return ((Name) node).getIdentifier(); + } + else if (node instanceof PropertyGet) { + PropertyGet prop = (PropertyGet) node; + return String.format("%s.%s", nameOf(prop.getTarget()), nameOf(prop.getProperty())); + } + else if (node instanceof StringLiteral) { + return ((StringLiteral) node).getValue(); + } + return null; + } + + /** + * Return string literal value. + */ + private String stringLiteral(final AstNode node) { + checkState(node instanceof StringLiteral, node, "Expected string literal only"); + //noinspection ConstantConditions + StringLiteral string = (StringLiteral) node; + return string.getValue(); + } + + /** + * Return number literal value. + */ + private double numberLiteral(final AstNode node) { + checkState(node instanceof NumberLiteral, node, "Expected number literal only"); + //noinspection ConstantConditions + NumberLiteral number = (NumberLiteral) node; + return number.getNumber(); + } + + /** + * Returns string array literal values. + */ + private List arrayStringLiteral(final AstNode node) { + checkState(node instanceof ArrayLiteral, node, "Expected array literal only"); + List result = new ArrayList<>(); + //noinspection ConstantConditions + ArrayLiteral array = (ArrayLiteral) node; + for (AstNode element : array.getElements()) { + result.add(stringLiteral(element)); + } + return result; + } + + /** + * Returns string literal or array of string literals. + * + * @see #stringLiteral(AstNode) + * @see #arrayStringLiteral(AstNode) + */ + private List stringLiterals(final AstNode node) { + // string literal or array of string literals + if (node instanceof StringLiteral) { + return Collections.singletonList(stringLiteral(node)); + } + else if (node instanceof ArrayLiteral) { + return arrayStringLiteral(node); + } + else { + throw reportError(node, "Expected string literal or array of string literal only"); + } + } + + /** + * Maybe warn if a needed class has not been required. + */ + private void maybeWarnMissingRequires(final String className, final String usage) { + if (warnings) { + if (!current.getDependencies().contains(className)) { + log.warn(String.format("Class '%s' missing requires for '%s' usage of: %s", + current.getName(), usage, className)); + } + } + } + + /** + * Process an {@code Ext.define} class definition. + */ + private void processClassDef(final FunctionCall node) { + List args = node.getArguments(); + checkState(args.size() >= 2, node, + "Invalid number of arguments for Ext.define"); // simple def or def with callback + + // class-name + checkState(args.get(0) instanceof StringLiteral, node, "Ext.define arg[0] must be string"); + String className = nameOf(args.get(0)); + + // try and avoid file-name/class-name mismatches early + sanityCheckClassName(className); + + current = new ClassDef(className, source); + classes.put(className, current); + + // class def object + checkState(args.get(1) instanceof ObjectLiteral, node, "Ext.define arg[1] must be object"); + ObjectLiteral obj = (ObjectLiteral) args.get(1); + for (ObjectProperty prop : obj.getElements()) { + String name = nameOf(prop.getLeft()); + switch (name) { + + // ExtJS core class def + + case "extend": + // string literal only + current.setExtend(stringLiteral(prop.getRight())); + break; + + case "override": + // string literal only + current.setOverride(stringLiteral(prop.getRight())); + break; + + case "@aggregate_priority": + // number only + current.setPriority(numberLiteral(prop.getRight())); + break; + + case "requires": + // array of string literals only + current.setRequires(arrayStringLiteral(prop.getRight())); + break; + + case "require": + // complain if we found 'require' this almost certainly should be 'requires' + log.warn( + String.format("Found 'require' and probably should be 'requires' in: %s#%s", source, prop.getLineno())); + break; + + case "uses": + // array of string literals only + current.setUses(arrayStringLiteral(prop.getRight())); + break; + + case "alternateClassName": { + // string literal or array of string literals + current.getAlternateClassName().addAll(stringLiterals(prop.getRight())); + break; + } + + case "alias": { + // string literal or array of string literals + current.getAlias().addAll(stringLiterals(prop.getRight())); + break; + } + + case "xtype": { + // string literal only + current.getAlias().add("widget." + stringLiteral(prop.getRight())); + break; + } + + case "mixins": { + // array of strings, or object + List mixins = new ArrayList<>(); + if (prop.getRight() instanceof ArrayLiteral) { + mixins.addAll(arrayStringLiteral(prop.getRight())); + } + else if (prop.getRight() instanceof ObjectLiteral) { + ObjectLiteral child = (ObjectLiteral) prop.getRight(); + for (ObjectProperty element : child.getElements()) { + mixins.add(stringLiteral(element.getRight())); + } + } + else { + throw reportError(prop.getRight(), "Expected array or object literal only"); + } + current.setMixins(mixins); + break; + } + } + + // Additional stuff we have to detect for ExtJS MVC support + + if (isExtends("Ext.app.Application")) { + // class looks like an application, process more fields + switch (name) { + case "controllers": + // array of string literals only + current.setMvcControllers(arrayStringLiteral(prop.getRight())); + break; + + case "models": + // array of string literals only + current.setMvcModels(arrayStringLiteral(prop.getRight())); + break; + + case "stores": + // array of string literals only + current.setMvcStores(arrayStringLiteral(prop.getRight())); + break; + + case "views": + // array of string literals only + current.setMvcViews(arrayStringLiteral(prop.getRight())); + break; + } + } + else if (isMvcClass("controller")) { + // class looks like a controller, process more fields + switch (name) { + case "models": + // array of string literals only + current.setMvcModels(arrayStringLiteral(prop.getRight())); + break; + + case "stores": + // array of string literals only + current.setMvcStores(arrayStringLiteral(prop.getRight())); + break; + + case "views": + // array of string literals only + current.setMvcViews(arrayStringLiteral(prop.getRight())); + break; + } + } + else if (isMvcClass("store")) { + // class looks like a store, process more fields + switch (name) { + case "model": + // string literal only + current.getDependencies().add(stringLiteral(prop.getRight())); + break; + } + } + } + } + + /** + * Complain if classes are defined with wrong names, or in wrong files. + */ + private void sanityCheckClassName(final String className) { + String found = source.toURI().getPath(); + String expected = className.replace('.', '/') + ".js"; + if (!found.endsWith(expected)) { + log.warn(String.format("Expected class '%s' to be defined in filename '%s', but was defined in: %s", + className, expected, found)); + } + } + + /** + * Highly hackish means to determine given class-name is a MVC type. + * Requires namespace to be configured. + */ + private boolean isMvcClass(final String type) { + return namespace != null && current.getName().startsWith(String.format("%s.%s.", namespace, type)); + } + + /** + * Check if current class extends (directly) given class-name. + */ + private boolean isExtends(final String className) { + String superClass = current.getExtend(); + return superClass != null && superClass.equals(className); + } + } +} diff --git a/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/FileAppender.java b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/FileAppender.java new file mode 100644 index 0000000000000000000000000000000000000000..49dc6f77acffbcce282c6fdf789609c1815fcf32 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/FileAppender.java @@ -0,0 +1,70 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Writer; + +import org.apache.maven.plugin.logging.Log; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * File appender. + * + * @since 3.0 + */ +public class FileAppender +{ + protected final Log log; + + protected final Writer output; + + public FileAppender(final Log log, final Writer output) { + this.log = checkNotNull(log); + this.output = checkNotNull(output); + } + + protected File source; + + protected long linenum; + + public void append(final File source) throws IOException { + this.source = checkNotNull(source); + this.linenum = 0; + + log.debug("Appending: " + source); + + BufferedReader input = new BufferedReader(new FileReader(source)); + try { + String line; + while ((line = input.readLine()) != null) { + linenum++; + append(line); + } + } + finally { + input.close(); + } + + output.write('\n'); + } + + protected void append(final String line) throws IOException { + output.write(line); + output.write('\n'); + } +} diff --git a/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/LogErrorReporter.java b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/LogErrorReporter.java new file mode 100644 index 0000000000000000000000000000000000000000..d94fdc620166710d81c3eb43bd40a5564eb1e030 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/LogErrorReporter.java @@ -0,0 +1,53 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import org.apache.maven.plugin.logging.Log; +import org.mozilla.javascript.ErrorReporter; +import org.mozilla.javascript.EvaluatorException; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Mojo {@link Log} adapting {@link ErrorReporter}. + * + * @since 3.0 + */ +public class LogErrorReporter + implements ErrorReporter +{ + private final Log log; + + public LogErrorReporter(final Log log) { + this.log = checkNotNull(log); + } + + private String format(final String message, final String sourceName, final int line, final String lineSource, final int lineOffset) { + return String.format("%s (%s#%d:%d): %s", message, sourceName, line, lineOffset, lineSource); + } + + @Override + public void warning(final String message, final String sourceName, final int line, final String lineSource, final int lineOffset) { + log.warn(format(message, sourceName, line, lineSource, lineOffset)); + } + + @Override + public void error(final String message, final String sourceName, final int line, final String lineSource, final int lineOffset){ + log.error(format(message, sourceName, line, lineSource, lineOffset)); + } + + @Override + public EvaluatorException runtimeError(final String message, final String sourceName, final int line, final String lineSource, final int lineOffset) { + return new EvaluatorException(message, sourceName, line, lineSource, lineOffset); + } +} diff --git a/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/OmissionFileAppender.java b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/OmissionFileAppender.java new file mode 100644 index 0000000000000000000000000000000000000000..74421700801aa80bd8c8403de7fb57042380d970 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/main/java/org/sonatype/nexus/extjs/OmissionFileAppender.java @@ -0,0 +1,113 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.annotation.Nullable; + +import org.apache.maven.plugin.logging.Log; + +/** + * Omission token aware file appender. + * + * @since 3.0 + */ +public class OmissionFileAppender + extends FileAppender +{ + public static final String IF_START_PREFIX = "// flags = new HashMap<>(); + + public OmissionFileAppender(final Log log, + final Writer output, + final @Nullable Map rawFlags) + { + super(log, output); + if (rawFlags != null) { + parseFlags(rawFlags); + } + } + + public Map getFlags() { + return flags; + } + + private void parseFlags(final Map rawFlags) { + for (Entry entry : rawFlags.entrySet()) { + boolean flag = Boolean.parseBoolean(entry.getValue()); + flags.put(entry.getKey(), flag); + } + log.debug("Omit flags: " + flags); + } + + private boolean omit; + + @Override + public void append(final File source) throws IOException { + this.omit = false; + + super.append(source); + + // complain if still in omit mode at the end of the file + if (omit) { + log.warn("Missing omit end token: " + source); + } + } + + @Override + protected void append(final String line) throws IOException { + // check for omission start and stop tokens + String trimmedLine = line.trim(); + if (isOmitStart(trimmedLine)) { + // complain if already in omit mode and we found a start token (nesting is not allowed) + if (omit) { + log.warn("Unexpected omit start token: " + source + "#" + linenum); + } + omit = true; + } + else if (isOmitStop(trimmedLine)) { + omit = false; + } + else if (omit) { + output.write("//"); + } + + super.append(line); + } + + private boolean isOmitStart(final String line) { + // assumes line is trimmed + if (line.startsWith(IF_START_PREFIX) && line.endsWith(IF_START_SUFFIX)) { + String flagName = line.substring(IF_START_PREFIX.length(), line.length() - IF_START_SUFFIX.length()); + Boolean flag = flags.get(flagName); + return flag != null && flag; + } + return false; + } + + private boolean isOmitStop(final String line) { + // assumes line is trimmed + return line.equals(IF_END); + } +} diff --git a/buildsupport/extjs-maven-plugin/src/test/java/org/sonatype/nexus/extjs/ClassDefScannerTest.java b/buildsupport/extjs-maven-plugin/src/test/java/org/sonatype/nexus/extjs/ClassDefScannerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..25574171e53313a2c67c23d8e9484bbd62ff4c7b --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/java/org/sonatype/nexus/extjs/ClassDefScannerTest.java @@ -0,0 +1,145 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.extjs; + +import java.io.File; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.logging.SystemStreamLog; +import org.codehaus.plexus.util.DirectoryScanner; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +/** + * Tests for {@link ClassDefScanner}. + */ +public class ClassDefScannerTest + extends TestSupport +{ + private ClassDefScanner scanner; + + @Before + public void setUp() throws Exception { + Log log = new SystemStreamLog() + { + @Override + public boolean isDebugEnabled() { + return true; + } + }; + this.scanner = new ClassDefScanner(log); + } + + private List scan(final File basedir) throws Exception { + DirectoryScanner files = new DirectoryScanner(); + files.setBasedir(basedir); + files.setIncludes(new String[]{"**/*.js"}); + + List classes = scanner.scan(files); + log("Classes:"); + for (ClassDef def : classes) { + log(" {} [priority: {}]", def.getName(), def.getPriority()); + for (String dep : def.getDependencies()) { + log(" + {}", dep); + } + } + return classes; + } + + @Test + public void basicOrder() throws Exception { + File basedir = util.resolveFile("src/test/resources/basic"); + List classes = scan(basedir); + + assertThat(classes, hasSize(3)); + + ClassDef baz = classes.get(0); + assertThat(baz.getName(), is("Baz")); + + ClassDef bar = classes.get(1); + assertThat(bar.getName(), is("Bar")); + assertThat(bar.getDependencies(), contains("Baz")); + + ClassDef foo = classes.get(2); + assertThat(foo.getName(), is("Foo")); + assertThat(foo.getDependencies(), contains("Bar")); + } + + @Test + public void priorityOrder() throws Exception { + File basedir = util.resolveFile("src/test/resources/priority"); + List classes = scan(basedir); + + assertThat(classes, hasSize(5)); + + ClassDef poo = classes.get(0); + assertThat(poo.getName(), is("Poo")); + + ClassDef ick = classes.get(1); + assertThat(ick.getName(), is("Ick")); + assertThat(ick.getDependencies(), contains("Poo")); + + ClassDef baz = classes.get(2); + assertThat(baz.getName(), is("Baz")); + + ClassDef bar = classes.get(3); + assertThat(bar.getName(), is("Bar")); + assertThat(bar.getDependencies(), contains("Baz")); + + ClassDef foo = classes.get(4); + assertThat(foo.getName(), is("Foo")); + assertThat(foo.getDependencies(), contains("Bar")); + } + + @Test + public void altClassNameOrder() throws Exception { + File basedir = util.resolveFile("src/test/resources/altclassname"); + List classes = scan(basedir); + + assertThat(classes, hasSize(3)); + + ClassDef baz = classes.get(0); + assertThat(baz.getName(), is("Baz")); + + ClassDef bar = classes.get(1); + assertThat(bar.getName(), is("Bar")); + assertThat(bar.getAlternateClassName(), containsInAnyOrder("Test.Bar", "Other.Bar")); + assertThat(bar.getDependencies(), contains("Baz")); + + ClassDef foo = classes.get(2); + assertThat(foo.getName(), is("Foo")); + assertThat(foo.getAlternateClassName(), containsInAnyOrder("Test.Foo")); + assertThat(foo.getDependencies(), contains("Test.Bar")); + } + + @Test + public void mvcOrder() throws Exception { + File basedir = util.resolveFile("src/test/resources/mvc"); + scanner.setNamespace("Test"); + List classes = scan(basedir); + + assertThat(classes, hasSize(5)); + + assertThat(classes.get(4).getName(), is("Test.Application")); + assertThat(classes.get(4).getDependencies(), containsInAnyOrder("Ext.app.Application", "Test.controller.Fun")); + } +} diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Bar.js b/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Bar.js new file mode 100644 index 0000000000000000000000000000000000000000..c8ce309d5fd7533be011e99c9848251ee6d2f151 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Bar.js @@ -0,0 +1,9 @@ +Ext.define('Bar', { + alternateClassName: [ + 'Test.Bar', + 'Other.Bar' + ], + requires: [ + 'Baz' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Baz.js b/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Baz.js new file mode 100644 index 0000000000000000000000000000000000000000..b1f7b6ae1e222646991132f9cf6a8ec63c9962f7 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Baz.js @@ -0,0 +1 @@ +Ext.define('Baz', {}); \ No newline at end of file diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Foo.js b/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Foo.js new file mode 100644 index 0000000000000000000000000000000000000000..efaa030112cc017bc47076754562a17471d7af0e --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/altclassname/Foo.js @@ -0,0 +1,6 @@ +Ext.define('Foo', { + alternateClassName: 'Test.Foo', + requires: [ + 'Test.Bar' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/basic/Bar.js b/buildsupport/extjs-maven-plugin/src/test/resources/basic/Bar.js new file mode 100644 index 0000000000000000000000000000000000000000..985ef1fee81b116b59e25acf78c3702d863bf011 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/basic/Bar.js @@ -0,0 +1,5 @@ +Ext.define('Bar', { + requires: [ + 'Baz' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/basic/Baz.js b/buildsupport/extjs-maven-plugin/src/test/resources/basic/Baz.js new file mode 100644 index 0000000000000000000000000000000000000000..b1f7b6ae1e222646991132f9cf6a8ec63c9962f7 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/basic/Baz.js @@ -0,0 +1 @@ +Ext.define('Baz', {}); \ No newline at end of file diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/basic/Foo.js b/buildsupport/extjs-maven-plugin/src/test/resources/basic/Foo.js new file mode 100644 index 0000000000000000000000000000000000000000..562d52fcf420709bd48583ee5d5eced143ffd10a --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/basic/Foo.js @@ -0,0 +1,5 @@ +Ext.define('Foo', { + requires: [ + 'Bar' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/mvc/Application.js b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/Application.js new file mode 100644 index 0000000000000000000000000000000000000000..1e3aaf41d07a06425c653acb92706382ede9518d --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/Application.js @@ -0,0 +1,7 @@ +Ext.define('Test.Application', { + extend: 'Ext.app.Application', + + controllers: [ + 'Fun' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/mvc/controller/Fun.js b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/controller/Fun.js new file mode 100644 index 0000000000000000000000000000000000000000..715fe24ffadfa95967a54293d7f853dc5d447dad --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/controller/Fun.js @@ -0,0 +1,10 @@ +Ext.define('Test.controller.Fun', { + extend: 'Ext.app.Controller', + + stores: [ + 'Fun' + ], + views: [ + 'Fun' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/mvc/model/Fun.js b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/model/Fun.js new file mode 100644 index 0000000000000000000000000000000000000000..27286dae349a4f266608619ed2e16abd6d3b60ab --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/model/Fun.js @@ -0,0 +1,3 @@ +Ext.define('Test.model.Fun', { + extend: 'Ext.data.Model' +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/mvc/store/Fun.js b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/store/Fun.js new file mode 100644 index 0000000000000000000000000000000000000000..ce394d17b905ba24194a7eff8530fa7af9a91948 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/store/Fun.js @@ -0,0 +1,4 @@ +Ext.define('Test.store.Fun', { + extend: 'Ext.data.Store', + model: 'Test.model.Fun' +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/mvc/view/Fun.js b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/view/Fun.js new file mode 100644 index 0000000000000000000000000000000000000000..be94030587a25472b1483d8f503e4d8bda9dfbce --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/mvc/view/Fun.js @@ -0,0 +1,3 @@ +Ext.define('Test.view.Fun', { + extend: 'Ext.panel.Panel' +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/priority/Bar.js b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Bar.js new file mode 100644 index 0000000000000000000000000000000000000000..985ef1fee81b116b59e25acf78c3702d863bf011 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Bar.js @@ -0,0 +1,5 @@ +Ext.define('Bar', { + requires: [ + 'Baz' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/priority/Baz.js b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Baz.js new file mode 100644 index 0000000000000000000000000000000000000000..b1f7b6ae1e222646991132f9cf6a8ec63c9962f7 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Baz.js @@ -0,0 +1 @@ +Ext.define('Baz', {}); \ No newline at end of file diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/priority/Foo.js b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Foo.js new file mode 100644 index 0000000000000000000000000000000000000000..562d52fcf420709bd48583ee5d5eced143ffd10a --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Foo.js @@ -0,0 +1,5 @@ +Ext.define('Foo', { + requires: [ + 'Bar' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/priority/Ick.js b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Ick.js new file mode 100644 index 0000000000000000000000000000000000000000..6d27a8c6e49f8520b21e87a0a57c8bbb9c8e3567 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Ick.js @@ -0,0 +1,6 @@ +Ext.define('Ick', { + '@aggregate_priority': 10, + requires: [ + 'Poo' + ] +}); diff --git a/buildsupport/extjs-maven-plugin/src/test/resources/priority/Poo.js b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Poo.js new file mode 100644 index 0000000000000000000000000000000000000000..b8b027779f0d46e1833a68f99d19336e7b562d76 --- /dev/null +++ b/buildsupport/extjs-maven-plugin/src/test/resources/priority/Poo.js @@ -0,0 +1,3 @@ +Ext.define('Poo', { + '@aggregate_priority': 9 +}); diff --git a/buildsupport/goodies/pom.xml b/buildsupport/goodies/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..944e7409afe7d597d7e9f193198c21e82b119d2d --- /dev/null +++ b/buildsupport/goodies/pom.xml @@ -0,0 +1,70 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-goodies + ${project.groupId}:${project.artifactId} + pom + + + 2.0.0-SNAPSHOT + + + + + + + org.sonatype.goodies + goodies-common + ${goodies.version} + + + + org.sonatype.goodies + goodies-i18n + ${goodies.version} + + + + org.sonatype.goodies + goodies-lifecycle + ${goodies.version} + + + + org.sonatype.goodies + goodies-prefs + ${goodies.version} + + + + org.sonatype.goodies + goodies-testsupport + ${goodies.version} + + + + + + diff --git a/buildsupport/groovy/pom.xml b/buildsupport/groovy/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0d7f6bf82360be2fa3018621d75dedbe96d002b3 --- /dev/null +++ b/buildsupport/groovy/pom.xml @@ -0,0 +1,84 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-groovy + ${project.groupId}:${project.artifactId} + pom + + + 2.4.4 + 1.9.6 + 1.0-groovy-2.4 + + + + + + + + + org.codehaus.groovy + groovy-all + ${groovy.version} + + + + org.codehaus.groovy + groovy + ${groovy.version} + + + + org.apache.ant + ant + ${ant.version} + + + + org.apache.ant + ant-launcher + ${ant.version} + + + + org.spockframework + spock-core + ${spock.version} + + + + org.spockframework + spock-guice + ${spock.version} + + + + + + diff --git a/buildsupport/guice/pom.xml b/buildsupport/guice/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..f2bbd3675cfbe6bb38e61a7a3a60e8e53605479d --- /dev/null +++ b/buildsupport/guice/pom.xml @@ -0,0 +1,109 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-guice + ${project.groupId}:${project.artifactId} + pom + + + 0.3.2 + 4.0 + + + + + + javax.annotation + javax.annotation-api + 1.2 + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.javax-inject + 1_2 + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.aopalliance + 1.0_6 + + + + javax.enterprise + cdi-api + 1.2 + + + javax.inject + javax.inject + + + + + + com.google.inject + guice + ${guice.version} + + + javax.inject + javax.inject + + + aopalliance + aopalliance + + + + + + com.google.inject.extensions + guice-servlet + ${guice.version} + + + + com.google.inject.extensions + guice-multibindings + ${guice.version} + + + + com.google.inject.extensions + guice-assistedinject + ${guice.version} + + + + org.eclipse.sisu + org.eclipse.sisu.inject + ${eclipse-sisu.version} + + + + + diff --git a/buildsupport/httpclient/pom.xml b/buildsupport/httpclient/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..c1242b681c35ddf19da57bb135bdfa7e10d87385 --- /dev/null +++ b/buildsupport/httpclient/pom.xml @@ -0,0 +1,60 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-httpclient + ${project.groupId}:${project.artifactId} + pom + + + + + + org.apache.httpcomponents + httpclient + 4.5.1 + + + commons-logging + commons-logging + + + + + + org.apache.httpcomponents + httpmime + 4.5.1 + + + + org.apache.httpcomponents + httpcore + 4.4.3 + + + + + + diff --git a/buildsupport/internal/pom.xml b/buildsupport/internal/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..d26d378f6593aecc8d1ce617a438be64ec889ca3 --- /dev/null +++ b/buildsupport/internal/pom.xml @@ -0,0 +1,174 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-internal + ${project.groupId}:${project.artifactId} + pom + + + 1.4.3 + 1.5.0 + 1.12.0-04 + 2.2.0 + + + + + + + + + com.sonatype.licensing + license-bundle + ${sonatype-licensing.version} + + + aopalliance + aopalliance + + + org.sonatype.sisu + sisu-guice + + + xmlpull + xmlpull + + + xpp3 + xpp3_min + + + + + + com.sonatype.licensing + license-creator + ${sonatype-licensing.version} + + + + com.sonatype.licensing + license-extension + ${sonatype-licensing.version} + + + + + + com.sonatype.clm + com.sonatype.clm.dto.model + ${clm.dto.model.version} + + + + com.sonatype.insight.brain + sonatype-clm-scanner + 1.11.1-01 + + + + com.sonatype.insight.scan + insight-scanner-core + ${insight-scanner.version} + + + + com.sonatype.insight.scan + insight-scanner-model-io + ${insight-scanner.version} + + + + com.sonatype.insight.scan + insight-mock-server + ${insight-scanner.version} + + + + com.sonatype.insight.brain + insight-rm-common + ${insight-brain.version} + + + + com.sonatype.insight.brain + insight-brain-service + ${insight-brain.version} + + + + junit + junit + + + javax.xml.stream + stax-api + + + com.sonatype.licensing + license-bundle + + + org.sonatype.sisu + sisu-inject-bean + + + + + + com.sonatype.insight.brain + insight-brain-service + ${insight-brain.version} + tests + + + com.sonatype.licensing + license-bundle + + + + + + + + com.sonatype.analytics + analytics-client + 1.1.0 + + + + com.sonatype.analytics + analytics-assembly + bundle + zip + 1.1.0 + + + + + diff --git a/buildsupport/jetty/pom.xml b/buildsupport/jetty/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..2f946e3920c6914834b72ee40f576f9240473f52 --- /dev/null +++ b/buildsupport/jetty/pom.xml @@ -0,0 +1,147 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-jetty + ${project.groupId}:${project.artifactId} + pom + + + 9.3.3.v20150827 + + + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + + org.eclipse.jetty + jetty-webapp + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-continuation + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-servlet + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-servlet + tests + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-security + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-server + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-deploy + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-client + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-util + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-util-ajax + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-servlets + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-proxy + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-http + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-io + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-xml + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-jmx + ${eclipse-jetty.version} + + + + org.eclipse.jetty + jetty-rewrite + ${eclipse-jetty.version} + + + + + diff --git a/buildsupport/logging/pom.xml b/buildsupport/logging/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..74d5160753756733f487ff5e3ac60ecd9be160c9 --- /dev/null +++ b/buildsupport/logging/pom.xml @@ -0,0 +1,122 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-logging + ${project.groupId}:${project.artifactId} + pom + + + 1.7.12 + 1.1.3 + 1.8.3 + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.slf4j + slf4j-simple + ${slf4j.version} + + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + + + + org.slf4j + jul-to-slf4j + ${slf4j.version} + + + + ch.qos.logback + logback-core + ${logback.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + ch.qos.logback + logback-access + ${logback.version} + + + + org.ops4j.pax.logging + pax-logging-api + ${pax-logging.version} + + + + org.ops4j.pax.logging + pax-logging-logback + ${pax-logging.version} + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + ch.qos.logback.contrib + logback-jackson + + + ch.qos.logback.contrib + logback-json-core + + + ch.qos.logback.contrib + logback-json-classic + + + + + + + diff --git a/buildsupport/maven/pom.xml b/buildsupport/maven/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..9a83babc100dfeb5f0040259ddef308b4f4e0aa1 --- /dev/null +++ b/buildsupport/maven/pom.xml @@ -0,0 +1,140 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-maven + ${project.groupId}:${project.artifactId} + pom + + + 3.3.3 + 1.0.2.v20150114 + + + + + + org.apache.maven + maven-aether-provider + ${apache-maven.version} + + + + org.apache.maven + maven-model + ${apache-maven.version} + + + + org.apache.maven + maven-repository-metadata + ${apache-maven.version} + + + + org.apache.maven + maven-artifact + ${apache-maven.version} + + + + org.apache.maven + maven-settings + ${apache-maven.version} + + + + org.apache.maven + maven-settings-builder + ${apache-maven.version} + + + + org.apache.maven + maven-core + ${apache-maven.version} + + + + org.apache.maven + maven-plugin-api + ${apache-maven.version} + + + + org.apache.maven + maven-compat + ${apache-maven.version} + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.4 + + + + org.apache.maven.shared + maven-verifier + 1.6 + + + junit + junit-dep + + + junit + junit + + + + + + org.eclipse.aether + aether-api + ${aether.version} + + + + org.eclipse.aether + aether-util + ${aether.version} + + + + org.eclipse.aether + aether-spi + ${aether.version} + + + + org.eclipse.aether + aether-impl + ${aether.version} + + + + + + diff --git a/buildsupport/metrics/pom.xml b/buildsupport/metrics/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..9434cd6377639641ab3b87ac964102a52d735af0 --- /dev/null +++ b/buildsupport/metrics/pom.xml @@ -0,0 +1,92 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-metrics + ${project.groupId}:${project.artifactId} + pom + + + 3.0.2 + + + + + + com.codahale.metrics + metrics-core + ${metrics.version} + + + + com.codahale.metrics + metrics-annotation + ${metrics.version} + + + + com.palominolabs.metrics + metrics-guice + ${metrics.version} + + + + com.codahale.metrics + metrics-servlets + ${metrics.version} + + + + com.codahale.metrics + metrics-jetty9 + ${metrics.version} + + + org.eclipse.jetty + jetty-server + + + + + + com.codahale.metrics + metrics-httpclient + ${metrics.version} + + + + com.codahale.metrics + metrics-logback + ${metrics.version} + + + + com.codahale.metrics + metrics-servlet + ${metrics.version} + + + + + diff --git a/buildsupport/osgi/pom.xml b/buildsupport/osgi/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..27cc921585f312ff373ea8573a1c0e6680b7f31b --- /dev/null +++ b/buildsupport/osgi/pom.xml @@ -0,0 +1,116 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-osgi + ${project.groupId}:${project.artifactId} + pom + + + 4.3.1 + 4.4.0 + 3.0.4 + + + + + + org.osgi + org.osgi.core + ${osgi-spec.version} + + + + org.osgi + org.osgi.compendium + ${osgi-spec.version} + + + + org.apache.felix + org.apache.felix.framework + ${felix.version} + + + + org.apache.karaf + org.apache.karaf.main + ${karaf.version} + + + + org.apache.karaf.bundle + org.apache.karaf.bundle.core + ${karaf.version} + + + + org.apache.karaf.features + framework + ${karaf.version} + kar + + + org.eclipse + org.eclipse.osgi + + + + + + org.apache.karaf.features + standard + ${karaf.version} + features + xml + + + + org.apache.karaf.features + org.apache.karaf.features.core + ${karaf.version} + + + + org.apache.karaf.shell + org.apache.karaf.shell.console + ${karaf.version} + + + + org.apache.karaf.jaas + org.apache.karaf.jaas.modules + + + + + + org.apache.karaf.shell + org.apache.karaf.shell.table + ${karaf.version} + + + + + diff --git a/buildsupport/other/pom.xml b/buildsupport/other/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..38d929ce973883fe2fb8992a2b74596413409317 --- /dev/null +++ b/buildsupport/other/pom.xml @@ -0,0 +1,240 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-other + ${project.groupId}:${project.artifactId} + pom + + + + + com.google.guava + guava + 18.0 + + + + com.google.code.findbugs + jsr305 + 3.0.0 + + + + javax.mail + mail + 1.4.7 + + + + org.apache.velocity + velocity + 1.7 + + + + joda-time + joda-time + 2.8.2 + + + + org.apache.tika + tika-core + 1.10 + + + + org.codehaus.mojo + animal-sniffer-annotations + 1.13 + + + + bitwalker + UserAgentUtils + 1.12 + + + + javax.validation + validation-api + 1.1.0.Final + + + + javax.el + javax.el-api + 2.2.5 + + + + org.glassfish.web + javax.el + 2.2.6 + + + + org.hibernate + hibernate-validator + 5.1.2.Final + + + + org.quartz-scheduler + quartz + 2.2.1 + + + + org.sonatype.sisu + sisu-odata4j + 0.0.7 + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.elasticsearch + 1.7.1_1 + + + + + com.vividsolutions + jts + 1.13 + + + + javax.interceptor + javax.interceptor-api + 1.2 + + + + javax.transaction + javax.transaction-api + 1.2 + + + + com.thoughtworks.paranamer + paranamer + 2.8 + + + + org.codehaus.plexus + plexus-interpolation + 1.22 + + + + org.codehaus.plexus + plexus-utils + 3.0.22 + + + + + + + + + + + org.apache.geronimo.specs + geronimo-jcache_1.0_spec + 1.0-alpha-1 + + + + net.sf.ehcache + ehcache + 2.10.0 + + + + net.sf.ehcache + ehcache + javadoc + 2.10.0 + + + + org.ehcache + jcache + 1.0.1 + + + net.sf.ehcache + ehcache + + + javax.cache + cache-api + + + + + + org.freemarker + freemarker + 2.3.23 + + + + org.mozilla + rhino + 1.7.7 + + + + com.atlassian.crowd.client + atlassian-crowd-rest-client + 1.1 + + + + commons-httpclient + commons-httpclient + + + + commons-lang + commons-lang + + + commons-codec + commons-codec + + + + + + + + diff --git a/buildsupport/pom.xml b/buildsupport/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..98c3fc5f60c4a26631f994bdeb2b99bb8e3a4a9d --- /dev/null +++ b/buildsupport/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-parent + 3.0.0-SNAPSHOT + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + ${project.groupId}:${project.artifactId} + pom + + + commons + db + goodies + groovy + guice + httpclient + internal + jetty + logging + maven + metrics + osgi + other + rest + security + testing + ui + + + all + + extjs-maven-plugin + + + + + include-scripts + + scripts + + + + + diff --git a/buildsupport/rest/pom.xml b/buildsupport/rest/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..f4d8f01e1b9bcc3dda3818d7eaa55d2a47fdcb1b --- /dev/null +++ b/buildsupport/rest/pom.xml @@ -0,0 +1,190 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-rest + ${project.groupId}:${project.artifactId} + pom + + + 2.5.4 + 3.0.12.Final + + + + + + + javax.ws.rs + javax.ws.rs-api + 2.0 + + + + org.jsoup + jsoup + 1.8.3 + + + + + com.thoughtworks.xstream + xstream + 1.4.8 + + + xmlpull + xmlpull + + + xpp3 + xpp3_min + + + + + + org.apache.servicemix.bundles + org.apache.servicemix.bundles.xpp3 + 1.1.4c_7 + + + + com.sun.xml.fastinfoset + FastInfoset + 1.2.13 + + + + + + org.jboss.resteasy + resteasy-jaxrs + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-jaxb-provider + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-multipart-provider + ${resteasy.version} + + + commons-logging + commons-logging + + + + + + org.jboss.resteasy + resteasy-atom-provider + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-client + ${resteasy.version} + + + + + org.jboss.resteasy + resteasy-jackson2-provider + ${resteasy.version} + + + + org.jboss.resteasy + resteasy-validator-provider-11 + ${resteasy.version} + + + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson2.version} + + + + com.fasterxml.jackson.core + jackson-core + ${jackson2.version} + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson2.version} + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${jackson2.version} + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-base + ${jackson2.version} + + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + ${jackson2.version} + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-smile + ${jackson2.version} + + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + ${jackson2.version} + + + + + diff --git a/buildsupport/scripts/fixcrlf.groovy b/buildsupport/scripts/fixcrlf.groovy new file mode 100644 index 0000000000000000000000000000000000000000..0716253ee3fd1b383bd1486d7d0adbe8ed52d7bf --- /dev/null +++ b/buildsupport/scripts/fixcrlf.groovy @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +def ant = new AntBuilder() + +def basedir = System.getProperty('basedir', '.') as File +def fixlast = System.getProperty('fixlast', 'false') + +println "Normalizing line-endings to LF in: ${basedir.canonicalFile}" +println " fixlast: $fixlast" + +ant.fixcrlf( + srcDir: basedir, + eol: 'lf', + eof: 'remove', + fixlast: fixlast +) { + include(name: '**/*.java') + include(name: '**/*.groovy') + include(name: '**/*.js') + include(name: '**/*.css') + include(name: '**/*.vm') + include(name: '**/*.tpl') + include(name: '**/*.properties') + include(name: '**/*.xml') + include(name: '**/*.yml') + include(name: '**/*.txt') +} diff --git a/buildsupport/scripts/gobble.groovy b/buildsupport/scripts/gobble.groovy new file mode 100644 index 0000000000000000000000000000000000000000..fc4c3ef3a3ef2c5f1b3a654934716d2102f99d70 --- /dev/null +++ b/buildsupport/scripts/gobble.groovy @@ -0,0 +1,56 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +// +// Helper to consume the output of `mvn versions:display-dependency-updates versions:display-plugin-updates` +// and spit out unique version upgrades available. +// +// Usage: +// +// mvn versions:display-dependency-updates versions:display-plugin-updates | groovy buildsupport/scripts/gobble.groovy +// + +def lines = System.in.readLines() + +def iter = lines.iterator() +while (iter.hasNext()) { + def line = iter.next() + if (line.startsWith('[INFO] --- versions-maven-plugin')) { + break + } +} + +def chomp(line) { + return line[6..-1].trim() +} + +def updates = new HashSet() +while (iter.hasNext()) { + def line = chomp(iter.next()) + + if (line.endsWith(' ...')) { + line = line + ' ' + chomp(iter.next()) + } + + if (line.contains('->')) { + def parts = line.split('\\s') + def update="${parts[0]}: ${parts[2]} -> ${parts[4]}" + updates << update + } +} + +updates = new ArrayList(updates) +updates.sort() + +for (update in updates) { + println update +} \ No newline at end of file diff --git a/buildsupport/scripts/i18nVerifier.groovy b/buildsupport/scripts/i18nVerifier.groovy new file mode 100644 index 0000000000000000000000000000000000000000..6486b75481254256361822bc51601ae9b2acac26 --- /dev/null +++ b/buildsupport/scripts/i18nVerifier.groovy @@ -0,0 +1,193 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ + +import groovy.io.FileType +import groovy.json.JsonOutput +import groovy.util.logging.Slf4j +import jdk.nashorn.internal.ir.CallNode +import jdk.nashorn.internal.ir.FunctionNode +import jdk.nashorn.internal.ir.LexicalContext +import jdk.nashorn.internal.ir.Node +import jdk.nashorn.internal.ir.ObjectNode +import jdk.nashorn.internal.ir.PropertyNode +import jdk.nashorn.internal.ir.visitor.NodeVisitor +import jdk.nashorn.internal.parser.Parser +import jdk.nashorn.internal.runtime.Context +import jdk.nashorn.internal.runtime.ErrorManager +import jdk.nashorn.internal.runtime.Source +import jdk.nashorn.internal.runtime.options.Options + +@Slf4j +@Grab('ch.qos.logback:logback-classic:1.1.2') +/** + * Parse Javascript files named 'PluginStrings.js' for i18n definitions and compare those to all calls against the + * NX.I18n service to find calls which would result in error. + */ +class NashornExtI18nParser +{ + private final List folders + + private final Options options + + NashornExtI18nParser(final List folders) { + this.folders = folders + folders.each { assert it.exists() && it.isDirectory(), "File doesn't exist or isn't a folder: $it" } + this.options = new Options('nashorn') + options.set('anon.functions', true) + options.set('parse.only', true) + options.set('scripting', true) + } + + Map parse() { + def candidates = [] + def pluginStringsFiles = [] + + folders.each { target -> + target.eachFileRecurse(FileType.FILES) { File file -> + if (file.name.endsWith('.js')) { + if (file.name == 'PluginStrings.js') { + pluginStringsFiles << file + } + else { + if(!shouldIgnore(file)) { + candidates << file + } + } + } + } + } + + def keys = [] as TreeSet + pluginStringsFiles.each { + log.debug "Processing PluginStrings.js: $it" + def pluginStrings = parsePluginStrings(it) + keys.addAll(pluginStrings) + log.debug "Added ${pluginStrings.size()} keys" + } + + LexicalContext lexicalContext = new LexicalContext() + NodeVisitor i18nVisitor = new I18NVisitor(lexicalContext) + NodeVisitor labelHelpVisitor = new LabelHelpVisitor(lexicalContext) + + candidates.each { File file -> + log.debug "Processing candidate: $file" + parseJS(file).accept(lexicalContext, i18nVisitor) + parseJS(file).accept(lexicalContext, labelHelpVisitor) + } + + Set usedKeys = i18nVisitor.keys + def unusedKeys = keys - usedKeys + def undefinedKeys = usedKeys - keys + return [ + 'Available key size': keys.size(), + 'Used key size' : usedKeys.size(), + 'Unused keys' : unusedKeys, + 'Undefined keys' : undefinedKeys, + 'Number of fields with help text' : labelHelpVisitor.fieldsWithHelp.size(), + 'Number of fields without help text' : labelHelpVisitor.fieldsWithoutHelp.size(), + 'Fields without help' : labelHelpVisitor.fieldsWithoutHelp + ] + } + + private List parsePluginStrings(File pluginStrings) { + FunctionNode functionNode = parseJS(pluginStrings) + + def extDefine = functionNode.body.statements[0] + def defineArgs = extDefine.expression.args[1] + def keysField = defineArgs.elements.find { it.keyName == 'keys' } + return keysField.value.elements.collect { it.keyName } + } + + private FunctionNode parseJS(File jsFile) { + ErrorManager errors = new ErrorManager() + Context context = new Context(options, errors, Thread.currentThread().getContextClassLoader()) + return new Parser(context.getEnv(), Source.sourceFor('temp', jsFile.text), errors).parse() + } + + private boolean shouldIgnore(File file) { + def isDev = file.absolutePath.split('/').contains('dev') + if(isDev) { + log.trace("Ignoring $file") + } + return isDev + } +} + +@Slf4j +class I18NVisitor + extends NodeVisitor +{ + + final Set keys = [] as TreeSet + + I18NVisitor(final LexicalContext lc) { + super(lc) + } + + @Override + protected boolean enterDefault(final Node node) { + if (node.getClass() == CallNode && node.toString().contains('NX.I18n') && + (node.function.property == 'get' || node.function.property == 'format')) { + log.trace('Found key: {}',node.args[0].value) + keys << node.args[0].value + } + return true + } +} + +@Slf4j +class LabelHelpVisitor + extends NodeVisitor +{ + static final Closure KEY_FINDER = { String keyName, Node n -> + n.getClass() == PropertyNode && n.keyName == keyName + } + static final Closure FIELD_LABEL = KEY_FINDER.curry('fieldLabel') + static final Closure HELP_TEXT = KEY_FINDER.curry('helpText') + + final Set fieldsWithHelp = [] as TreeSet + final Set fieldsWithoutHelp = [] as TreeSet + + LabelHelpVisitor(final LexicalContext lc) { + super(lc) + } + + @Override + protected boolean enterDefault(final Node node) { + if (node.getClass() == ObjectNode && node.elements.find(FIELD_LABEL)) { + PropertyNode fieldLabel = node.elements.find(FIELD_LABEL) + PropertyNode helpText = node.elements.find(HELP_TEXT) + log.trace(" Found fieldLabel: ${fieldLabel.keyName} with value: ${fieldLabel.value} and helpText: ${helpText?.value}") + if(helpText) { + fieldsWithHelp << fieldLabel.value.toString() + } + else { + fieldsWithoutHelp << fieldLabel.value.toString() + } + } + return true + } +} + +/** + * Default if run from parent of nexus-oss/nexus-pro folders, can be overridden by space separated command line + * parameters + */ +def targetFolders = args ?: [ + 'plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui', + 'private/plugins/nexus-proui-plugin/src/main/resources/static/rapture/NX/proui', + 'plugins/nexus-rapture-plugin/src/main/resources/static/rapture/NX' +] +println JsonOutput. + prettyPrint(JsonOutput. + toJson(new NashornExtI18nParser(targetFolders.collect { String folder -> new File(folder) }).parse())) diff --git a/buildsupport/scripts/nexusresourcedirs.groovy b/buildsupport/scripts/nexusresourcedirs.groovy new file mode 100644 index 0000000000000000000000000000000000000000..d55da06fe0c983781b61771b24760772eb20a298 --- /dev/null +++ b/buildsupport/scripts/nexusresourcedirs.groovy @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +// +// Helper to generate NEXUS_RESOURCE_DIRS environment variable. +// +// Usage from project directory: +// +// export NEXUS_RESOURCE_DIRS=`groovy ./buildsupport/scripts/nexusresourcedirs.groovy` +// +// Aggregate projects using 'roots' property, from parent of project directories: +// +// export NEXUS_RESOURCE_DIRS=`groovy -Droots=nexus-oss,nexus-pro,nexus-bundles nexus-oss/buildsupport/scripts/nexusresourcedirs.groovy` +// + +def roots = System.getProperty('roots', '.') +def dirs = [] + +roots.split(',').each { root -> + def dir = new File(root) + if (dir.exists()) { + dir.eachDirRecurse { + if (it.path.endsWith('src/main/resources/static')) { + dirs << it.parentFile.canonicalPath + } + else if (it.path.endsWith('src/test/ft-resources')) { + dirs << it.canonicalPath + } + } + } +} + +println dirs.join(',') diff --git a/buildsupport/scripts/pom.xml b/buildsupport/scripts/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..137389937463a6b67ec354093cf8d368978456d3 --- /dev/null +++ b/buildsupport/scripts/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-scripts + ${project.groupId}:${project.artifactId} + pom + + + + + + + + org.sonatype.nexus.buildsupport + nexus-buildsupport-all + pom + 3.0.0-SNAPSHOT + import + + + + + + + + + idea + + + org.codehaus.groovy + groovy-all + provided + + + + + + diff --git a/buildsupport/security/pom.xml b/buildsupport/security/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a59a7187afcae0f106801db09b7818fc09e19da4 --- /dev/null +++ b/buildsupport/security/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-security + ${project.groupId}:${project.artifactId} + pom + + + 1.52 + 1.2.4 + + + + + + + + + org.bouncycastle + bcprov-jdk15on + ${bouncycastle.version} + + + + org.bouncycastle + bcpg-jdk15on + ${bouncycastle.version} + + + + org.bouncycastle + bcpkix-jdk15on + ${bouncycastle.version} + + + + + + org.apache.geronimo.framework + geronimo-crypto + 2.2.1 + + + + + + org.apache.shiro + shiro-core + ${apache-shiro.version} + + + commons-logging + commons-logging + + + commons-beanutils + commons-beanutils + + + + + + org.apache.shiro + shiro-guice + ${apache-shiro.version} + + + + org.apache.shiro + shiro-web + ${apache-shiro.version} + + + commons-logging + commons-logging + + + + + + + diff --git a/buildsupport/testing/pom.xml b/buildsupport/testing/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..92b03dbe0c1db7235ae9873577f1f7934130c603 --- /dev/null +++ b/buildsupport/testing/pom.xml @@ -0,0 +1,135 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-testing + ${project.groupId}:${project.artifactId} + pom + + + 4.5.0 + 2.38.0 + + + + + + + org.sonatype.http-testing-harness + server-provider + 1.0-SNAPSHOT + + + + org.sonatype.http-testing-harness + junit-runner + 1.0-SNAPSHOT + + + + org.databene + contiperf + 2.3.4 + + + + com.icegreen + greenmail + 1.4.1 + + + javax.activation + activation + + + + + + junit + junit + 4.12 + + + + org.ops4j.pax.exam + pax-exam + ${pax-exam.version} + + + + org.ops4j.pax.exam + pax-exam-junit4 + ${pax-exam.version} + + + + org.ops4j.pax.exam + pax-exam-container-karaf + ${pax-exam.version} + + + + org.ops4j.pax.url + pax-url-aether + 2.4.1 + + + + + org.apache.directory.server + apacheds-test-framework + 2.0.0-M19 + + + org.slf4j + slf4j-log4j12 + + + bouncycastle + bcprov-jdk15 + + + org.apache.directory.server + apacheds-jdbm-partition + + + org.apache.directory.jdbm + apacheds-jdbm1 + + + + + + com.bryntum.siesta + siesta-standard + 3.1.0 + zip + + + + + + diff --git a/buildsupport/ui/pom.xml b/buildsupport/ui/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..47def0f345b2380ef909555461196c342cc6dab0 --- /dev/null +++ b/buildsupport/ui/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + + + org.sonatype.nexus.buildsupport + nexus-buildsupport + 3.0.0-SNAPSHOT + + + nexus-buildsupport-ui + ${project.groupId}:${project.artifactId} + pom + + + + + com.sencha + ext + commercial + zip + 4.2.3 + + + + com.softwarementors.extjs + directjngine + 2.2 + + + + com.google.code.gson + gson + 2.3.1 + + + + + diff --git a/components/google-guava-eventbus/pom.xml b/components/google-guava-eventbus/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..9035acfe93bc69479d1a7d8d1bc64b108b63b065 --- /dev/null +++ b/components/google-guava-eventbus/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.0.0-SNAPSHOT + + + google-guava-eventbus + ${project.groupId}:${project.artifactId} + bundle + + + + com.google.guava + guava + + + + org.sonatype.goodies + goodies-testsupport + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + guava + + com.google.guava + + + + + + + diff --git a/components/google-guava-eventbus/src/main/java/com/google/common/eventbus/ReentrantEventBus.java b/components/google-guava-eventbus/src/main/java/com/google/common/eventbus/ReentrantEventBus.java new file mode 100644 index 0000000000000000000000000000000000000000..51ea061d1a40490329add56435cc98a53a369e92 --- /dev/null +++ b/components/google-guava-eventbus/src/main/java/com/google/common/eventbus/ReentrantEventBus.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package com.google.common.eventbus; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * A Guava {@link EventBus} that differs from default one by dispatching events as they appear (is re-entrant). + * Guava will queue up all event and dispatch them in the order they were posted, without re-entrance. + * + * @since 3.0 + */ +public class ReentrantEventBus + extends EventBus +{ + /** + * Queues of events for the current thread to dispatch. + */ + private final ThreadLocal> eventsToDispatch = + new ThreadLocal>() + { + @Override + protected Queue initialValue() { + return new LinkedList<>(); + } + }; + + public ReentrantEventBus() { + // empty + } + + public ReentrantEventBus(final String identifier) { + super(identifier); + } + + public ReentrantEventBus(final SubscriberExceptionHandler subscriberExceptionHandler) { + super(subscriberExceptionHandler); + } + + @Override + void enqueueEvent(Object event, EventSubscriber subscriber) { + eventsToDispatch.get().offer(new EventWithSubscriber(event, subscriber)); + } + + @Override + void dispatchQueuedEvents() { + Queue events = eventsToDispatch.get(); + eventsToDispatch.remove(); + EventWithSubscriber eventWithSubscriber; + while ((eventWithSubscriber = events.poll()) != null) { + dispatch(eventWithSubscriber.event, eventWithSubscriber.subscriber); + } + } +} diff --git a/components/google-guava-eventbus/src/main/java/com/google/common/eventbus/package-info.java b/components/google-guava-eventbus/src/main/java/com/google/common/eventbus/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..202200e599c116ec994471dd7b1ce422e8d87b27 --- /dev/null +++ b/components/google-guava-eventbus/src/main/java/com/google/common/eventbus/package-info.java @@ -0,0 +1,16 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +/** + * Customizations of Guava eventbus components, which expose API as package-private. + */ +package com.google.common.eventbus; \ No newline at end of file diff --git a/components/nexus-analytics-api/pom.xml b/components/nexus-analytics-api/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..e9071e4b03a6d8af3f3db8cf19f4457127ba6806 --- /dev/null +++ b/components/nexus-analytics-api/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.0.0-SNAPSHOT + + + nexus-analytics-api + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.apache.shiro + shiro-core + + + + org.sonatype.goodies + goodies-testsupport + test + + + + diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/Anonymizer.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/Anonymizer.java new file mode 100644 index 0000000000000000000000000000000000000000..4593e32a5a5262aeb0c57c042f3e91db9ebb5c36 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/Anonymizer.java @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +/** + * Helper to make data anonymous. + * + * @since 3.0 + */ +public interface Anonymizer +{ + byte[] anonymize(byte[] data); + + String anonymize(String data); +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventData.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventData.java new file mode 100644 index 0000000000000000000000000000000000000000..4cd4cdbf7dada9fffe5d632dde5113296d192b55 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventData.java @@ -0,0 +1,147 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.sonatype.nexus.common.entity.Entity; + +import com.google.common.base.Throwables; + +/** + * Analytics event data. + * + * @since 3.0 + */ +public class EventData + extends Entity + implements Cloneable, Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Event type. + */ + private String type; + + /** + * Event timestamp in milliseconds. + */ + private Long timestamp; + + /** + * Event sequence (rolling) to distinguish events which have the same timestamp. + */ + private Long sequence; + + /** + * Event duration in nanoseconds. + */ + private Long duration; + + /** + * Event user-id. + */ + private String userId; + + /** + * Event session-id. + */ + private String sessionId; + + /** + * Event attributes. + */ + private Map attributes = new HashMap<>(); + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public Long getTimestamp() { + return timestamp; + } + + public void setTimestamp(final Long timestamp) { + this.timestamp = timestamp; + } + + public Long getSequence() { + return sequence; + } + + public void setSequence(final Long sequence) { + this.sequence = sequence; + } + + public Long getDuration() { + return duration; + } + + public void setDuration(final Long duration) { + this.duration = duration; + } + + public String getUserId() { + return userId; + } + + public void setUserId(final String userId) { + this.userId = userId; + } + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(final String sessionId) { + this.sessionId = sessionId; + } + + public Map getAttributes() { + return attributes; + } + + /** + * Returns a deeply cloned copy. + */ + public EventData copy() { + try { + EventData copy = (EventData) clone(); + copy.attributes = new HashMap<>(this.attributes); + return copy; + } + catch (CloneNotSupportedException e) { + throw Throwables.propagate(e); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "type='" + type + '\'' + + ", timestamp=" + timestamp + + ", sequence=" + sequence + + ", duration=" + duration + + ", userId='" + userId + '\'' + + ", sessionId='" + sessionId + '\'' + + ", attributes=" + attributes + + '}'; + } +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventDataBuilder.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventDataBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..f572be2920ca651f146b264b42bcc5ca515febb8 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventDataBuilder.java @@ -0,0 +1,74 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import javax.annotation.Nullable; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Helper to build {@link EventData} instances. + * + * @since 3.0 + */ +public class EventDataBuilder +{ + private static final RollingCounter counter = new RollingCounter(999_999_999_999_999L); + + private final EventData data = new EventData(); + + private final long started; + + public EventDataBuilder(final String type) { + data.setType(type); + data.setTimestamp(System.currentTimeMillis()); + data.setSequence(counter.next()); + + // capture the user and session ids if we can + Subject subject = SecurityUtils.getSubject(); + if (subject != null) { + Object principal = subject.getPrincipal(); + if (principal != null) { + data.setUserId(principal.toString()); + } + + Session session = subject.getSession(false); + if (session != null) { + data.setSessionId(session.getId().toString()); + } + } + + // track started time in nanoseconds for duration calculation + started = System.nanoTime(); + } + + public EventDataBuilder set(final String name, final @Nullable Object value) { + checkNotNull(name); + if (value == null) { + data.getAttributes().put(name, null); + } + else { + data.getAttributes().put(name, String.valueOf(value)); + } + return this; + } + + public EventData build() { + data.setDuration(System.nanoTime() - started); + return data; + } +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventExporter.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventExporter.java new file mode 100644 index 0000000000000000000000000000000000000000..f43c610b4fcd4ac7b4e04fc2a6a5e7e010e71da4 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventExporter.java @@ -0,0 +1,23 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +/** + * Analytics data exporter. + * + * @since 3.0 + */ +public interface EventExporter +{ + void export(EventWriter writer, boolean dropAfterExport) throws Exception; +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventHeader.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..4d7bd1e11461deda1903c3005a9e2d628ec26412 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventHeader.java @@ -0,0 +1,116 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import com.google.common.base.Throwables; + +/** + * Analytics event header. + * + * @since 3.0 + */ +public class EventHeader + implements Cloneable, Serializable +{ + private static final long serialVersionUID = 1L; + + /** + * Format of the event stream (ex. zip-bundle/1) + */ + private String format; + + /** + * The product which produced the events (ex. nexus-oss/2.8) + */ + private String product; + + /** + * The organization the events belong to (ex. customer license identifier). + */ + private String organization; + + /** + * Unique identifier for the server instance that produced events. + */ + private String node; + + /** + * Custom data to associate with event stream. + */ + private Map attributes = new HashMap<>(); + + public String getFormat() { + return format; + } + + public void setFormat(final String format) { + this.format = format; + } + + public String getProduct() { + return product; + } + + public void setProduct(final String product) { + this.product = product; + } + + public String getOrganization() { + return organization; + } + + public void setOrganization(final String organization) { + this.organization = organization; + } + + public String getNode() { + return node; + } + + public void setNode(final String node) { + this.node = node; + } + + public Map getAttributes() { + return attributes; + } + + /** + * Returns a deeply cloned copy. + */ + public EventHeader copy() { + try { + EventHeader copy = (EventHeader) clone(); + copy.attributes = new HashMap<>(this.attributes); + return copy; + } + catch (CloneNotSupportedException e) { + throw Throwables.propagate(e); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "format='" + format + '\'' + + ", product='" + product + '\'' + + ", organization='" + organization + '\'' + + ", node='" + node + '\'' + + ", attributes=" + attributes + + '}'; + } +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventRecorder.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventRecorder.java new file mode 100644 index 0000000000000000000000000000000000000000..10362c9cb642f8fad5aa29e56d24287c3e65c013 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventRecorder.java @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +/** + * Analytics event data recorder. + * + * @since 3.0 + */ +public interface EventRecorder +{ + boolean isEnabled(); + + void record(EventData data); +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventStore.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventStore.java new file mode 100644 index 0000000000000000000000000000000000000000..6e5ef92dced7dd15b183fa98649be1e2d9fff304 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventStore.java @@ -0,0 +1,35 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.common.collect.AutoClosableIterable; + +/** + * Analytics event data store. + * + * @since 3.0 + */ +public interface EventStore + extends Lifecycle +{ + void add(EventData data) throws Exception; + + void clear() throws Exception; + + long approximateSize() throws Exception; + + AutoClosableIterable browse(long offset, @Nullable Long limit) throws Exception; +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventSubmitter.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventSubmitter.java new file mode 100644 index 0000000000000000000000000000000000000000..44c7a85486a32fa7ba0813fb0d285cd49569d3d0 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventSubmitter.java @@ -0,0 +1,25 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import java.io.File; + +/** + * Submits exported events to Analytics service. + * + * @since 3.0 + */ +public interface EventSubmitter +{ + void submit(File file) throws Exception; +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventWriter.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..a1b5f5ceec2db3bc801bd8462e229df318be132b --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/EventWriter.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Analytics event data writer. + * + * @since 3.0 + */ +public interface EventWriter + extends Closeable +{ + /** + * Open event stream and write header. + * + * This must be done prior to any {@link #write} operations. + */ + void open(EventHeader header) throws IOException; + + void write(EventData data) throws IOException; +} diff --git a/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/RollingCounter.java b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/RollingCounter.java new file mode 100644 index 0000000000000000000000000000000000000000..754407c33ce205020d94affe5a34d695bd93bf96 --- /dev/null +++ b/components/nexus-analytics-api/src/main/java/org/sonatype/nexus/analytics/RollingCounter.java @@ -0,0 +1,44 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import java.util.concurrent.atomic.AtomicLong; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Rolling (zero-based) long counter. + * + * @since 3.0 + */ +class RollingCounter +{ + private final AtomicLong value = new AtomicLong(-1); + + private final long max; + + RollingCounter(final long max) { + checkArgument(max > 0); + this.max = max + 1; // inclusive + } + + public long next() { + long current, next; + do { + current = value.get(); + next = (current + 1) % max; + } + while (!value.compareAndSet(current, next)); + return next; + } +} diff --git a/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/EventDataBuilderTest.java b/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/EventDataBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9ab94ae84936d447bf8549806ec129abf6d79774 --- /dev/null +++ b/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/EventDataBuilderTest.java @@ -0,0 +1,107 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.apache.shiro.session.Session; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.subject.support.SubjectThreadState; +import org.apache.shiro.util.ThreadState; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link EventDataBuilder}. + */ +public class EventDataBuilderTest + extends TestSupport +{ + private ThreadState threadState; + + @Mock + private Subject subject; + + @Before + public void setUp() throws Exception { + threadState = new SubjectThreadState(subject); + threadState.bind(); + } + + @After + public void tearDown() throws Exception { + if (threadState != null) { + threadState.clear(); + threadState = null; + } + } + + @Test + public void basics() throws Exception { + when(subject.getPrincipal()).thenReturn("foo"); + Session session = mock(Session.class); + when(subject.getSession(false)).thenReturn(session); + when(session.getId()).thenReturn("1234"); + + EventData event = new EventDataBuilder("TEST").build(); + assertThat(event, notNullValue()); + assertThat(event.getType(), is("TEST")); + assertThat(event.getTimestamp(), notNullValue()); + assertThat(event.getSequence(), notNullValue()); + assertThat(event.getUserId(), is("foo")); + assertThat(event.getSessionId(), is("1234")); + assertThat(event.getDuration(), notNullValue()); + } + + @Test + public void subjectWithNullPrincipal() throws Exception { + when(subject.getPrincipal()).thenReturn(null); + + EventData event = new EventDataBuilder("TEST").build(); + assertThat(event, notNullValue()); + assertThat(event.getUserId(), nullValue()); + } + + @Test + public void subjectWithNullSession() throws Exception { + when(subject.getSession(false)).thenReturn(null); + + EventData event = new EventDataBuilder("TEST").build(); + assertThat(event, notNullValue()); + assertThat(event.getSessionId(), nullValue()); + } + + @Test + public void setAttributes() throws Exception { + EventData event = new EventDataBuilder("TEST") + .set("foo", "bar") + .set("baz", null) + .build(); + + assertThat(event, notNullValue()); + assertThat(event.getAttributes().entrySet(), hasSize(2)); + assertThat(event.getAttributes(), hasEntry("foo", (Object) "bar")); + assertThat(event.getAttributes(), hasEntry("baz", null)); + } +} diff --git a/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/RollingCounterTest.java b/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/RollingCounterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..147aa92c9e5e5befaea5de8767c31811ed176387 --- /dev/null +++ b/components/nexus-analytics-api/src/test/java/org/sonatype/nexus/analytics/RollingCounterTest.java @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.analytics; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +/** + * Tests for {@link RollingCounter}. + */ +public class RollingCounterTest + extends TestSupport +{ + @Test + public void basics() { + RollingCounter underTest = new RollingCounter(5); + assertThat(underTest.next(), is(0L)); + assertThat(underTest.next(), is(1L)); + assertThat(underTest.next(), is(2L)); + assertThat(underTest.next(), is(3L)); + assertThat(underTest.next(), is(4L)); + assertThat(underTest.next(), is(5L)); + + // rolls + assertThat(underTest.next(), is(0L)); + assertThat(underTest.next(), is(1L)); + assertThat(underTest.next(), is(2L)); + assertThat(underTest.next(), is(3L)); + assertThat(underTest.next(), is(4L)); + assertThat(underTest.next(), is(5L)); + + // rolls + assertThat(underTest.next(), is(0L)); + assertThat(underTest.next(), is(1L)); + assertThat(underTest.next(), is(2L)); + assertThat(underTest.next(), is(3L)); + assertThat(underTest.next(), is(4L)); + assertThat(underTest.next(), is(5L)); + } + + @Test(expected = IllegalArgumentException.class) + public void maxTooSmall() { + new RollingCounter(0); + } +} diff --git a/components/nexus-blobstore-api/pom.xml b/components/nexus-blobstore-api/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..4a8d95093697444769e1334c94cb96eef47b70f8 --- /dev/null +++ b/components/nexus-blobstore-api/pom.xml @@ -0,0 +1,44 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.0.0-SNAPSHOT + + + nexus-blobstore-api + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.goodies + goodies-testsupport + test + + + + + diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/Blob.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/Blob.java new file mode 100644 index 0000000000000000000000000000000000000000..76bbc4defd13c0c7a979964cdc6af221173b6dd1 --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/Blob.java @@ -0,0 +1,51 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import java.io.InputStream; +import java.util.Map; + +/** + * A handle for binary data stored within a {@link BlobStore}. + * + * @since 3.0 + */ +public interface Blob +{ + BlobId getId(); + + /** + * An immutable map of the headers that were provided when the blob was created. + * + * @throws BlobStoreException may be thrown if the blob is {@link BlobStore#delete deleted} or + * {@link BlobStore#delete hard deleted}. + */ + Map getHeaders(); + + /** + * Opens an input stream to the blob's content. The returned stream may be closed asynchronously if the blob is + * {@link BlobStore#deleteHard(BlobId) hard deleted}. + * + * @throws BlobStoreException may be thrown if the blob is {@link BlobStore#delete deleted} or + * {@link BlobStore#delete hard deleted}. + */ + InputStream getInputStream(); + + /** + * Provides metrics about this Blob. + * + * @throws BlobStoreException may be thrown if the blob is {@link BlobStore#delete deleted} or + * {@link BlobStore#delete hard deleted}. + */ + BlobMetrics getMetrics(); +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobId.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobId.java new file mode 100644 index 0000000000000000000000000000000000000000..f8508ca0fbacf1277746849a34ac013fb1a0817a --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobId.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import java.io.Serializable; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A unique identifier for a blob within a specific {@link BlobStore}. + * + * @since 3.0 + */ +public class BlobId + implements Serializable, Comparable +{ + private final String id; + + public BlobId(final String id) { + this.id = checkNotNull(id); + } + + public String asUniqueString() { + return id; + } + + @Override + public String toString() { + return id; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BlobId blobId = (BlobId) o; + + return id.equals(blobId.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public int compareTo(final BlobId o) { + return id.compareTo(o.id); + } +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobMetrics.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobMetrics.java new file mode 100644 index 0000000000000000000000000000000000000000..7c457ed1c0c9545b209f46b4501e60542b8088ec --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobMetrics.java @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import java.io.Serializable; + +import org.joda.time.DateTime; + +/** + * Provides basic metrics about a BLOB. + * + * @since 3.0 + */ +public class BlobMetrics + implements Serializable +{ + private final DateTime creationTime; + + private final String SHA1Hash; + + private final long contentSize; + + public BlobMetrics(final DateTime creationTime, final String SHA1Hash, final long contentSize) { + this.creationTime = creationTime; + this.SHA1Hash = SHA1Hash; + this.contentSize = contentSize; + } + + public DateTime getCreationTime() { + return creationTime; + } + + public String getSHA1Hash() { + return SHA1Hash; + } + + public long getContentSize() { + return contentSize; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "creationTime=" + creationTime + + ", SHA1Hash='" + SHA1Hash + '\'' + + ", contentSize=" + contentSize + + '}'; + } +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobRef.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobRef.java new file mode 100644 index 0000000000000000000000000000000000000000..ba95d2ac8e22314716259fff62fce82692ba6b1c --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobRef.java @@ -0,0 +1,103 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Provides a pointer to a blob on a given node, in a given store. + * + * @since 3.0 + */ +public class BlobRef +{ + private final String node; + + private final String store; + + private final String blob; + + public static final Pattern BLOB_REF_PATTERN = Pattern.compile("([^@]+)@([^:]+):(.*)"); + + public BlobRef(final String node, final String store, final String blob) { + this.node = checkNotNull(node); + this.store = checkNotNull(store); + this.blob = checkNotNull(blob); + } + + public String getNode() { + return node; + } + + public String getStore() { + return store; + } + + public String getBlob() { + return blob; + } + + public BlobId getBlobId() { + return new BlobId(getBlob()); + } + + public static BlobRef parse(final String spec) { + final Matcher matcher = BLOB_REF_PATTERN.matcher(spec); + checkArgument(matcher.matches(), "Not a valid blob reference"); + + return new BlobRef(matcher.group(2), matcher.group(1), matcher.group(3)); + } + + /** + * @return the blob ref encoded as a string, using the syntax {@code store@node:blob-id} + */ + public String toString() { + return String.format("%s@%s:%s", getStore(), getNode(), getBlob()); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BlobRef blobRef = (BlobRef) o; + + if (!blob.equals(blobRef.blob)) { + return false; + } + if (!node.equals(blobRef.node)) { + return false; + } + if (!store.equals(blobRef.store)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = node.hashCode(); + result = 31 * result + store.hashCode(); + result = 31 * result + blob.hashCode(); + return result; + } +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStore.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStore.java new file mode 100644 index 0000000000000000000000000000000000000000..db855be86a1cc55cf360ba2929ec433f4eed3ae6 --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStore.java @@ -0,0 +1,123 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import java.io.InputStream; +import java.util.Map; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.common.collect.AutoClosableIterable; + +/** + * A generic storage bin for binary objects of all sizes. + * + * In general, most methods can throw {@link BlobStoreException} for conditions such as network connectivity problems, + * or file IO issues, blob store misconfiguration, or internal corruption. + * + * @since 3.0 + */ +public interface BlobStore + extends Lifecycle +{ + /** + * An identifying name for disaster recovery purposes (which isn't required to be strictly unique) + */ + String BLOB_NAME_HEADER = "BlobStore.blob-name"; + + /** + * An informational header for disaster recovery purposes (describes what blob contains) + */ + String CONTENT_TYPE_HEADER = "BlobStore.content-type"; + + /** + * Audit information (e.g. the name of a principal that created the blob) + */ + String CREATED_BY_HEADER = "BlobStore.created-by"; + + /** + * Creates a new blob. The header map must contain at least two keys: + * + *
    + *
  • {@link #BLOB_NAME_HEADER}
  • + *
  • {@link #CREATED_BY_HEADER}
  • + *
+ * + * @throws BlobStoreException (or a subclass) if the input stream can't be read correctly + * @throws IllegalArgumentException if mandatory headers are missing + */ + Blob create(InputStream blobData, Map headers); + + /** + * Returns the corresponding {@link Blob}, or {@code null} if the blob does not exist or has been {@link #delete + * deleted}. + */ + @Nullable + Blob get(BlobId blobId); + + /** + * Removes a blob from the blob store. This may not immediately delete the blob from the underlying storage + * mechanism, but will make it immediately unavailable to future calls to {@link BlobStore#get(BlobId)}. + * + * @return {@code true} if the blob has been deleted, {@code false} if no blob was found by that ID. + */ + boolean delete(BlobId blobId); + + /** + * Removes a blob from the blob store immediately, disregarding any locking or concurrent access by other threads. + * This should be considered exceptional (e.g. administrative) usage. + * + * @return {@code true} if the blob has been deleted, {@code false} if no blob was found by that ID. + */ + boolean deleteHard(BlobId blobId); + + /** + * Provides metrics about the BlobStore's usage. + */ + BlobStoreMetrics getMetrics(); + + /** + * Installs a listener to receive blob store events. Subsequent calls replace the listener. + */ + void setBlobStoreListener(@Nullable BlobStoreListener listener); + + /** + * Returns whatever BlobStoreListener has been installed, or {@code null}. + */ + @Nullable + BlobStoreListener getBlobStoreListener(); + + /** + * Perform garbage collection, purging blobs marked for deletion or whatever other periodic, implementation-specific + * tasks need doing. + */ + void compact(); + + /** + * Returns the configuration entity for the BlobStore. + */ + BlobStoreConfiguration getBlobStoreConfiguration(); + + /** + * Initialize the BlobStore. + */ + void init(BlobStoreConfiguration configuration) throws Exception; + + /** + * Returns an Iterable of BlobIds. + * + * @return Iterable handle must be closed when finished using it. + */ + AutoClosableIterable iterator(); +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreConfiguration.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..dea5367ed33ab6e554a13ddc1a14af898a769178 --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreConfiguration.java @@ -0,0 +1,86 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import java.util.Map; + +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.common.entity.Entity; + +import com.google.common.collect.Maps; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link BlobStore} configuration. + * + * @since 3.0 + */ +public class BlobStoreConfiguration + extends Entity +{ + private String name; + + private String type; + + private Map> attributes; + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public Map> getAttributes() { + return attributes; + } + + public void setAttributes(final Map> attributes) { + this.attributes = attributes; + } + + public NestedAttributesMap attributes(final String key) { + checkNotNull(key); + + if (attributes == null) { + attributes = Maps.newHashMap(); + } + + Map map = attributes.get(key); + if (map == null) { + map = Maps.newHashMap(); + attributes.put(key, map); + } + + return new NestedAttributesMap(key, map); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "name='" + name + '\'' + + ", type='" + type + '\'' + + ", attributes=" + attributes + + '}'; + } +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreConfigurationStore.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreConfigurationStore.java new file mode 100644 index 0000000000000000000000000000000000000000..36cf17fa6fc4da7536819e249d839c5a3b89f36f --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreConfigurationStore.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import java.util.List; + +import org.sonatype.goodies.lifecycle.Lifecycle; + +/** + * {@link BlobStoreConfiguration} store. + * + * since 3.0 + */ +public interface BlobStoreConfigurationStore + extends Lifecycle +{ + /** + * @return all BlobStoreConfigurations + */ + List list(); + + /** + * Persist a new BlobStoreConfiguration. + */ + void create(BlobStoreConfiguration configuration); + + /** + * Delete an existing BlobStoreConfiguration. + */ + void delete(BlobStoreConfiguration configuration); +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreException.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreException.java new file mode 100644 index 0000000000000000000000000000000000000000..10d0e1af55492d2c5f405fa07dbb1ab802277575 --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreException.java @@ -0,0 +1,47 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import javax.annotation.Nullable; + +/** + * @since 3.0 + */ +public class BlobStoreException + extends RuntimeException +{ + private final BlobId blobId; + + public BlobStoreException(final String message, final @Nullable BlobId blobId) { + super(message); + this.blobId = blobId; + } + + public BlobStoreException(final String message, final Throwable cause, final @Nullable BlobId blobId) { + super(message, cause); + this.blobId = blobId; + } + + public BlobStoreException(final Throwable cause, final @Nullable BlobId blobId) { + super(cause); + this.blobId = blobId; + } + + /** + * The BlobId of the blob related to this exception, or {@code null} if there is none. + */ + @Nullable + public BlobId getBlobId() { + return blobId; + } +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreListener.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreListener.java new file mode 100644 index 0000000000000000000000000000000000000000..9f64078a1f7111adf2f03852f7b0490d0d8d1b5f --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreListener.java @@ -0,0 +1,57 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import java.io.InputStream; +import java.util.Map; + +import javax.annotation.Nullable; + +/** + * An interface for listening to blob store events. + * + * Listeners must be thread-safe, able to receive multiple events concurrently. + * + * @since 3.0 + */ +public interface BlobStoreListener +{ + /** + * Fired after the blob store completes {@link BlobStore#create(InputStream, Map) creation of a new blob}. + * + * @param message an implementation-specific message (e.g. the file the blob was written to) + */ + void blobCreated(Blob blob, @Nullable String message); + + /** + * Fired when the blob store initiates {@link Blob#getInputStream() retrieval of a blob's bytes}. + * + * @param message an implementation-specific message (e.g. which file was accessed) + */ + void blobAccessed(Blob blob, @Nullable String message); + + /** + * Fired after the blob store {@link BlobStore#delete(BlobId) marks a blob for deletion}. + * + * @param message an implementation-specific message (e.g. which file was queued for deletion) + */ + void blobDeleteRequested(BlobId blobId, @Nullable String message); + + /** + * Fired after the blob store removes a blob's bytes from the underlying storage mechanism, whether by an internal + * cleanup process or by a {@link BlobStore#deleteHard(BlobId) hard delete}. + * + * @param message an implementation-specific message (e.g. which file was deleted) + */ + void blobDeleted(BlobId blobId, @Nullable String message); +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreManager.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreManager.java new file mode 100644 index 0000000000000000000000000000000000000000..1d5877772b9a2fc7aa0ffcba991a05dda649920e --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreManager.java @@ -0,0 +1,55 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import org.sonatype.goodies.lifecycle.Lifecycle; + +/** + * {@link BlobStore} manager. + * + * @since 3.0 + */ +public interface BlobStoreManager + extends Lifecycle +{ + /** + * Default blob store name. + */ + final String DEFAULT_BLOBSTORE_NAME = "default"; + + /** + * @return all BlobStores + */ + Iterable browse(); + + /** + * Create a new BlobStore + */ + BlobStore create(BlobStoreConfiguration blobStoreConfiguration) throws Exception; + + /** + * Delete an existing BlobStore + */ + void delete(BlobStoreConfiguration blobStoreConfiguration) throws Exception; + + /** + * Lookup a BlobStore by name + */ + BlobStore get(String name); + + /** + * Delete a BlobStore by name + */ + void delete(String name) throws Exception; + +} diff --git a/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreMetrics.java b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreMetrics.java new file mode 100644 index 0000000000000000000000000000000000000000..add122cee8cf742aee8865a850dd5b7c4ec4bd5d --- /dev/null +++ b/components/nexus-blobstore-api/src/main/java/org/sonatype/nexus/blobstore/api/BlobStoreMetrics.java @@ -0,0 +1,38 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +/** + * Provides usage metrics for a blob-store. + * + * @since 3.0 + */ +public interface BlobStoreMetrics +{ + /** + * Get an approximate count of the number of blobs in the blob store. + */ + long getBlobCount(); + + /** + * Get the approximate total storage space consumed by this blob store in bytes, including blobs, headers, and any + * other metadata required by the store. + */ + long getTotalSize(); + + /** + * An estimate of the remaining space available in the blob store, in bytes. For certain blob stores like S3, this + * may return a value set by a policy rather than some hard storage limit. + */ + long getAvailableSpace(); +} diff --git a/components/nexus-blobstore-api/src/test/java/org/sonatype/nexus/blobstore/api/BlobRefTest.java b/components/nexus-blobstore-api/src/test/java/org/sonatype/nexus/blobstore/api/BlobRefTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b548fd97f74d85798a3cf90926ddd5dad1ca2631 --- /dev/null +++ b/components/nexus-blobstore-api/src/test/java/org/sonatype/nexus/blobstore/api/BlobRefTest.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.api; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; + +/** + * Tests for {@link BlobRef}. + * + * @since 3.0 + */ +public class BlobRefTest +{ + @Test + public void testToString() { + final BlobRef blobRef = new BlobRef("node", "store", "blobId"); + final String spec = blobRef.toString(); + final BlobRef reconstituted = BlobRef.parse(spec); + + assertThat(reconstituted, is(equalTo(blobRef))); + } +} diff --git a/components/nexus-blobstore-file/pom.xml b/components/nexus-blobstore-file/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..cffe2fb0dae86dd21b2eea421e29ad5b60082ea3 --- /dev/null +++ b/components/nexus-blobstore-file/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.0.0-SNAPSHOT + + + nexus-blobstore-file + ${project.groupId}:${project.artifactId} + bundle + + + + org.sonatype.nexus + nexus-common + + + + org.sonatype.nexus + nexus-blobstore-api + + + + org.mapdb + mapdb + + + + org.sonatype.goodies + goodies-testsupport + test + + + + diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobMetadata.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobMetadata.java new file mode 100644 index 0000000000000000000000000000000000000000..32b0f596ad5c84861393ff9d6d1dbde8f57fb03f --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobMetadata.java @@ -0,0 +1,71 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import java.util.Map; + +import org.sonatype.nexus.blobstore.api.BlobMetrics; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Metadata about a blob content, including headers, metrics and deleted status. + * + * @since 3.0 + */ +public class FileBlobMetadata +{ + private FileBlobState blobState; + + private final Map headers; + + private BlobMetrics metrics; + + public FileBlobMetadata(final FileBlobState blobState, final Map headers) { + this.blobState = checkNotNull(blobState); + this.headers = checkNotNull(headers); + } + + public FileBlobState getBlobState() { + return blobState; + } + + public void setBlobState(final FileBlobState blobState) { + this.blobState = blobState; + } + + public boolean isAlive() { + return FileBlobState.ALIVE.equals(blobState); + } + + public Map getHeaders() { + return headers; + } + + public void setMetrics(final BlobMetrics metrics) { + this.metrics = metrics; + } + + public BlobMetrics getMetrics() { + return metrics; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "blobState=" + blobState + + ", headers=" + headers + + ", metrics=" + metrics + + '}'; + } +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobMetadataStore.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobMetadataStore.java new file mode 100644 index 0000000000000000000000000000000000000000..d1607ac84f69c840c57b1ceea10042ff28474f8c --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobMetadataStore.java @@ -0,0 +1,63 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.lifecycle.Lifecycle; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.common.collect.AutoClosableIterable; + +/** + * @since 3.0 + */ +public interface FileBlobMetadataStore + extends Lifecycle +{ + /** + * Adds the metadata, and returns the key it's now associated with. + */ + BlobId add(FileBlobMetadata metadata); + + @Nullable + FileBlobMetadata get(BlobId key); + + void update(BlobId blobId, FileBlobMetadata metadata); + + void delete(BlobId blobId); + + /** + * Returns iterable with all blob-ids in the given state. + * Iterable handle must be closed when finished using it. + */ + AutoClosableIterable findWithState(FileBlobState state); + + long getBlobCount(); + + /** + * @return total size in bytes of metadata and blob content + */ + long getTotalSize(); + + /** + * @return total size in bytes used to store metadata + */ + long getMetadataSize(); + + /** + * @return total size in bytes used to store blobs + */ + long getBlobSize(); + + void compact(); +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobState.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobState.java new file mode 100644 index 0000000000000000000000000000000000000000..ac3022676ebe2ac2082e7d20a29bc3325cb1ecb3 --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobState.java @@ -0,0 +1,36 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +/** + * A representation of the lifecycle of blobs in the store. + * + * @since 3.0 + */ +public enum FileBlobState +{ + /** + * State for blobs that are in process of being created. + */ + CREATING, + + /** + * State for blobs that have finished being created. + */ + ALIVE, + + /** + * State for blobs that are marked for deletion. + */ + MARKED_FOR_DELETION +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobStore.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobStore.java new file mode 100644 index 0000000000000000000000000000000000000000..f3bf70125d033fc7dd35261b553021c9dff09afa --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileBlobStore.java @@ -0,0 +1,394 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Named; + +import org.sonatype.goodies.lifecycle.LifecycleSupport; +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.blobstore.api.BlobStoreException; +import org.sonatype.nexus.blobstore.api.BlobStoreListener; +import org.sonatype.nexus.blobstore.api.BlobStoreMetrics; +import org.sonatype.nexus.blobstore.file.FileOperations.StreamMetrics; +import org.sonatype.nexus.blobstore.file.internal.FileBlobMetadataStoreImpl; +import org.sonatype.nexus.common.collect.AutoClosableIterable; +import org.sonatype.nexus.common.io.DirSupport; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Throwables; +import com.google.common.collect.Maps; +import org.joda.time.DateTime; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A {@link BlobStore} that stores its content on the file system, and metadata in a {@link FileBlobMetadataStore}. + * + * @since 3.0 + */ +@Named(FileBlobStore.TYPE) +public class FileBlobStore + extends LifecycleSupport + implements BlobStore +{ + public static final String TYPE = "File"; + + public static final String BLOB_CONTENT_SUFFIX = ".blob"; + + private static final String CONFIG_KEY = "file"; + + private static final String PATH_KEY = "path"; + + private Path root; + + private FileBlobMetadataStore metadataStore; + + private final LocationStrategy locationStrategy; + + private final FileOperations fileOperations; + + private volatile BlobStoreListener listener; + + private BlobStoreConfiguration blobStoreConfiguration; + + @Inject + public FileBlobStore(final LocationStrategy locationStrategy, + final FileOperations fileOperations) + { + this.locationStrategy = checkNotNull(locationStrategy); + this.fileOperations = checkNotNull(fileOperations); + } + + @VisibleForTesting + public FileBlobStore(final Path root, + final LocationStrategy locationStrategy, + final FileOperations fileOperations, + final FileBlobMetadataStore metadataStore, + final BlobStoreConfiguration configuration) + { + this(locationStrategy, fileOperations); + this.root = checkNotNull(root); + this.metadataStore = checkNotNull(metadataStore); + this.blobStoreConfiguration = checkNotNull(configuration); + } + + @Override + protected void doStart() throws Exception { + metadataStore.start(); + } + + @Override + protected void doStop() throws Exception { + metadataStore.stop(); + } + + @Override + public void setBlobStoreListener(@Nullable final BlobStoreListener listener) { + this.listener = listener; + } + + @Nullable + @Override + public BlobStoreListener getBlobStoreListener() { + return listener; + } + + /** + * Returns path for blob-id content file relative to root directory. + */ + private Path pathFor(final BlobId id) { + String location = locationStrategy.location(id); + return root.resolve(location + BLOB_CONTENT_SUFFIX); + } + + @Override + public Blob create(final InputStream blobData, final Map headers) { + checkNotNull(blobData); + checkNotNull(headers); + + checkArgument(headers.containsKey(BLOB_NAME_HEADER), "Missing header: %s", BLOB_NAME_HEADER); + checkArgument(headers.containsKey(CREATED_BY_HEADER), "Missing header: %s", CREATED_BY_HEADER); + + BlobId blobId = null; + + try { + // If the storing of bytes fails, we record a reminder to clean up afterwards + final FileBlobMetadata metadata = new FileBlobMetadata(FileBlobState.CREATING, headers); + blobId = metadataStore.add(metadata); + + final Path path = pathFor(blobId); + log.debug("Writing blob {} to {}", blobId, path); + + final StreamMetrics streamMetrics = fileOperations.create(path, blobData); + final BlobMetrics metrics = new BlobMetrics(new DateTime(), streamMetrics.getSHA1(), streamMetrics.getSize()); + final FileBlob blob = new FileBlob(blobId, headers, path, metrics); + + if (listener != null) { + listener.blobCreated(blob, "Blob: " + blobId + " written to: " + path); + } + + metadata.setMetrics(metrics); + // Storing the content went fine, so we can now unmark this for deletion + metadata.setBlobState(FileBlobState.ALIVE); + metadataStore.update(blobId, metadata); + + return blob; + } + catch (IOException e) { + throw new BlobStoreException(e, blobId); + } + } + + @Nullable + @Override + public Blob get(final BlobId blobId) { + checkNotNull(blobId); + + FileBlobMetadata metadata = metadataStore.get(blobId); + if (metadata == null) { + log.debug("Attempt to access non-existent blob {}", blobId); + return null; + } + + if (!metadata.isAlive()) { + log.debug("Attempt to access blob {} in state {}", blobId, metadata.getBlobState()); + return null; + } + + final FileBlob blob = new FileBlob(blobId, metadata.getHeaders(), pathFor(blobId), metadata.getMetrics()); + + log.debug("Accessing blob {}", blobId); + if (listener != null) { + listener.blobAccessed(blob, null); + } + return blob; + } + + @Override + public boolean delete(final BlobId blobId) { + checkNotNull(blobId); + + FileBlobMetadata metadata = metadataStore.get(blobId); + if (metadata == null) { + log.debug("Attempt to mark-for-delete non-existent blob {}", blobId); + return false; + } + else if (!metadata.isAlive()) { + log.debug("Attempt to delete blob {} in state {}", blobId, metadata.getBlobState()); + return false; + } + + metadata.setBlobState(FileBlobState.MARKED_FOR_DELETION); + // TODO: Handle concurrent modification of metadata + metadataStore.update(blobId, metadata); + return true; + } + + @Override + public boolean deleteHard(final BlobId blobId) { + checkNotNull(blobId); + + FileBlobMetadata metadata = metadataStore.get(blobId); + if (metadata == null) { + log.debug("Attempt to deleteHard non-existent blob {}", blobId); + return false; + } + + try { + final Path path = pathFor(blobId); + final boolean blobDeleted = fileOperations.delete(path); + + if (!blobDeleted) { + log.error("Deleting blob {} : content file was missing", blobId); + } + + log.debug("Deleting-hard blob {}", blobId); + + if (listener != null) { + listener.blobDeleted(blobId, "Path: " + path); + } + + metadataStore.delete(blobId); + + return blobDeleted; + } + catch (IOException e) { + throw new BlobStoreException(e, blobId); + } + } + + @Override + public BlobStoreMetrics getMetrics() { + return new BlobStoreMetrics() + { + @Override + public long getBlobCount() { + return metadataStore.getBlobCount(); + } + + @Override + public long getTotalSize() { + return metadataStore.getTotalSize(); + } + + @Override + public long getAvailableSpace() { + try { + final FileStore fileStore = Files.getFileStore(root); + return fileStore.getUsableSpace(); + } + catch (IOException e) { + throw new BlobStoreException(e, null); + } + } + }; + } + + @Override + public void compact() { + log.debug("Compacting"); + + try { + int count = 0; + try (AutoClosableIterable iter = metadataStore.findWithState(FileBlobState.MARKED_FOR_DELETION)) { + for (BlobId blobId : iter) { + deleteHard(blobId); + count++; + } + } + + metadataStore.compact(); + + log.debug("Deleted {} blobs", count); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + } + + @Override + public BlobStoreConfiguration getBlobStoreConfiguration() { + return this.blobStoreConfiguration; + } + + @Override + public void init(final BlobStoreConfiguration configuration) throws IOException { + this.blobStoreConfiguration = configuration; + Path blobDir = Paths.get(String.valueOf(configuration.attributes(CONFIG_KEY).require(PATH_KEY))); + Path content = blobDir.resolve("content"); + File metadataFile = blobDir.resolve("metadata").toFile(); + DirSupport.mkdir(content); + DirSupport.mkdir(metadataFile); + this.root = content; + this.metadataStore = FileBlobMetadataStoreImpl.create(metadataFile); + } + + @Override + public AutoClosableIterable iterator() { + return metadataStore.findWithState(FileBlobState.ALIVE); + } + + private void checkExists(final Path path, final BlobId blobId) throws IOException { + if (!fileOperations.exists(path)) { + // I'm not completely happy with this, since it means that blob store clients can get a blob, be satisfied + // that it exists, and then discover that it doesn't, mid-operation + throw new BlobStoreException("Blob has been deleted", blobId); + } + } + + private String getPath(final Map> attributes) { + return (String) attributes.get("file").get("path"); + } + + public static Map> attributes(final String path) { + Map> map = Maps.newHashMap(); + HashMap attributes = Maps.newHashMap(); + attributes.put("path", path); + map.put("file", attributes); + return map; + } + + public static BlobStoreConfiguration configure(final String name, final String path) { + BlobStoreConfiguration configuration = new BlobStoreConfiguration(); + configuration.setName(name); + configuration.setType(FileBlobStore.TYPE); + configuration.attributes(CONFIG_KEY).set(PATH_KEY, path); + return configuration; + } + + class FileBlob + implements Blob + { + private final BlobId blobId; + + private final Map headers; + + private final Path contentPath; + + private final BlobMetrics metrics; + + FileBlob(final BlobId blobId, + final Map headers, + final Path contentPath, + final BlobMetrics metrics) + { + this.blobId = checkNotNull(blobId); + this.headers = checkNotNull(headers); + this.contentPath = checkNotNull(contentPath); + this.metrics = checkNotNull(metrics); + } + + @Override + public BlobId getId() { + return blobId; + } + + @Override + public Map getHeaders() { + return headers; + } + + @Override + public InputStream getInputStream() { + try { + checkExists(contentPath, blobId); + return fileOperations.openInputStream(contentPath); + } + catch (IOException e) { + throw new BlobStoreException(e, blobId); + } + } + + @Override + public BlobMetrics getMetrics() { + return metrics; + } + } +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileOperations.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileOperations.java new file mode 100644 index 0000000000000000000000000000000000000000..0d419ccf4050400a1d361ffa5a5f97cdc15cbcf5 --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/FileOperations.java @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; + +/** + * A wrapper around file operations to make mocking easier. + * + * @since 3.0 + */ +public interface FileOperations +{ + class StreamMetrics + { + private final long size; + + private final String SHA1; + + public StreamMetrics(final long size, final String SHA1) { + this.size = size; + this.SHA1 = SHA1; + } + + public long getSize() { + return size; + } + + public String getSHA1() { + return SHA1; + } + } + + /** + * Creates a file (and its containing directories, if necessary) and populates it from the + * InputStream, which gets closed. + * + * @return Basic metrics about the stream. + */ + StreamMetrics create(Path path, InputStream data) throws IOException; + + boolean exists(Path path); + + InputStream openInputStream(Path path) throws IOException; + + /** + * Returns true if the file existed before deletion, false otherwise. + */ + boolean delete(Path path) throws IOException; +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/LocationStrategy.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/LocationStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..c90734fa6d95069eb70b80eee904aeffdab1260e --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/LocationStrategy.java @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import org.sonatype.nexus.blobstore.api.BlobId; + +/** + * Strategy determines the location of a blob file in a store. + * + * Implementations might use algorithms to divide files up into multiple directories for easier management. + * + * @since 3.0 + */ +public interface LocationStrategy +{ + /** + * Returns the location where blob file should exist. + */ + String location(BlobId blobId); +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/SimpleFileOperations.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/SimpleFileOperations.java new file mode 100644 index 0000000000000000000000000000000000000000..31ba3df82e7ae22b96d643f964170597e0f83e21 --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/SimpleFileOperations.java @@ -0,0 +1,92 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +import javax.inject.Named; + +import org.sonatype.goodies.common.ComponentSupport; +import org.sonatype.nexus.blobstore.file.internal.MetricsInputStream; +import org.sonatype.nexus.common.io.DirSupport; + +import com.google.common.io.ByteStreams; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A simple {@code java.nio} implementation of {@link FileOperations}. + * + * TODO: Move this and MapdbBlobMetadataStore back into .internal, with a parameterizable provider + * + * @since 3.0 + */ +@Named +public class SimpleFileOperations + extends ComponentSupport + implements FileOperations +{ + @Override + public StreamMetrics create(final Path path, final InputStream data) throws IOException { + checkNotNull(path); + checkNotNull(data); + + // Ensure path exists for new blob + Path dir = path.getParent(); + checkNotNull(dir, "Null parent for path: %s", path); + DirSupport.mkdir(dir); + + final MetricsInputStream input = new MetricsInputStream(data); + try { + try (final OutputStream output = Files.newOutputStream(path, StandardOpenOption.CREATE_NEW)) { + ByteStreams.copy(input, output); + } + } + finally { + // FIXME: Revisit closing stream which is passed in as parameter, this should be the responsibility of the caller + data.close(); + } + + return input.getMetrics(); + } + + @Override + public boolean exists(final Path path) { + checkNotNull(path); + return Files.exists(path); + } + + @Override + public InputStream openInputStream(final Path path) throws IOException { + checkNotNull(path); + return Files.newInputStream(path, StandardOpenOption.READ); + } + + @Override + public boolean delete(final Path path) throws IOException { + checkNotNull(path); + boolean deleted = Files.deleteIfExists(path); + + // complain if request to delete file has failed + if (!deleted && exists(path)) { + throw new IOException("File was not successfully deleted: " + path); + } + + return deleted; + } +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/VolumeChapterLocationStrategy.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/VolumeChapterLocationStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..37d82c7a87c76af13ac4cbec1f18fb0a7af7a561 --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/VolumeChapterLocationStrategy.java @@ -0,0 +1,59 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import java.util.regex.Pattern; + +import javax.inject.Named; + +import org.sonatype.nexus.blobstore.api.BlobId; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Stores blobs in a two-deep directory tree. + * + * The first layer, {@code vol}, having {@link #TIER_1_MODULO} directories, + * and the second {@code chap} having {@link #TIER_2_MODULO}. + * + * @since 3.0 + */ +@Named("volume-chapter") +public class VolumeChapterLocationStrategy + implements LocationStrategy +{ + private static final int TIER_1_MODULO = 43; + + private static final int TIER_2_MODULO = 47; + + private static final Pattern UNSAFE_TOKENS = Pattern.compile("[.\\\\:/]"); + + @Override + public String location(final BlobId blobId) { + checkNotNull(blobId); + + return String.format("vol-%02d/chap-%02d/%s", + tier(blobId, TIER_1_MODULO), + tier(blobId, TIER_2_MODULO), + escapeFilename(blobId.asUniqueString()) + ); + } + + private int tier(final BlobId blobId, final int modulo) { + return Math.abs(blobId.hashCode() % modulo) + 1; + } + + private String escapeFilename(final String value) { + return UNSAFE_TOKENS.matcher(value).replaceAll("-"); + } +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/BlobIdSerializer.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/BlobIdSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..a16612bfc8a811c9e2e244cf5e82b8c9b80237c0 --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/BlobIdSerializer.java @@ -0,0 +1,46 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.Serializable; + +import org.sonatype.nexus.blobstore.api.BlobId; + +import org.mapdb.Serializer; + +/** + * {@link BlobId} serializer. + * + * @since 3.0 + */ +class BlobIdSerializer + implements Serializer, Serializable +{ + @Override + public void serialize(final DataOutput out, final BlobId value) throws IOException { + out.writeUTF(value.asUniqueString()); + } + + @Override + public BlobId deserialize(final DataInput in, final int available) throws IOException { + return new BlobId(in.readUTF()); + } + + @Override + public int fixedSize() { + return -1; + } +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/ExternalizationHelper.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/ExternalizationHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..a5bc265ccb49613600d8a3770e8863720dd0fb6c --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/ExternalizationHelper.java @@ -0,0 +1,68 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.ObjectOutput; + +import javax.annotation.Nullable; + +/** + * Helper methods for externalizing primitives to {@link ObjectOutput} instances. + * + * @since 3.0 + */ +public class ExternalizationHelper +{ + /** + * Writes a possibly null {@link Long} to an {@link DataOutput}. + * + * @see #readNullableLong(DataInput) + */ + public static void writeNullableLong(DataOutput out, Long value) throws IOException { + out.writeBoolean(value != null); + if (value != null) { + out.writeLong(value); + } + } + + @Nullable + public static Long readNullableLong(DataInput in) throws IOException { + if (in.readBoolean()) { + return in.readLong(); + } + return null; + } + + /** + * Writes a possibly null {@link String} to an {@link DataOutput}. + * + * @see #readNullableString(DataInput) + */ + public static void writeNullableString(DataOutput out, String value) throws IOException { + out.writeBoolean(value != null); + if (value != null) { + out.writeUTF(value); + } + } + + @Nullable + public static String readNullableString(DataInput in) throws IOException { + if (in.readBoolean()) { + return in.readUTF(); + } + return null; + } +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/FileBlobMetadataStoreImpl.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/FileBlobMetadataStoreImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..38cf1cd92ffa577eecbcc1269d2183091598e888 --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/FileBlobMetadataStoreImpl.java @@ -0,0 +1,390 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.File; +import java.util.Iterator; +import java.util.NavigableSet; + +import javax.annotation.Nullable; + +import org.sonatype.goodies.lifecycle.LifecycleSupport; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.file.FileBlobMetadata; +import org.sonatype.nexus.blobstore.file.FileBlobMetadataStore; +import org.sonatype.nexus.blobstore.file.FileBlobState; +import org.sonatype.nexus.common.collect.AutoClosableIterable; +import org.sonatype.nexus.common.guice.TcclWrapperFactory; +import org.sonatype.nexus.common.io.DirSupport; + +import com.google.common.collect.Maps; +import org.mapdb.Atomic; +import org.mapdb.BTreeKeySerializer.BasicKeySerializer; +import org.mapdb.DB; +import org.mapdb.DBMaker; +import org.mapdb.Fun; +import org.mapdb.HTreeMap; +import org.mapdb.TxBlock; +import org.mapdb.TxMaker; +import org.mapdb.TxRollbackException; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * MapDB implementation of {@link FileBlobMetadataStore}. + * + * @since 3.0 + */ +public class FileBlobMetadataStoreImpl + extends LifecycleSupport + implements FileBlobMetadataStore +{ + private static final String ID_SEQUENCE_NAME = "id_sequence"; + + private static final String ENTRIES_NAME = "entries"; + + private static final String STATE_PREFIX = "state_"; + + private final File file; + + private TxMaker database; + + /** + * The number of records counted when last cacheing size + */ + private long numRecords = 0L; + + /** + * The cached number of bytes stored. + */ + private long cachedSize = 0L; + + private FileBlobMetadataStoreImpl(final File directory) { + checkNotNull(directory); + this.file = new File(directory, directory.getName() + ".db"); + log.debug("File: {}", file); + } + + /** + * MapDB uses a classloading strategy incompatible with OSGi (it uses the current thread's context class loader). + * This method produces a BlobMetadataStore that has been wrapped with a proxy that ensures the right classloader + * is used for mapdb's serializing/deserializing operations. + */ + public static FileBlobMetadataStore create(final File directory) { + return TcclWrapperFactory.create( + FileBlobMetadataStore.class, + new FileBlobMetadataStoreImpl(directory), + FileBlobMetadataStoreImpl.class.getClassLoader() + ); + } + + /** + * Returns the primary database file. MapDB has additional files which are based on this filename. + */ + public File getFile() { + return file; + } + + @Override + protected void doStart() throws Exception { + DirSupport.mkdir(file.getParentFile()); + + this.database = DBMaker.newFileDB(file) + .checksumEnable() + .mmapFileEnableIfSupported() + .makeTxMaker(); + + // prepare the database tables, non-lazy so that use of DB.snapshot() will not fail if table does not exist yet + database.execute(new TxBlock() + { + @Override + public void tx(final DB db) { + db.getAtomicLong(ID_SEQUENCE_NAME); + + db.createHashMap(ENTRIES_NAME) + .counterEnable() + .keySerializer(new BlobIdSerializer()) + .valueSerializer(new MetadataRecord.SerializerImpl()) + .makeOrGet(); + + for (FileBlobState state : FileBlobState.values()) { + db.createTreeSet(STATE_PREFIX + state.name()) + .serializer(new BasicKeySerializer(new BlobIdSerializer())) + .makeOrGet(); + } + } + }); + } + + @Override + protected void doStop() throws Exception { + database.close(); + database = null; + } + + /** + * Access id-sequence counter. + */ + private Atomic.Long idSequence(final DB db) { + return db.getAtomicLong(ID_SEQUENCE_NAME); + } + + /** + * Access entries table. + */ + private HTreeMap entries(final DB db) { + return db.getHashMap(ENTRIES_NAME); + } + + /** + * Access blob-state table. + */ + private NavigableSet states(final DB db, final FileBlobState state) { + return db.getTreeSet(STATE_PREFIX + state.name()); + } + + private MetadataRecord convert(final FileBlobMetadata source) { + return new MetadataRecord(source); + } + + private FileBlobMetadata convert(final MetadataRecord source) { + FileBlobMetadata target = new FileBlobMetadata(source.state, Maps.newHashMap(source.headers)); + if (source.metrics) { + target.setMetrics(new BlobMetrics(source.created, source.sha1, source.size)); + } + return target; + } + + /** + * Generate a new blob identifier. + */ + private BlobId newId(final DB db) { + long id = idSequence(db).incrementAndGet(); + return new BlobId(String.format("%016x", id)); + } + + @Override + public BlobId add(final FileBlobMetadata metadata) { + checkNotNull(metadata); + ensureStarted(); + + final MetadataRecord record = convert(metadata); + + return database.execute(new Fun.Function1() + { + @Override + public BlobId run(final DB db) { + BlobId id = newId(db); + log.trace("Add: {}={}", id, record); + + MetadataRecord prev = entries(db).put(id, record); + checkState(prev == null, "Duplicate blob-id: %s", id); + + // track state + states(db, record.state).add(id); + + return id; + } + }); + } + + @Nullable + @Override + public FileBlobMetadata get(final BlobId id) { + checkNotNull(id); + ensureStarted(); + + log.trace("Get: {}", id); + + DB db = database.makeTx(); + try { + MetadataRecord record = entries(db).get(id); + if (record != null) { + return convert(record); + } + return null; + } + finally { + db.close(); + } + } + + @Override + public void update(final BlobId id, final FileBlobMetadata metadata) { + checkNotNull(id); + checkNotNull(metadata); + ensureStarted(); + + final MetadataRecord record = convert(metadata); + log.trace("Update: {}={}", id, record); + + database.execute(new TxBlock() + { + @Override + public void tx(final DB db) { + MetadataRecord prev = entries(db).put(id, record); + checkState(prev != null, "Can not update non-existent blob-id: %s", id); + + // replace state + states(db, prev.state).remove(id); + states(db, record.state).add(id); + } + }); + } + + @Override + public void delete(final BlobId id) { + checkNotNull(id); + ensureStarted(); + + log.trace("Delete: {}", id); + + database.execute(new TxBlock() + { + @Override + public void tx(final DB db) { + MetadataRecord prev = entries(db).remove(id); + checkState(prev != null, "Can not delete non-existent blob-id: %s", id); + + // remove state + states(db, prev.state).remove(id); + } + }); + } + + @Override + public AutoClosableIterable findWithState(final FileBlobState state) { + checkNotNull(state); + ensureStarted(); + + log.trace("Find with state: {}", state); + + final DB db = database.makeTx().snapshot(); + + return new AutoClosableIterable() + { + private volatile boolean closed = false; + + @Override + public Iterator iterator() { + return states(db, state).iterator(); + } + + @Override + public void close() throws Exception { + db.close(); + closed = true; + } + + @Override + protected void finalize() throws Throwable { + try { + if (!closed) { + log.warn("Leaked database connection: {}", db); + db.close(); + } + } + finally { + super.finalize(); + } + } + }; + } + + private File[] listFiles() { + File[] files = file.getParentFile().listFiles(); + if (files == null) { + // should never happen + return new File[0]; + } + return files; + } + + @Override + public long getBlobCount() { + ensureStarted(); + DB db = database.makeTx(); + try { + return entries(db).sizeLong(); + } + finally { + db.close(); + } + } + + @Override + public long getTotalSize() { + ensureStarted(); + + return getMetadataSize() + getBlobSize(); + } + + @Override + public long getMetadataSize() { + ensureStarted(); + + // sum all file bytes in the database root + long bytes = 0; + for (File file : listFiles()) { + bytes += file.length(); + } + return bytes; + } + + @Override + public long getBlobSize() { + ensureStarted(); + return database.execute(new Fun.Function1() + { + @Override + public Long run(final DB db) { + long totalSize = 0L; + HTreeMap entries = entries(db); + if (entries.sizeLong() == numRecords) { + log.debug("Returning cached blob size"); + return cachedSize; + } + + log.debug("Cached data is invalid, recalculating size of blobs"); + + for (MetadataRecord metadataRecord : entries.values()) { + if (metadataRecord.size != null) { + totalSize += metadataRecord.size; + } + else { + log.warn("Blob metadata has no size indicated for sha1: {}", metadataRecord.sha1); + } + } + numRecords = entries.sizeLong(); + cachedSize = totalSize; + return cachedSize; + } + }); + } + + @Override + public void compact() { + ensureStarted(); + + database.execute(new TxBlock() + { + @Override + public void tx(final DB db) throws TxRollbackException { + log.trace("Compacting"); + db.compact(); + } + }); + } +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/MetadataRecord.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/MetadataRecord.java new file mode 100644 index 0000000000000000000000000000000000000000..2ad1d55915c407251c7a580073fc08896341d09a --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/MetadataRecord.java @@ -0,0 +1,192 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.Serializable; +import java.util.Map; +import java.util.Objects; + +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.file.FileBlobMetadata; +import org.sonatype.nexus.blobstore.file.FileBlobState; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; +import org.joda.time.DateTime; +import org.mapdb.Serializer; + +import static com.google.common.base.Preconditions.checkState; +import static org.sonatype.nexus.blobstore.file.internal.ExternalizationHelper.readNullableLong; +import static org.sonatype.nexus.blobstore.file.internal.ExternalizationHelper.readNullableString; +import static org.sonatype.nexus.blobstore.file.internal.ExternalizationHelper.writeNullableLong; +import static org.sonatype.nexus.blobstore.file.internal.ExternalizationHelper.writeNullableString; + +/** + * Metadata record for internal storage in MapDB. + * + * @since 3.0 + */ +class MetadataRecord +{ + FileBlobState state; + + Map headers; + + boolean metrics; + + DateTime created; + + String sha1; + + Long size; + + /** + * CTOR for deserialization. + */ + @VisibleForTesting + MetadataRecord() { + // empty + } + + public MetadataRecord(final FileBlobMetadata source) { + this.state = source.getBlobState(); + this.headers = Maps.newHashMap(source.getHeaders()); + BlobMetrics metrics = source.getMetrics(); + if (metrics != null) { + this.metrics = true; + this.created = metrics.getCreationTime(); + this.sha1 = metrics.getSHA1Hash(); + this.size = metrics.getContentSize(); + } + else { + this.metrics = false; + this.created = null; + this.sha1 = null; + this.size = null; + } + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + MetadataRecord that = (MetadataRecord) o; + + if (!Objects.equals(state, that.state)) { + return false; + } + if (!Objects.equals(headers, that.headers)) { + return false; + } + if (metrics != that.metrics) { + return false; + } + if (!Objects.equals(created, that.created)) { + return false; + } + if (!Objects.equals(sha1, that.sha1)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return Objects.hash(state, headers, metrics, created, sha1, size); + } + + + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + "state=" + state + + ", headers=" + headers + + ", metrics=" + metrics + + ", created=" + created + + ", sha1='" + sha1 + '\'' + + ", size=" + size + + '}'; + } + + // + // SerializerImpl + // + + static class SerializerImpl + implements Serializer, Serializable + { + final static int FORMAT_VERSION = 1; + + @Override + public void serialize(final DataOutput out, final MetadataRecord value) throws IOException { + out.writeInt(FORMAT_VERSION); + + out.writeInt(value.state.ordinal()); + + out.writeInt(value.headers.size()); + for (Map.Entry header : value.headers.entrySet()) { + writeNullableString(out, header.getKey()); + writeNullableString(out, header.getValue()); + } + + out.writeBoolean(value.metrics); + + if (value.metrics) { + // writeObject preserves nulls + writeNullableLong(out, value.created == null ? null : value.created.getMillis()); + writeNullableString(out, value.sha1); + writeNullableLong(out, value.size); + } + } + + @Override + public MetadataRecord deserialize(final DataInput in, final int available) throws IOException { + MetadataRecord value = new MetadataRecord(); + + final int version = in.readInt(); + checkState(version == FORMAT_VERSION, "Version must be %s", FORMAT_VERSION); + + value.state = FileBlobState.values()[in.readInt()]; + + value.headers = Maps.newHashMap(); + final int numberOfHeaders = in.readInt(); + for (int i = 0; i < numberOfHeaders; i++) { + value.headers.put(readNullableString(in), readNullableString(in)); + } + + value.metrics = in.readBoolean(); + if (value.metrics) { + final Long createdMillis = readNullableLong(in); + if (createdMillis != null) { + value.created = new DateTime(createdMillis); + } + value.sha1 = readNullableString(in); + value.size = readNullableLong(in); + } + return value; + } + + @Override + public int fixedSize() { + return -1; + } + } +} diff --git a/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/MetricsInputStream.java b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/MetricsInputStream.java new file mode 100644 index 0000000000000000000000000000000000000000..b4124cead88c35c6e7ad7c4cdda1be9e7fc1a5b9 --- /dev/null +++ b/components/nexus-blobstore-file/src/main/java/org/sonatype/nexus/blobstore/file/internal/MetricsInputStream.java @@ -0,0 +1,72 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.FilterInputStream; +import java.io.InputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.sonatype.nexus.blobstore.file.FileOperations.StreamMetrics; + +import com.google.common.base.Throwables; +import com.google.common.io.BaseEncoding; +import com.google.common.io.CountingInputStream; + +/** + * A utility to collect metrics about the content of an input stream. + * + * @since 3.0 + */ +public class MetricsInputStream + extends FilterInputStream +{ + private final MessageDigest messageDigest; + + private final CountingInputStream countingInputStream; + + public MetricsInputStream(final InputStream input) { + this(new CountingInputStream(input), createSha1()); + } + + private MetricsInputStream(final CountingInputStream countingStream, final MessageDigest messageDigest) { + super(new DigestInputStream(countingStream, messageDigest)); + this.messageDigest = messageDigest; + this.countingInputStream = countingStream; + } + + private static final BaseEncoding HEX = BaseEncoding.base16().lowerCase(); + + public String getMessageDigest() { + return HEX.encode(messageDigest.digest()); + } + + public long getSize() { + return countingInputStream.getCount(); + } + + public StreamMetrics getMetrics() { + return new StreamMetrics(getSize(), getMessageDigest()); + } + + private static MessageDigest createSha1() { + try { + return MessageDigest.getInstance("SHA1"); + } + catch (NoSuchAlgorithmException e) { + // should never happen + throw Throwables.propagate(e); + } + } +} diff --git a/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreConcurrencyIT.java b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreConcurrencyIT.java new file mode 100644 index 0000000000000000000000000000000000000000..7e955a40c2c4f25dc2578073b170420993b05a0e --- /dev/null +++ b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreConcurrencyIT.java @@ -0,0 +1,208 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.security.NoSuchAlgorithmException; +import java.util.Queue; +import java.util.Random; +import java.util.concurrent.ConcurrentLinkedDeque; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.goodies.testsupport.concurrent.ConcurrentRunner; +import org.sonatype.goodies.testsupport.concurrent.ConcurrentTask; +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.blobstore.api.BlobStoreException; +import org.sonatype.nexus.blobstore.file.internal.FileBlobMetadataStoreImpl; +import org.sonatype.nexus.blobstore.file.internal.MetricsInputStream; + +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableMap; +import com.google.common.io.ByteStreams; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static com.google.common.io.ByteStreams.nullOutputStream; +import static org.sonatype.nexus.blobstore.api.BlobStore.BLOB_NAME_HEADER; +import static org.sonatype.nexus.blobstore.api.BlobStore.CREATED_BY_HEADER; + +/** + * {@link FileBlobStore} concurrency tests. + */ +public class FileBlobStoreConcurrencyIT + extends TestSupport +{ + public static final ImmutableMap TEST_HEADERS = ImmutableMap.of( + CREATED_BY_HEADER, "test", + BLOB_NAME_HEADER, "test/randomData.bin" + ); + + public static final int BLOB_MAX_SIZE_BYTES = 5_000_000; + + private FileBlobMetadataStore metadataStore; + + private FileBlobStore underTest; + + @Before + public void setUp() throws Exception { + Path root = util.createTempDir().toPath(); + Path content = root.resolve("content"); + Path metadata = root.resolve("metadata"); + + this.metadataStore = FileBlobMetadataStoreImpl.create(metadata.toFile()); + this.underTest = new FileBlobStore(content, new VolumeChapterLocationStrategy(), new SimpleFileOperations(), + metadataStore, new BlobStoreConfiguration()); + underTest.start(); + } + + @After + public void tearDown() throws Exception { + underTest.stop(); + } + + @Test + public void concurrencyTest() throws Exception { + + final Random random = new Random(); + + int numberOfCreators = 10; + int numberOfDeleters = 5; + int numberOfReaders = 30; + int numberOfCompactors = 1; + int numberOfShufflers = 3; + + final Queue blobIdsInTheStore = new ConcurrentLinkedDeque<>(); + + int numberOfIterations = 15; + int timeoutMinutes = 5; + final ConcurrentRunner runner = new ConcurrentRunner(numberOfIterations, timeoutMinutes * 60); + + runner.addTask(numberOfCreators, + new ConcurrentTask() + { + @Override + public void run() throws Exception { + final byte[] data = new byte[random.nextInt(BLOB_MAX_SIZE_BYTES) + 1]; + random.nextBytes(data); + final Blob blob = underTest.create(new ByteArrayInputStream(data), TEST_HEADERS); + + blobIdsInTheStore.add(blob.getId()); + } + }); + + runner.addTask(numberOfReaders, + new ConcurrentTask() + { + @Override + public void run() throws Exception { + final BlobId blobId = blobIdsInTheStore.peek(); + + log("Attempting to read " + blobId); + + if (blobId == null) { + return; + } + + final Blob blob = underTest.get(blobId); + if (blob == null) { + log("Attempted to obtain blob, but it was deleted:" + blobId); + return; + } + + try (InputStream inputStream = blob.getInputStream()) { + readContentAndValidateMetrics(blobId, inputStream, blob.getMetrics()); + } + catch (BlobStoreException e) { + // This is normal operation if another thread deletes your blob after you obtain a Blob reference + log("Concurrent deletion suspected while calling blob.getInputStream().", e); + } + } + } + ); + + runner.addTask(numberOfDeleters, + new ConcurrentTask() + { + @Override + public void run() throws Exception { + final BlobId blobId = blobIdsInTheStore.poll(); + if (blobId == null) { + log("deleter: null blob id"); + return; + } + underTest.delete(blobId); + } + } + ); + + // Shufflers pull blob IDs off the front of the queue and stick them on the back, to make the blobID queue a bit less orderly + runner.addTask(numberOfShufflers, + new ConcurrentTask() + { + @Override + public void run() throws Exception { + final BlobId blobId = blobIdsInTheStore.poll(); + if (blobId != null) { + blobIdsInTheStore.add(blobId); + } + } + }); + + + runner.addTask(numberOfCompactors, + new ConcurrentTask() + { + @Override + public void run() throws Exception { + underTest.compact(); + } + }); + + runner.go(); + } + + /** + * Read all the content from a blob, and compare it with the metrics on file in the blob store. + * + * @throws RuntimeException if there is any deviation + */ + private void readContentAndValidateMetrics(final BlobId blobId, + final InputStream inputStream, + final BlobMetrics metadataMetrics) + throws NoSuchAlgorithmException, IOException + { + final MetricsInputStream measured = new MetricsInputStream(inputStream); + ByteStreams.copy(measured, nullOutputStream()); + + checkEqual("stream length", metadataMetrics.getContentSize(), measured.getSize(), blobId); + checkEqual("SHA1 hash", metadataMetrics.getSHA1Hash(), measured.getMessageDigest(), blobId); + } + + private void checkEqual(final String propertyName, final Object expected, final Object measured, + final BlobId blobId) + { + if (!Objects.equal(measured, expected)) { + throw new RuntimeException( + "Blob " + blobId + "'s measured " + propertyName + " differed from its metadata. Expected " + expected + + " but was " + measured + "." + ); + } + } +} diff --git a/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreIT.java b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreIT.java new file mode 100644 index 0000000000000000000000000000000000000000..aaa4abf83debcbb6dd6f0ec02a6eaf9cdf316c23 --- /dev/null +++ b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreIT.java @@ -0,0 +1,142 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.Random; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.blobstore.api.BlobStoreMetrics; +import org.sonatype.nexus.blobstore.file.internal.FileBlobMetadataStoreImpl; + +import com.google.common.collect.ImmutableMap; +import com.google.common.io.ByteStreams; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.sonatype.nexus.blobstore.api.BlobStore.BLOB_NAME_HEADER; +import static org.sonatype.nexus.blobstore.api.BlobStore.CREATED_BY_HEADER; + +/** + * {@link FileBlobStore} integration tests. + */ +public class FileBlobStoreIT + extends TestSupport +{ + public static final int TEST_DATA_LENGTH = 10_000; + + public static final ImmutableMap TEST_HEADERS = ImmutableMap.of( + CREATED_BY_HEADER, "test", + BLOB_NAME_HEADER, "test/randomData.bin" + ); + + private FileBlobMetadataStore metadataStore; + + private FileBlobStore underTest; + + @Before + public void setUp() throws Exception { + Path root = util.createTempDir().toPath(); + Path content = root.resolve("content"); + Path metadata = root.resolve("metadata"); + + this.metadataStore = FileBlobMetadataStoreImpl.create(metadata.toFile()); + this.underTest = new FileBlobStore(content, new VolumeChapterLocationStrategy(), new SimpleFileOperations(), + metadataStore, new BlobStoreConfiguration()); + underTest.start(); + } + + @After + public void tearDown() throws Exception { + underTest.stop(); + } + + @Test + public void basicSmokeTest() throws Exception { + final byte[] content = new byte[TEST_DATA_LENGTH]; + new Random().nextBytes(content); + + final Blob blob = underTest.create(new ByteArrayInputStream(content), TEST_HEADERS); + + final byte[] output = extractContent(blob); + assertThat("data must survive", content, is(equalTo(output))); + + final BlobMetrics metrics = blob.getMetrics(); + assertThat("size must be calculated correctly", metrics.getContentSize(), is(equalTo((long) TEST_DATA_LENGTH))); + + final BlobStoreMetrics storeMetrics = underTest.getMetrics(); + assertThat(storeMetrics.getBlobCount(), is(equalTo(1L))); + + // FIXME: This is no longer valid + //assertThat(storeMetrics.getTotalSize(), is(equalTo((long) TEST_DATA_LENGTH))); + + assertThat(storeMetrics.getAvailableSpace(), is(greaterThan(0L))); + + final boolean deleted = underTest.delete(blob.getId()); + assertThat(deleted, is(equalTo(true))); + + final Blob deletedBlob = underTest.get(blob.getId()); + assertThat(deletedBlob, is(nullValue())); + + // Now that we've deleted the blob, there shouldn't be anything left + final BlobStoreMetrics storeMetrics2 = underTest.getMetrics(); + + // FIXME: This is no longer valid + //assertThat(storeMetrics2.getBlobCount(), is(equalTo(0L))); + //assertThat(storeMetrics2.getTotalSize(), is(equalTo((long) TEST_DATA_LENGTH))); + + underTest.compact(); + + final BlobStoreMetrics storeMetrics3 = underTest.getMetrics(); + + // FIXME: This is no longer valid + //assertThat("compacting should reclaim deleted blobs' space", storeMetrics3.getTotalSize(), is(equalTo(0L))); + } + + private byte[] extractContent(final Blob blob) throws IOException { + try (InputStream inputStream = blob.getInputStream()) { + return ByteStreams.toByteArray(inputStream); + } + } + + @Test + public void hardDeletePreventsGetDespiteOpenStreams() throws Exception { + final byte[] content = new byte[TEST_DATA_LENGTH]; + new Random().nextBytes(content); + + final Blob blob = underTest.create(new ByteArrayInputStream(content), TEST_HEADERS); + + final InputStream inputStream = blob.getInputStream(); + + // Read half the data + inputStream.read(new byte[content.length / 2]); + + // force delete + underTest.deleteHard(blob.getId()); + + final Blob newBlob = underTest.get(blob.getId()); + assertThat(newBlob, is(nullValue())); + } +} diff --git a/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreTest.java b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a50ad059d8c22a3dd159c4f13105d23fe27501a4 --- /dev/null +++ b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/FileBlobStoreTest.java @@ -0,0 +1,161 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.HashMap; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.blobstore.api.Blob; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.api.BlobStore; +import org.sonatype.nexus.blobstore.api.BlobStoreConfiguration; +import org.sonatype.nexus.blobstore.file.FileOperations.StreamMetrics; +import org.sonatype.nexus.common.collect.AutoClosableIterable; + +import com.google.common.collect.ImmutableMap; +import org.joda.time.DateTime; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Tests for {@link FileBlobStore}. + */ +public class FileBlobStoreTest + extends TestSupport +{ + private LocationStrategy locationStrategy; + + private FileOperations fileOps; + + private FileBlobMetadataStore metadataStore; + + private Path root; + + private FileBlobStore underTest; + + @Before + public void setUp() throws Exception { + locationStrategy = mock(LocationStrategy.class); + fileOps = mock(FileOperations.class); + metadataStore = mock(FileBlobMetadataStore.class); + + root = util.createTempDir().toPath(); + underTest = new FileBlobStore(root, locationStrategy, fileOps, metadataStore, new BlobStoreConfiguration()); + underTest.start(); + } + + @After + public void shutdown() throws Exception { + underTest.stop(); + } + + @Test(expected = IllegalArgumentException.class) + public void createRequiresHeaders() { + final ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[100]); + final HashMap headers = new HashMap<>(); + underTest.create(inputStream, headers); + } + + @Test + public void successfulCreation() throws Exception { + final BlobId fakeId = new BlobId("testId"); + final long contentSize = 200L; + final String fakeSHA1 = "3757y5abc234cfgg"; + final InputStream inputStream = mock(InputStream.class); + final ImmutableMap headers = ImmutableMap.of( + BlobStore.BLOB_NAME_HEADER, "my blob", + BlobStore.CREATED_BY_HEADER, "John did this" + ); + + when(metadataStore.add(any(FileBlobMetadata.class))).thenReturn(fakeId); + when(locationStrategy.location(fakeId)).thenReturn("fakePath"); + final Path fakePath = root.resolve("fakePath" + FileBlobStore.BLOB_CONTENT_SUFFIX); + when(fileOps.create(fakePath, inputStream)).thenReturn(new StreamMetrics(contentSize, fakeSHA1)); + + final Blob blob = underTest.create(inputStream, headers); + + final BlobMetrics metrics = blob.getMetrics(); + + assertThat(metrics.getSHA1Hash(), is(equalTo(fakeSHA1))); + assertThat(metrics.getContentSize(), is(equalTo(contentSize))); + + assertTrue("Creation time should be very recent", + metrics.getCreationTime().isAfter(new DateTime().minusSeconds(2))); + } + + @Test + public void getExistingBlob() throws Exception { + final BlobId fakeId = new BlobId("fakeId"); + final FileBlobMetadata metadata = mock(FileBlobMetadata.class); + when(metadataStore.get(fakeId)).thenReturn(metadata); + when(metadata.isAlive()).thenReturn(true); + when(metadata.getMetrics()).thenReturn(mock(BlobMetrics.class)); + + when(locationStrategy.location(fakeId)).thenReturn("fakePath"); + final Path fakePath = root.resolve("fakePath" + FileBlobStore.BLOB_CONTENT_SUFFIX); + when(fileOps.exists(fakePath)).thenReturn(true); + + when(fileOps.openInputStream(fakePath)).thenReturn(mock(InputStream.class)); + + final Blob blob = underTest.get(fakeId); + assertThat(blob, notNullValue()); + assertThat(blob.getId(), is(equalTo(fakeId))); + } + + @Test + public void deletingMarksAsDeleted() { + final BlobId fakeId = new BlobId("fakeId"); + final FileBlobMetadata metadata = mock(FileBlobMetadata.class); + when(metadataStore.get(fakeId)).thenReturn(metadata); + + // The blob isn't already deleted + when(metadata.isAlive()).thenReturn(true); + + final boolean deleted = underTest.delete(fakeId); + assertThat(deleted, is(equalTo(true))); + + verify(metadata).setBlobState(FileBlobState.MARKED_FOR_DELETION); + } + + @Test + public void secondDeletionRedundant() { + final BlobId fakeId = new BlobId("testId"); + final FileBlobMetadata metadata = mock(FileBlobMetadata.class); + when(metadataStore.get(fakeId)).thenReturn(metadata); + when(metadata.isAlive()).thenReturn(false); + + final boolean deleted = underTest.delete(fakeId); + assertThat(deleted, is(equalTo(false))); + } + + @Test + public void iteratorIsOnlyForAliveBlobs() { + final AutoClosableIterable iterator = underTest.iterator(); + verify(metadataStore).findWithState(FileBlobState.ALIVE); + } +} diff --git a/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/FileBlobMetadataStoreImplTest.java b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/FileBlobMetadataStoreImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..55a39ddb5cb047ba9e0a2beeec7b5210d3344623 --- /dev/null +++ b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/FileBlobMetadataStoreImplTest.java @@ -0,0 +1,161 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.File; +import java.util.List; + +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.blobstore.api.BlobId; +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.file.FileBlobMetadata; +import org.sonatype.nexus.blobstore.file.FileBlobMetadataStore; +import org.sonatype.nexus.blobstore.file.FileBlobState; +import org.sonatype.nexus.common.collect.AutoClosableIterable; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import org.joda.time.DateTime; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.emptyIterable; + +/** + * Tests for {@link FileBlobMetadataStoreImpl}. + */ +public class FileBlobMetadataStoreImplTest + extends TestSupport +{ + private FileBlobMetadataStore underTest; + + @Before + public void setUp() throws Exception { + File root = util.createTempDir("databases"); + File dir = new File(root, "test"); + this.underTest = FileBlobMetadataStoreImpl.create(dir); + underTest.start(); + } + + @After + public void tearDown() throws Exception { + if (underTest != null) { + underTest.stop(); + } + } + + /** + * Helper to find states and close iterable. + */ + private Iterable findWithState(final FileBlobState state) throws Exception { + List results = Lists.newArrayList(); + try (AutoClosableIterable iter = underTest.findWithState(state)) { + for (BlobId id : iter) { + results.add(id); + } + } + return results; + } + + @Test + public void stateTracking() throws Exception { + FileBlobMetadata md = new FileBlobMetadata(FileBlobState.CREATING, ImmutableMap.of("foo", "bar")); + BlobId id = underTest.add(md); + log("Added: {} -> {}", id, md); + + // states should only contain CREATING + assertThat(findWithState(FileBlobState.CREATING), contains(id)); + assertThat(findWithState(FileBlobState.ALIVE), emptyIterable()); + assertThat(findWithState(FileBlobState.MARKED_FOR_DELETION), emptyIterable()); + + md.setBlobState(FileBlobState.ALIVE); + underTest.update(id, md); + log("Updated: {} -> {}", id, md); + + // states should only contain ALIVE + assertThat(findWithState(FileBlobState.CREATING), emptyIterable()); + assertThat(findWithState(FileBlobState.ALIVE), contains(id)); + assertThat(findWithState(FileBlobState.MARKED_FOR_DELETION), emptyIterable()); + + md.setBlobState(FileBlobState.MARKED_FOR_DELETION); + underTest.update(id, md); + log("Updated: {} -> {}", id, md); + + // states should only contain marked for MARKED_FOR_DELETION + assertThat(findWithState(FileBlobState.CREATING), emptyIterable()); + assertThat(findWithState(FileBlobState.ALIVE), emptyIterable()); + assertThat(findWithState(FileBlobState.MARKED_FOR_DELETION), contains(id)); + + underTest.delete(id); + log("Deleted: {}", id); + + // states all be empty + assertThat(findWithState(FileBlobState.CREATING), emptyIterable()); + assertThat(findWithState(FileBlobState.ALIVE), emptyIterable()); + assertThat(findWithState(FileBlobState.MARKED_FOR_DELETION), emptyIterable()); + } + + @Test + public void basic() throws Exception { + FileBlobMetadata md = new FileBlobMetadata(FileBlobState.CREATING, ImmutableMap.of("foo", "bar")); + BlobMetrics test = new BlobMetrics(new DateTime(), "test", 1l); + md.setMetrics(test); + log(md); + + // add a record + log("add"); + BlobId id = underTest.add(md); + log(id); + + assertThat(underTest.getBlobSize(), is(1l)); + assertThat("Total should be size of metadata + size of blobs", + underTest.getTotalSize() - underTest.getMetadataSize(), is(1l)); + + dumpStates(); + + // update a record + log("update"); + md.setBlobState(FileBlobState.ALIVE); + underTest.update(id, md); + + dumpStates(); + + // fetch a record + log("fetch"); + FileBlobMetadata md2 = underTest.get(id); + log(md2); + + // delete a record + log("delete"); + underTest.delete(id); + + dumpStates(); + + // compact + log("compact"); + underTest.compact(); + } + + private void dumpStates() throws Exception { + for (FileBlobState state : FileBlobState.values()) { + log(state); + for (BlobId foundId : findWithState(state)) { + log(" {}", foundId); + } + } + } +} diff --git a/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MapdbTrial.java b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MapdbTrial.java new file mode 100644 index 0000000000000000000000000000000000000000..15cd3095cf202084938ffdd48e455b33245dcb38 --- /dev/null +++ b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MapdbTrial.java @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.File; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; +import org.mapdb.DB; +import org.mapdb.DBMaker; + +/** + * Trials of MapDB + */ +public class MapdbTrial + extends TestSupport +{ + @Test + public void createDatabase() throws Exception { + File file = util.createTempFile("db"); + DB db = DBMaker.newFileDB(file).make(); + try { + log("Database: {}, file: {}", db, file); + } + finally { + db.close(); + } + } +} diff --git a/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MetadataRecordSerializerImplTest.java b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MetadataRecordSerializerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..aa835e13c9779f875b506b29beaad3e37d5f64f7 --- /dev/null +++ b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MetadataRecordSerializerImplTest.java @@ -0,0 +1,90 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.sonatype.nexus.blobstore.api.BlobMetrics; +import org.sonatype.nexus.blobstore.file.FileBlobMetadata; +import org.sonatype.nexus.blobstore.file.FileBlobState; + +import com.google.common.collect.ImmutableMap; +import org.hamcrest.Matchers; +import org.joda.time.DateTime; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +/** + * Tests for {@link MetadataRecord.SerializerImpl}. + */ +public class MetadataRecordSerializerImplTest +{ + private MetadataRecord.SerializerImpl underTest; + + @Before + public void setUp() throws Exception { + underTest = new MetadataRecord.SerializerImpl(); + } + + @Test + public void roundTrip() throws Exception { + FileBlobMetadata blobMetadata = new FileBlobMetadata(FileBlobState.CREATING, ImmutableMap.of("Hi", "mom")); + blobMetadata.setMetrics(new BlobMetrics(new DateTime(), "pretend hash", 33434)); + + roundTrip(blobMetadata); + } + + @Test + public void roundTripWithEmptyObject() throws Exception { + Map headers = new HashMap<>(); + headers.put(null, null); + + FileBlobMetadata blobMetadata = new FileBlobMetadata(FileBlobState.CREATING, headers); + blobMetadata.setMetrics(new BlobMetrics(null, null, 0)); + + roundTrip(blobMetadata); + } + + private void roundTrip(final FileBlobMetadata blobMetadata) throws IOException { + MetadataRecord metadata = new MetadataRecord(blobMetadata); + + byte[] bytes = serialize(metadata); + MetadataRecord roundTripMetadata = deserialize(bytes); + + assertThat(roundTripMetadata, Matchers.is(equalTo(metadata))); + } + + private byte[] serialize(MetadataRecord metadata) throws IOException { + ByteArrayOutputStream buff = new ByteArrayOutputStream(); + try (DataOutputStream out = new DataOutputStream(buff)) { + underTest.serialize(out, metadata); + out.flush(); + return buff.toByteArray(); + } + } + + private MetadataRecord deserialize(final byte[] bytes) throws IOException { + try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes))) { + return underTest.deserialize(in, in.available()); + } + } +} diff --git a/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MetricsInputStreamTest.java b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MetricsInputStreamTest.java new file mode 100644 index 0000000000000000000000000000000000000000..77eae4e0b872ed1d220dea988fdd2c5bf7e07e09 --- /dev/null +++ b/components/nexus-blobstore-file/src/test/java/org/sonatype/nexus/blobstore/file/internal/MetricsInputStreamTest.java @@ -0,0 +1,65 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.blobstore.file.internal; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import org.sonatype.goodies.testsupport.TestSupport; + +import org.junit.Test; + +import static com.google.common.io.ByteStreams.copy; +import static com.google.common.io.ByteStreams.nullOutputStream; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +/** + * Tests for {@link MetricsInputStream}. + */ +public class MetricsInputStreamTest + extends TestSupport +{ + @Test + public void testLength() throws Exception { + assertThat(measure("ABC".getBytes("UTF-8")).getSize(), is(equalTo(3L))); + assertThat(measure(new byte[10000]).getSize(), is(equalTo(10000L))); + } + + @Test + public void testHashesDiffer() throws Exception { + final String hash1 = measure("ABC".getBytes("UTF-8")).getMessageDigest(); + final String hash2 = measure(new byte[10000]).getMessageDigest(); + + assertThat(hash1, not(equalTo(hash2))); + } + + @Test + public void referenceHashMatches() throws Exception { + final MetricsInputStream measure = measure( + getClass().getResourceAsStream("sha1_is_2589766c6dac3402cab552602d457e7e8af12efd.bytes")); + assertThat(measure.getMessageDigest(), is(equalTo("2589766c6dac3402cab552602d457e7e8af12efd"))); + } + + private MetricsInputStream measure(final byte[] testData) throws Exception { + return measure(new ByteArrayInputStream(testData)); + } + + private MetricsInputStream measure(final InputStream inputStream) throws Exception { + final MetricsInputStream metricStream = new MetricsInputStream(inputStream); + copy(metricStream, nullOutputStream()); + return metricStream; + } +} diff --git a/components/nexus-blobstore-file/src/test/resources/logback-test.xml b/components/nexus-blobstore-file/src/test/resources/logback-test.xml new file mode 100644 index 0000000000000000000000000000000000000000..d6343ca7b87eee66dd8475ca2cff16cc34a1a500 --- /dev/null +++ b/components/nexus-blobstore-file/src/test/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + System.out + + %date %level [%thread%X{DC}] %logger - %msg%n + + + + + + + + + + diff --git a/components/nexus-blobstore-file/src/test/resources/org/sonatype/nexus/blobstore/file/internal/sha1_is_2589766c6dac3402cab552602d457e7e8af12efd.bytes b/components/nexus-blobstore-file/src/test/resources/org/sonatype/nexus/blobstore/file/internal/sha1_is_2589766c6dac3402cab552602d457e7e8af12efd.bytes new file mode 100644 index 0000000000000000000000000000000000000000..917a774420784ebf148bf68bdf0760f1d4237193 Binary files /dev/null and b/components/nexus-blobstore-file/src/test/resources/org/sonatype/nexus/blobstore/file/internal/sha1_is_2589766c6dac3402cab552602d457e7e8af12efd.bytes differ diff --git a/components/nexus-bootstrap/pom.xml b/components/nexus-bootstrap/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..f2e0ac2d387f4010154251a2b1b223d7d2b7aed1 --- /dev/null +++ b/components/nexus-bootstrap/pom.xml @@ -0,0 +1,131 @@ + + + + 4.0.0 + + + org.sonatype.nexus + nexus-components + 3.0.0-SNAPSHOT + + + nexus-bootstrap + ${project.groupId}:${project.artifactId} + bundle + + + + org.slf4j + slf4j-api + + + + org.slf4j + jul-to-slf4j + true + + + + org.slf4j + jcl-over-slf4j + + + + org.slf4j + log4j-over-slf4j + + + + com.google.code.findbugs + jsr305 + true + + + + org.codehaus.plexus + plexus-interpolation + + + + org.osgi + org.osgi.core + true + + + + org.osgi + org.osgi.compendium + true + + + + org.eclipse.jetty + jetty-webapp + true + + + + org.eclipse.jetty + jetty-xml + true + + + + com.codahale.metrics + metrics-jetty9 + true + + + + com.codahale.metrics + metrics-logback + true + + + + org.apache.karaf.features + org.apache.karaf.features.core + true + + + + org.sonatype.goodies + goodies-testsupport + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + + org.sonatype.nexus.bootstrap.osgi.LauncherActivator + + + org.eclipse.jetty.*,ch.qos.logback.access.jetty + + + + + + + + diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ConfigurationBuilder.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ConfigurationBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..fac565328166989a0630e6b0ede707b660e89f9d --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ConfigurationBuilder.java @@ -0,0 +1,197 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; + +import org.codehaus.plexus.interpolation.EnvarBasedValueSource; +import org.codehaus.plexus.interpolation.Interpolator; +import org.codehaus.plexus.interpolation.MapBasedValueSource; +import org.codehaus.plexus.interpolation.StringSearchInterpolator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Helper to build bootstrap configuration properties. + * + * @since 2.8 + */ +public class ConfigurationBuilder +{ + private static final Logger log = LoggerFactory.getLogger(ConfigurationBuilder.class); + + private final PropertyMap properties = new PropertyMap(); + + public ConfigurationBuilder properties(final Map props) { + if (props == null) { + throw new NullPointerException(); + } + if (log.isDebugEnabled()) { + log.debug("Adding properties:"); + for (Entry entry : props.entrySet()) { + log.debug(" {}='{}'", entry.getKey(), entry.getValue()); + } + } + this.properties.putAll(props); + return this; + } + + public ConfigurationBuilder properties(final URL url) throws IOException { + if (url == null) { + throw new NullPointerException(); + } + log.debug("Reading properties from: {}", url); + PropertyMap props = new PropertyMap(); + props.load(url); + return properties(props); + } + + public ConfigurationBuilder properties(final String resource, final boolean required) throws IOException { + return properties(getClass(), resource, required); + } + + /** + * @since 3.0 + */ + public ConfigurationBuilder properties(final Class clazz, final String resource, final boolean required) throws IOException { + URL url = clazz.getResource(resource); + if (url == null) { + if (required) { + throw new IllegalStateException("Missing required resource: " + resource); + } + return this; + } + return properties(url); + } + + /** + * @since 3.0 + */ + public ConfigurationBuilder properties(final File resource, final boolean required) throws IOException { + if (resource == null || !resource.exists()) { + if (required) { + throw new IllegalStateException("Missing required resource: " + resource); + } + return this; + } + return properties(resource.toURI().toURL()); + } + + public ConfigurationBuilder defaults() throws IOException { + return properties("default.properties", true); + } + + public ConfigurationBuilder set(final String name, final String value) { + if (name == null) { + throw new NullPointerException(); + } + if (value == null) { + throw new NullPointerException(); + } + log.debug("Set: {}={}", name, value); + properties.put(name, value); + return this; + } + + /** + * Provides customization of configuration. + */ + public static interface Customizer + { + void apply(ConfigurationBuilder builder) throws Exception; + } + + public ConfigurationBuilder custom(final Customizer customizer) throws Exception { + if (customizer == null) { + throw new NullPointerException(); + } + log.debug("Customizing: {}", customizer); + customizer.apply(this); + return this; + } + + /** + * Override any existing properties with values from the given set of overrides. + * + * @since 2.8.1 + */ + public ConfigurationBuilder override(final Map overrides) { + if (overrides == null) { + throw new NullPointerException(); + } + for (Entry entry : overrides.entrySet()) { + String name = entry.getKey(); + if (properties.containsKey(name)) { + String value = entry.getValue(); + log.debug("Override: {}={}", name, value); + properties.put(name, value); + } + } + return this; + } + + /** + * @see #override(Map) + * + * @since 2.8.1 + */ + public ConfigurationBuilder override(final Properties overrides) { + return override(new PropertyMap(overrides)); + } + + private void canonicalize(final String name) throws IOException { + String value = properties.get(name); + if (value == null) { + log.warn("Unable to canonicalize null entry: {}", name); + return; + } + File file = new File(value).getCanonicalFile(); + properties.put(name, file.getPath()); + } + + private void interpolate() throws Exception { + Interpolator interpolator = new StringSearchInterpolator(); + interpolator.addValueSource(new MapBasedValueSource(properties)); + interpolator.addValueSource(new MapBasedValueSource(System.getProperties())); + interpolator.addValueSource(new EnvarBasedValueSource()); + + for (Entry entry : properties.entrySet()) { + properties.put(entry.getKey(), interpolator.interpolate(entry.getValue())); + } + } + + public Map build() throws Exception { + if (properties.isEmpty()) { + throw new IllegalStateException("Not configured"); + } + + interpolate(); + + // make some entries canonical + canonicalize("nexus-work"); + + // return copy + PropertyMap props = new PropertyMap(properties); + log.info("Properties:"); + for (String key : props.keys()) { + log.info(" {}='{}'", key, props.get(key)); + } + + return props; + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ConfigurationHolder.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ConfigurationHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..9f2edcbee82513988908652501093df1eadda972 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ConfigurationHolder.java @@ -0,0 +1,37 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap; + +import java.util.Map; + +/** + * Holder for bootstrap configuration properties. + * + * @since 2.8 + */ +public class ConfigurationHolder +{ + private static final InheritableThreadLocal> reference = new InheritableThreadLocal<>(); + + public static void set(final Map properties) { + reference.set(properties); + } + + public static Map get() { + return reference.get(); + } + + public static void unset() { + reference.remove(); + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/EnvironmentVariables.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/EnvironmentVariables.java new file mode 100644 index 0000000000000000000000000000000000000000..17d9feab0847e021c6ab3b21be4d535e7dd00297 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/EnvironmentVariables.java @@ -0,0 +1,60 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap; + +import org.sonatype.nexus.bootstrap.ConfigurationBuilder.Customizer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Support for customizing configuration from environment variables. + * + * @since 2.8 + */ +public class EnvironmentVariables + implements Customizer +{ + private static final Logger log = LoggerFactory.getLogger(EnvironmentVariables.class); + + @Override + public void apply(final ConfigurationBuilder builder) throws Exception { + // complain if we find any legacy environment variables + maybeSetLegacy(builder, "application-host", "PLEXUS_APPLICATION_HOST"); + maybeSetLegacy(builder, "application-port", "PLEXUS_APPLICATION_PORT"); + maybeSetLegacy(builder, "nexus-work", "PLEXUS_NEXUS_WORK"); + maybeSetLegacy(builder, "nexus-context-path", "PLEXUS_CONTEXT_PATH"); + + // non-legacy environment variables take precedence + maybeSet(builder, "application-host", "NEXUS_APPLICATION_HOST"); + maybeSet(builder, "application-port", "NEXUS_APPLICATION_PORT"); + maybeSet(builder, "nexus-work", "NEXUS_WORK"); + maybeSet(builder, "nexus-context-path", "NEXUS_CONTEXT_PATH"); + } + + private boolean maybeSet(final ConfigurationBuilder builder, final String property, final String env) { + String value = System.getenv(env); + if (value != null) { + log.debug("Environment variable: {}={}", env, value); + builder.set(property, value); + return true; + } + return false; + } + + private void maybeSetLegacy(final ConfigurationBuilder builder, final String property, final String env) { + if (maybeSet(builder, property, env)) { + log.warn("Detected legacy environment variable: {}", env); + } + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/Launcher.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/Launcher.java new file mode 100644 index 0000000000000000000000000000000000000000..6e450339fe3885a1f77de314ca41ca066c97611c --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/Launcher.java @@ -0,0 +1,234 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.logging.Handler; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.bootstrap.jetty.JettyServer; +import org.sonatype.nexus.bootstrap.monitor.CommandMonitorThread; +import org.sonatype.nexus.bootstrap.monitor.KeepAliveThread; +import org.sonatype.nexus.bootstrap.monitor.commands.ExitCommand; +import org.sonatype.nexus.bootstrap.monitor.commands.HaltCommand; +import org.sonatype.nexus.bootstrap.monitor.commands.PingCommand; +import org.sonatype.nexus.bootstrap.monitor.commands.StopApplicationCommand; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import static org.sonatype.nexus.bootstrap.monitor.CommandMonitorThread.LOCALHOST; +import static org.sonatype.nexus.bootstrap.monitor.KeepAliveThread.KEEP_ALIVE_PING_INTERVAL; +import static org.sonatype.nexus.bootstrap.monitor.KeepAliveThread.KEEP_ALIVE_PORT; +import static org.sonatype.nexus.bootstrap.monitor.KeepAliveThread.KEEP_ALIVE_TIMEOUT; + +/** + * Nexus bootstrap launcher. + * + * @since 2.1 + */ +public class Launcher +{ + static { + boolean hasJulBridge; + try { + // check whether we have access to the optional JUL->SLF4J logging bridge + hasJulBridge = Handler.class.isAssignableFrom(org.slf4j.bridge.SLF4JBridgeHandler.class); + } + catch (Exception | LinkageError e) { + hasJulBridge = false; + } + HAS_JUL_BRIDGE = hasJulBridge; + } + + private static final boolean HAS_JUL_BRIDGE; + + public static final String IGNORE_SHUTDOWN_HELPER = ShutdownHelper.class.getName() + ".ignore"; + + // FIXME: Move this to CommandMonitorThread + public static final String COMMAND_MONITOR_PORT = CommandMonitorThread.class.getName() + ".port"; + + public static final String SYSTEM_USERID = "*SYSTEM"; + + private static final String FIVE_SECONDS = "5000"; + + private static final String ONE_SECOND = "1000"; + + private final JettyServer server; + + public Launcher(final @Nullable ClassLoader classLoader, + final @Nullable Map overrides, + final String[] args) + throws Exception + { + this(null, classLoader, overrides, args); + } + + public Launcher(final @Nullable String basePath, + final @Nullable ClassLoader classLoader, + final @Nullable Map overrides, + final String[] args) + throws Exception + { + if (args == null || args.length == 0) { + throw new IllegalArgumentException("Missing args"); + } + + if (HAS_JUL_BRIDGE) { + org.slf4j.bridge.SLF4JBridgeHandler.removeHandlersForRootLogger(); + org.slf4j.bridge.SLF4JBridgeHandler.install(); + } + + File baseDir = new File(basePath == null ? "." : basePath).getCanonicalFile(); + ClassLoader cl = (classLoader == null) ? getClass().getClassLoader() : classLoader; + + ConfigurationBuilder builder = new ConfigurationBuilder().defaults(); + + builder.set("nexus-base", baseDir.getPath()); + if (basePath != null) { + // look for configuration relative to the base + builder.properties(new File(baseDir, "etc/org.sonatype.nexus.cfg"), true); + } + else { + // search the classpath for the configuration + builder.properties("/org.sonatype.nexus.cfg", true); + } + + builder.custom(new EnvironmentVariables()); + builder.override(System.getProperties()); + + if (overrides != null) { + // using properties() instead of override() so we get all values added, not just those with existing entries + builder.properties(overrides); + } + + Map props = builder.build(); + System.getProperties().putAll(props); + ConfigurationHolder.set(props); + + // log critical information about the runtime environment + Logger log = LoggerFactory.getLogger(Launcher.class); + log.info("Java: {}, {}, {}, {}", + System.getProperty("java.version"), + System.getProperty("java.vm.name"), + System.getProperty("java.vm.vendor"), + System.getProperty("java.vm.version") + ); + log.info("OS: {}, {}, {}", + System.getProperty("os.name"), + System.getProperty("os.version"), + System.getProperty("os.arch") + ); + log.info("User: {}, {}, {}", + System.getProperty("user.name"), + System.getProperty("user.language"), + System.getProperty("user.home") + ); + log.info("CWD: {}", System.getProperty("user.dir")); + + // ensure the temporary directory is sane + File tmpdir = TemporaryDirectory.get(); + log.info("TMP: {}", tmpdir); + + if (!"false".equalsIgnoreCase(getProperty(IGNORE_SHUTDOWN_HELPER, "false"))) { + log.warn("ShutdownHelper requests will be ignored!"); + ShutdownHelper.setDelegate(ShutdownHelper.NOOP); + } + + this.server = new JettyServer(cl, props, args); + } + + public JettyServer getServer() { + return server; + } + + public void start() throws Exception { + start(true, null); + } + + /** + * Starts Jetty without waiting for it to fully start up. + * + * @param callback optional, callback executed immediately after Jetty is fully started up. + * @see JettyServer#start(boolean, Runnable) + */ + public void startAsync(@Nullable final Runnable callback) throws Exception { + start(false, callback); + } + + private void start(boolean waitForServer, @Nullable final Runnable callback) throws Exception { + maybeEnableCommandMonitor(); + maybeEnableShutdownIfNotAlive(); + + server.start(waitForServer, callback); + } + + private String getProperty(final String name, final String defaultValue) { + String value = System.getProperty(name, System.getenv(name)); + if (value == null) { + value = defaultValue; + } + return value; + } + + private void maybeEnableCommandMonitor() throws IOException { + String port = getProperty(COMMAND_MONITOR_PORT, null); + if (port != null) { + new CommandMonitorThread( + Integer.parseInt(port), + new StopApplicationCommand(new Runnable() + { + @Override + public void run() { + Launcher.this.commandStop(); + } + }), + new PingCommand(), + new ExitCommand(), + new HaltCommand() + ).start(); + } + } + + private void maybeEnableShutdownIfNotAlive() throws IOException { + String port = getProperty(KEEP_ALIVE_PORT, null); + if (port != null) { + String pingInterval = getProperty(KEEP_ALIVE_PING_INTERVAL, FIVE_SECONDS); + String timeout = getProperty(KEEP_ALIVE_TIMEOUT, ONE_SECOND); + + new KeepAliveThread( + LOCALHOST, + Integer.parseInt(port), + Integer.parseInt(pingInterval), + Integer.parseInt(timeout) + ).start(); + } + } + + public void commandStop() { + ShutdownHelper.exit(0); + } + + public void stop() throws Exception { + server.stop(); + } + + public static void main(final String[] args) throws Exception { + MDC.put("userId", SYSTEM_USERID); + new Launcher(null, null, args).start(); + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/LockFile.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/LockFile.java new file mode 100644 index 0000000000000000000000000000000000000000..d14a7a3fdb83eecd84ade7e0b14abe3fb86107d5 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/LockFile.java @@ -0,0 +1,151 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.management.ManagementFactory; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; +import java.nio.charset.Charset; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * File locker implementation, inspired by Eclipse Locker. It uses Java NIO {@link FileChannel#tryLock(long, long, + * boolean)} method to perform locking. As a commodity function, it also writes to a file a payload, making problem + * diagnosing a bit easier, as reading (ie. from console) of the lock file content might reveal useful information + * about lock owner. + * All the limitations mentioned for {@link FileLock} stands. + * + * @since 2.7.0 + */ +public class LockFile +{ + private static final Logger log = LoggerFactory.getLogger(LockFile.class); + + private static final byte[] DEFAULT_PAYLOAD = ManagementFactory.getRuntimeMXBean().getName().getBytes( + Charset.forName("UTF-8")); + + private final File lockFile; + + private final byte[] payload; + + private FileLock fileLock; + + private RandomAccessFile randomAccessFile; + + /** + * Creates a LockFile with default payload (that contains the JVM name, usually {@code PID@hostname}). + */ + public LockFile(final File lockFile) { + this(lockFile, DEFAULT_PAYLOAD); + } + + /** + * Creates a LockFile with custom payload. + */ + public LockFile(final File lockFile, final byte[] payload) { + if (lockFile == null || payload == null) { + throw new NullPointerException(); + } + this.lockFile = lockFile; + this.payload = payload; + } + + /** + * Returns the file used by this instance. + */ + public File getFile() { + return lockFile; + } + + /** + * Returns the payload used by this instance. + */ + public byte[] getPayload() { + return payload; + } + + /** + * Performs locking. If returns {@code true}, locking was successful and caller holds the lock. Multiple invocations, + * after lock is acquired, does not have any effect, locking happens only once. + */ + public synchronized boolean lock() { + if (fileLock != null) { + return true; + } + try { + randomAccessFile = new RandomAccessFile(lockFile, "rws"); + fileLock = randomAccessFile.getChannel().tryLock(0L, 1L, false); + if (fileLock != null) { + randomAccessFile.setLength(0); + randomAccessFile.seek(0); + randomAccessFile.write(payload); + } + } + catch (IOException | OverlappingFileLockException e) { + log.warn("Failed to write lock file", e); + // handle it as null result + fileLock = null; + } + finally { + if (fileLock == null) { + release(); + return false; + } + } + return true; + } + + /** + * Releases the lock. Multiple invocations of this file are possible, release will happen only once. + */ + public synchronized void release() { + close(fileLock); + fileLock = null; + close(randomAccessFile); + randomAccessFile = null; + } + + /** + * Reads the contents of the lock file for confirmation purposes; only call this method when a lock has been + * obtained. Package-scoped as this is only used by tests. + */ + byte[] readBytes() throws IOException { + if (randomAccessFile == null) { + throw new IllegalStateException("No lock obtained, cannot read file contents."); + } + + byte[] buffer = new byte[(int) randomAccessFile.length()]; + randomAccessFile.seek(0); + randomAccessFile.read(buffer, 0, buffer.length); + return buffer; + } + + // == + + private static void close(AutoCloseable closeable) { + if (closeable != null) { + try { + closeable.close(); + } + catch (Exception e) { + // muted + } + } + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/PropertyMap.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/PropertyMap.java new file mode 100644 index 0000000000000000000000000000000000000000..1aa689e8cba221b403444715454a93d6fe2bf9d2 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/PropertyMap.java @@ -0,0 +1,84 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +// TODO Copy this to goodies-common, though has to be duplicated here + +/** + * Properties-like map with appropriate generics signature. + * + * @since 2.8 + */ +public class PropertyMap + extends HashMap +{ + public PropertyMap() { + super(); + } + + public PropertyMap(final Map map) { + super(map); + } + + /** + * @since 2.8.1 + */ + public PropertyMap(final Properties properties) { + putAll(properties); + } + + public void putAll(final Properties props) { + for (Object key : props.keySet()) { + put(key.toString(), String.valueOf(props.get(key))); + } + } + + public String get(final String key, final String defaultValue) { + String value = super.get(key); + if (value == null) { + return defaultValue; + } + return value; + } + + public void load(final InputStream input) throws IOException { + Properties p = new Properties(); + p.load(input); + putAll(p); + } + + public void load(final URL url) throws IOException { + try (InputStream input = url.openStream()) { + load(input); + } + } + + /** + * Returns list of sorted keys. + */ + public List keys() { + List keys = new ArrayList<>(keySet()); + Collections.sort(keys); + return Collections.unmodifiableList(keys); + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ShutdownHelper.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ShutdownHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..b17ada5e7b27be6635ec96aac5fc0ce642b02853 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/ShutdownHelper.java @@ -0,0 +1,89 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Helper to cope with different mechanisms to shutdown. + * + * @since 2.2 + */ +public class ShutdownHelper +{ + private static final Logger log = LoggerFactory.getLogger(ShutdownHelper.class); + + public static interface ShutdownDelegate + { + void doExit(int code); + + void doHalt(int code); + } + + public static class JavaShutdownDelegate + implements ShutdownDelegate + { + @Override + public void doExit(final int code) { + System.exit(code); + } + + @Override + public void doHalt(final int code) { + Runtime.getRuntime().halt(code); + } + } + + public static class NoopShutdownDelegate + implements ShutdownDelegate + { + @Override + public void doExit(final int code) { + log.warn("Ignoring exit({}) request", code); + } + + @Override + public void doHalt(final int code) { + log.warn("Ignoring halt({}) request", code); + } + } + + public static final ShutdownDelegate JAVA = new JavaShutdownDelegate(); + + public static final ShutdownDelegate NOOP = new NoopShutdownDelegate(); + + private static ShutdownDelegate delegate = JAVA; + + public static ShutdownDelegate getDelegate() { + if (delegate == null) { + throw new IllegalStateException(); + } + return delegate; + } + + public static void setDelegate(final ShutdownDelegate delegate) { + if (delegate == null) { + throw new NullPointerException(); + } + ShutdownHelper.delegate = delegate; + } + + public static void exit(final int code) { + getDelegate().doExit(code); + } + + public static void halt(final int code) { + getDelegate().doHalt(code); + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/TemporaryDirectory.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/TemporaryDirectory.java new file mode 100644 index 0000000000000000000000000000000000000000..a42c753045ce5cfb97c11b005d395fa3c2800471 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/TemporaryDirectory.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Helper to ensure temporary directory is sane. + * + * @since 2.8 + */ +public class TemporaryDirectory +{ + public static final String PROPERTY = "java.io.tmpdir"; + + // NOTE: Do not reset the system property, we can not ensure this value will be used + + public static File get() throws IOException { + String location = System.getProperty(PROPERTY, "tmp"); + File dir = new File(location).getCanonicalFile(); + mkdir(dir); + + // ensure we can create temporary files in this directory + Path file = Files.createTempFile("nexus-tmpdir", ".tmp"); + Files.delete(file); + return dir; + } + + private static void mkdir(final File dir) throws IOException { + try { + Files.createDirectories(dir.toPath()); + } + catch (FileAlreadyExistsException e) { + // handle symlink case + if (!Files.isDirectory(dir.toPath())) { + throw new IOException("Unable to create java.io.tmpdir: " + dir, e); + } + } + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorConfiguration.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..30e35f6c3ae5eab06c3d31ffe76ad43fd486eded --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorConfiguration.java @@ -0,0 +1,40 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.server.HttpConfiguration; + +/** + * Connector configuration should be registered as service by plugin requesting dedicated connectors, and unregistered + * once the connector is not needed. + * + * @since 3.0 + */ +public interface ConnectorConfiguration +{ + /** + * The required connector scheme. + */ + HttpScheme getScheme(); + + /** + * The required connector port. + */ + int getPort(); + + /** + * Allows implementation to customize the default configuration for needed connector or replace it completely. + */ + HttpConfiguration customize(HttpConfiguration configuration); +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorManager.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorManager.java new file mode 100644 index 0000000000000000000000000000000000000000..29e4b8f3035fe94d9d613ebe34f37f2089a3e955 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorManager.java @@ -0,0 +1,268 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static java.util.Collections.unmodifiableList; + +/** + * {@link JettyServer}'s internal connector manager to manage custom {@link ConnectorConfiguration}s. + *

+ * Note: This class relies on "id" properties in Jetty XML configuration! Hence, the existing beans in Jetty XML + * should NOT be renamed, while adding new (with unique, non-clashing) IDs is okay thing to do. + */ +public class ConnectorManager +{ + private static final Logger log = LoggerFactory.getLogger(ConnectorManager.class); + + private static final String HTTP_CONFIG_ID = "httpConfig"; + + private static final String HTTP_CONNECTOR_ID = "httpConnector"; + + private static final String SSL_CONTEXT_FACTORY_ID = "sslContextFactory"; + + private static final String HTTPS_CONFIG_ID = "httpsConfig"; + + private static final String HTTPS_CONNECTOR_ID = "httpsConnector"; + + private final Server server; + + private final Map namedBeans; + + private final IdentityHashMap managedConnectors; + + private final List defaultConnectors; + + public ConnectorManager(final Server server, + final Map namedBeans) + { + this.server = server; + this.namedBeans = namedBeans; + this.managedConnectors = new IdentityHashMap<>(); + this.defaultConnectors = unmodifiableList(buildDefaultConnectors()); + } + + public List defaultConnectors() { + return defaultConnectors; + } + + /** + * Adds and starts connector to Jetty based on passed in configuration. + */ + public ServerConnector addConnector(final ConnectorConfiguration connectorConfiguration) + { + final HttpScheme httpScheme = connectorConfiguration.getScheme(); + verifyConfiguration(httpScheme); + + final HttpConfiguration httpConfiguration = + connectorConfiguration.customize(defaultHttpConfiguration(httpScheme)); + final ServerConnector connectorPrototype = + defaultConnector(httpScheme); + final ServerConnector serverConnector; + if (HttpScheme.HTTP == httpScheme) { + serverConnector = new ServerConnector( + server, + connectorPrototype.getAcceptors(), + connectorPrototype.getSelectorManager().getSelectorCount(), + new InstrumentedConnectionFactory( + new HttpConnectionFactory(httpConfiguration) + ) + ); + } + else if (HttpScheme.HTTPS == httpScheme) { + final SslContextFactory sslContextFactory = bean(SSL_CONTEXT_FACTORY_ID, SslContextFactory.class); + serverConnector = new ServerConnector( + server, + connectorPrototype.getAcceptors(), + connectorPrototype.getSelectorManager().getSelectorCount(), + new InstrumentedConnectionFactory( + new SslConnectionFactory(sslContextFactory, "http/1.1") + ), + new HttpConnectionFactory(httpConfiguration) + ); + } + else { + throw new UnsupportedHttpSchemeException(httpScheme); + } + serverConnector.setHost(connectorPrototype.getHost()); + serverConnector.setPort(connectorConfiguration.getPort()); + serverConnector.setIdleTimeout(connectorPrototype.getIdleTimeout()); + serverConnector.setSoLingerTime(connectorPrototype.getSoLingerTime()); + serverConnector.setAcceptorPriorityDelta(connectorPrototype.getAcceptorPriorityDelta()); + serverConnector.setSelectorPriorityDelta(connectorPrototype.getSelectorPriorityDelta()); + serverConnector.setAcceptQueueSize(connectorPrototype.getAcceptQueueSize()); + + managedConnectors.put(connectorConfiguration, serverConnector); + server.addConnector(serverConnector); + try { + serverConnector.start(); + } + catch (Exception e) { + log.warn("Could not start connector: {}", connectorConfiguration, e); + throw new RuntimeException(e); // NOSONAR: no guava in scope + } + + return serverConnector; + } + + /** + * Stops and removes the connector configuration from Jetty. + */ + public void removeConnector(final ConnectorConfiguration connectorConfiguration) { + final ServerConnector serverConnector = managedConnectors.remove(connectorConfiguration); + if (serverConnector != null) { + try { + serverConnector.stop(); + } + catch (Exception e) { + log.warn("Could not stop connector: {}", connectorConfiguration, e); + throw new RuntimeException(e); // NOSONAR: no guava in scope + } + server.removeConnector(serverConnector); + } + } + + // == + + /** + * Verifies all the needed bits are present in Jetty XML configuration (as HTTPS must be enabled by users). + */ + private void verifyConfiguration(final HttpScheme httpScheme) { + try { + if (HttpScheme.HTTP == httpScheme) { + bean(HTTP_CONFIG_ID, HttpConfiguration.class); + bean(HTTP_CONNECTOR_ID, ServerConnector.class); + } + else if (HttpScheme.HTTPS == httpScheme) { + bean(SSL_CONTEXT_FACTORY_ID, SslContextFactory.class); + bean(HTTPS_CONFIG_ID, HttpConfiguration.class); + bean(HTTPS_CONNECTOR_ID, ServerConnector.class); + } + else { + throw new UnsupportedHttpSchemeException(httpScheme); + } + } + catch (IllegalStateException e) { + throw new IllegalStateException("Jetty HTTPS is not enabled in Nexus", e); + } + } + + private List buildDefaultConnectors() { + final List result = new ArrayList<>(); + try { + verifyConfiguration(HttpScheme.HTTP); + final int port = defaultConnector(HttpScheme.HTTP).getPort(); + result.add(new ConnectorConfiguration() + { + @Override + public HttpScheme getScheme() { + return HttpScheme.HTTP; + } + + @Override + public int getPort() { + return port; + } + + @Override + public HttpConfiguration customize(final HttpConfiguration configuration) { + return configuration; + } + }); + } + catch (IllegalStateException e) { + log.debug("No HTTP configuration present", e); + } + try { + verifyConfiguration(HttpScheme.HTTPS); + final int port = defaultConnector(HttpScheme.HTTPS).getPort(); + result.add(new ConnectorConfiguration() + { + @Override + public HttpScheme getScheme() { + return HttpScheme.HTTPS; + } + + @Override + public int getPort() { + return port; + } + + @Override + public HttpConfiguration customize(final HttpConfiguration configuration) { + return configuration; + } + }); + } + catch (IllegalStateException e) { + log.debug("No HTTPS configuration present", e); + } + return result; + } + + /** + * Returns the OOTB defined configuration for given HTTP scheme. + */ + private HttpConfiguration defaultHttpConfiguration(final HttpScheme httpScheme) { + if (HttpScheme.HTTP == httpScheme) { + return bean(HTTP_CONFIG_ID, HttpConfiguration.class); + } + else if (HttpScheme.HTTPS == httpScheme) { + return bean(HTTPS_CONFIG_ID, HttpConfiguration.class); + } + else { + throw new UnsupportedHttpSchemeException(httpScheme); + } + } + + /** + * Returns the OOTB defined connector for given HTTP scheme. + */ + private ServerConnector defaultConnector(final HttpScheme httpScheme) { + if (HttpScheme.HTTP == httpScheme) { + return bean(HTTP_CONNECTOR_ID, ServerConnector.class); + } + else if (HttpScheme.HTTPS == httpScheme) { + return bean(HTTPS_CONNECTOR_ID, ServerConnector.class); + } + else { + throw new UnsupportedHttpSchemeException(httpScheme); + } + } + + /** + * Gets a bean for Jetty XML configuration by name, casted to given type. + */ + private T bean(final String name, final Class clazz) { + final Object bean = namedBeans.get(name); + if (bean == null) { + throw new IllegalStateException( + "Jetty XML configuration does not contain bean with name: " + name + ", type=" + clazz.getName()); + } + return clazz.cast(bean); + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorRegistrar.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorRegistrar.java new file mode 100644 index 0000000000000000000000000000000000000000..c960e8861c7a919bf213483bbae3ff9604f83451 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/ConnectorRegistrar.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import java.util.List; + +import org.eclipse.jetty.http.HttpScheme; + +/** + * Connector registrar component. + * + * @since 3.0 + */ +public interface ConnectorRegistrar +{ + /** + * Returns the list of available HTTP schemes. This list depends on current Jetty XML configuration (is HTTPS + * enabled). If schema is not available, method {@link #addConnector(ConnectorConfiguration)} will reject to add it. + */ + List availableSchemes(); + + /** + * Returns the list of already occupied ports. If configuration port is occupied, method {@link + * #addConnector(ConnectorConfiguration)} will reject to add configuration. + */ + List unavailablePorts(); + + /** + * Registers a new Jetty {@link ConnectorConfiguration}, that creates a new connector on Jetty based on passed + * in configuration. + * + * @throws UnsupportedHttpSchemeException if requested HTTP schema is not available. + * @throws IllegalArgumentException requested port is not available, if current connectorConfiguration instance + * was already added + */ + void addConnector(ConnectorConfiguration connectorConfiguration); + + /** + * Removes a Jetty {@link ConnectorConfiguration} by closing and removing the Jetty connector. Caller MUST use the + * same instance of {@link ConnectorConfiguration} that was used with {@link #addConnector(ConnectorConfiguration)} + * method to create a connector in the first place. + */ + void removeConnector(ConnectorConfiguration connectorConfiguration); +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedConnectionFactory.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedConnectionFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e90c6ef48dafebab017292f0feb25c429374bd77 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedConnectionFactory.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import java.util.List; + +import com.codahale.metrics.SharedMetricRegistries; +import org.eclipse.jetty.server.ConnectionFactory; + +/** + * Extension of {@link com.codahale.metrics.jetty9.InstrumentedConnectionFactory}. + * + * @since 3.0 + */ +public final class InstrumentedConnectionFactory + extends com.codahale.metrics.jetty9.InstrumentedConnectionFactory +{ + private final ConnectionFactory connectionFactory; + + public InstrumentedConnectionFactory(final ConnectionFactory connectionFactory) { + super(connectionFactory, SharedMetricRegistries.getOrCreate("nexus").timer("connection-duration")); + this.connectionFactory = connectionFactory; + } + + // HACK: metrics-jetty9 (presently) is based on jetty 9.2, but we have to add more api for jetty 9.3 + + @Override + public List getProtocols() { + return connectionFactory.getProtocols(); + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedHandler.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..a0f20ca9f8d63a19179e205aa5912e56533e69ad --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedHandler.java @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import com.codahale.metrics.SharedMetricRegistries; +import org.eclipse.jetty.server.Handler; + +/** + * Extension of {@link com.codahale.metrics.jetty9.InstrumentedHandler} that restores the delegate constructor. + * + * @since 3.0 + */ +public final class InstrumentedHandler + extends com.codahale.metrics.jetty9.InstrumentedHandler +{ + public InstrumentedHandler(Handler delegate) { + super(SharedMetricRegistries.getOrCreate("nexus")); + setHandler(delegate); + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedQueuedThreadPool.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedQueuedThreadPool.java new file mode 100644 index 0000000000000000000000000000000000000000..9a2e5a1220def46ce3a1fd7c579f3a130fb51501 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/InstrumentedQueuedThreadPool.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import com.codahale.metrics.SharedMetricRegistries; + +/** + * Extension of {@link com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool} that restores the default constructor. + * + * @since 3.0 + */ +public final class InstrumentedQueuedThreadPool + extends com.codahale.metrics.jetty9.InstrumentedQueuedThreadPool +{ + public InstrumentedQueuedThreadPool() { + super(SharedMetricRegistries.getOrCreate("nexus")); + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/JettyServer.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/JettyServer.java new file mode 100644 index 0000000000000000000000000000000000000000..9aa8542f7999f20b42e791cdb6434a34d834fa0b --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/JettyServer.java @@ -0,0 +1,363 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import java.net.URL; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import javax.annotation.Nullable; + +import org.sonatype.nexus.bootstrap.PropertyMap; +import org.sonatype.nexus.bootstrap.ShutdownHelper; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandler.Context; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.xml.XmlConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Jetty server. + * + * @since 2.8 + */ +public class JettyServer +{ + private static final Logger log = LoggerFactory.getLogger(JettyServer.class); + + private final ClassLoader classLoader; + + private final Map properties; + + private final String[] args; + + private JettyMainThread thread; + + private ConnectorManager connectorManager; + + public JettyServer(final ClassLoader classLoader, final Map properties, final String[] args) { + if (classLoader == null) { + throw new NullPointerException(); + } + this.classLoader = classLoader; + + if (properties == null) { + throw new NullPointerException(); + } + this.properties = properties; + + if (args == null) { + throw new NullPointerException(); + } + this.args = args; + } + + private Exception propagateThrowable(final Throwable e) throws Exception { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + else if (e instanceof Exception) { + throw (Exception) e; + } + else if (e instanceof Error) { + throw (Error) e; + } + throw new Error(e); + } + + public List defaultConnectors() { + return connectorManager.defaultConnectors(); + } + + public void addCustomConnector(final ConnectorConfiguration connectorConfiguration) { + connectorManager.addConnector(connectorConfiguration); + } + + public void removeCustomConnector(final ConnectorConfiguration connectorConfiguration) { + connectorManager.removeConnector(connectorConfiguration); + } + + /** + * Starts Jetty, in sync or async mode, depending on value of {@code waitForServer} parameter. + * + * @param waitForServer if {@code true}, method will block until Jetty is fully started, otherwise will + * return immediately. + * @param callback optional, callback executed immediately after Jetty is fully started up. + */ + public synchronized void start(final boolean waitForServer, @Nullable final Runnable callback) throws Exception { + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(classLoader); + + try { + final AtomicReference exception = new AtomicReference<>(); + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() { + try { + doStart(waitForServer, callback); + } + catch (Exception e) { + exception.set(e); + } + return null; + } + }); + + Throwable e = exception.get(); + if (e != null) { + log.error("Start failed", e); + throw propagateThrowable(e); + } + } + finally { + Thread.currentThread().setContextClassLoader(cl); + } + } + + private void doStart(boolean waitForServer, @Nullable final Runnable callback) throws Exception { + if (thread != null) { + throw new IllegalStateException("Already started"); + } + + log.info("Starting"); + + List components = new ArrayList<>(); + + PropertyMap props = new PropertyMap(); + props.putAll(JettyServer.this.properties); + + // For all arguments, load properties or parse XMLs + XmlConfiguration last = null; + for (String arg : args) { + URL url = Resource.newResource(arg).getURL(); + + if (url.getFile().toLowerCase(Locale.ENGLISH).endsWith(".properties")) { + log.info("Loading properties: {}", url); + + props.load(url); + } + else { + log.info("Applying configuration: {}", url); + + XmlConfiguration configuration = new XmlConfiguration(url); + if (last != null) { + configuration.getIdMap().putAll(last.getIdMap()); + } + if (!props.isEmpty()) { + configuration.getProperties().putAll(props); + } + Object component = configuration.configure(); + if (component instanceof LifeCycle) { + components.add((LifeCycle) component); + } + last = configuration; + } + } + + // complain if no components configured + if (components.isEmpty()) { + throw new Exception("Failed to configure any components"); + } + + Server server = null; + for (Object object : components) { + if (object instanceof Server) { + server = (Server) object; + break; + } + } + + connectorManager = new ConnectorManager(server, last.getIdMap()); + + thread = new JettyMainThread(components, callback); + thread.setContextClassLoader(classLoader); + thread.startComponents(waitForServer); + } + + public synchronized void stop() throws Exception { + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(classLoader); + + try { + final AtomicReference exception = new AtomicReference<>(); + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() { + try { + doStop(); + } + catch (Exception e) { + exception.set(e); + } + return null; + } + }); + + Throwable e = exception.get(); + if (e != null) { + log.error("Stop failed", e); + throw propagateThrowable(e); + } + } + finally { + Thread.currentThread().setContextClassLoader(cl); + } + } + + private void doStop() throws Exception { + if (thread == null) { + throw new IllegalStateException("Not started"); + } + + log.info("Stopping"); + + thread.stopComponents(); + connectorManager = null; + thread = null; + + log.info("Stopped"); + } + + /** + * Jetty thread used to start components, wait for the server's threads to join and stop components. + * + * Needed so that once {@link JettyServer#stop()} returns that we know that the server has actually stopped, + * which is required for embedding. + */ + private static class JettyMainThread + extends Thread + { + private static final AtomicInteger INSTANCE_COUNTER = new AtomicInteger(1); + + private final List components; + + private final Runnable callback; + + private final CountDownLatch started; + + private final CountDownLatch stopped; + + private volatile Exception exception; + + public JettyMainThread(final List components, @Nullable final Runnable callback) { + super("jetty-main-" + INSTANCE_COUNTER.getAndIncrement()); + this.components = components; + this.callback = callback; + this.started = new CountDownLatch(1); + this.stopped = new CountDownLatch(1); + } + + @Override + public void run() { + try { + Server server = null; + try { + for (LifeCycle component : components) { + if (!component.isRunning()) { + log.info("Starting: {}", component); + component.start(); + } + + // capture the server reference + if (component instanceof Server) { + server = (Server) component; + } + } + } + catch (Exception e) { + exception = e; + } + finally { + started.countDown(); + } + + if (server != null) { + logStartupBanner(server); + if (callback != null) { + callback.run(); + } + server.join(); + } + else { + log.error("Failed to start", exception); + ShutdownHelper.exit(-1); + } + } + catch (InterruptedException e) { + // nothing + } + finally { + stopped.countDown(); + } + } + + public void startComponents(boolean waitForServer) throws Exception { + start(); + + if (waitForServer) { + started.await(); + } + + if (exception != null) { + throw exception; + } + } + + public void stopComponents() throws Exception { + Collections.reverse(components); + + // if Jetty thread is still waiting for a component to start, this should unblock it + interrupt(); + + for (LifeCycle component : components) { + if (component.isRunning()) { + log.info("Stopping: {}", component); + component.stop(); + } + } + + components.clear(); + stopped.await(); + } + + private static void logStartupBanner(Server server) { + Object banner = null; + + ContextHandler contextHandler = server.getChildHandlerByClass(ContextHandler.class); + if (contextHandler != null) { + Context context = contextHandler.getServletContext(); + if (context != null) { + banner = context.getAttribute("nexus-banner"); + } + } + + StringBuilder buf = new StringBuilder(); + buf.append("\n-------------------------------------------------\n\n"); + buf.append("Started ").append(banner instanceof String ? banner : "Sonatype Nexus"); + buf.append("\n\n-------------------------------------------------"); + log.info(buf.toString()); + } + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/JettyServerConfiguration.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/JettyServerConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..a12613890eb59944c9653ca42dd10b0ccaf83fe8 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/JettyServerConfiguration.java @@ -0,0 +1,33 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import java.util.List; + +/** + * Jetty server default configuration. + * + * @since 3.0 + */ +public class JettyServerConfiguration +{ + private final List defaultConnectors; + + public JettyServerConfiguration(final List defaultConnectors) { + this.defaultConnectors = defaultConnectors; + } + + public List defaultConnectors() { + return defaultConnectors; + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/UnsupportedHttpSchemeException.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/UnsupportedHttpSchemeException.java new file mode 100644 index 0000000000000000000000000000000000000000..85ddc45db0dd545b96762cbc98da2f689a41934b --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/jetty/UnsupportedHttpSchemeException.java @@ -0,0 +1,30 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.jetty; + +import org.eclipse.jetty.http.HttpScheme; + +public class UnsupportedHttpSchemeException + extends IllegalArgumentException +{ + private final HttpScheme httpScheme; + + public UnsupportedHttpSchemeException(final HttpScheme httpScheme) { + super("Unsupported HTTP Scheme: " + httpScheme); + this.httpScheme = httpScheme; + } + + public HttpScheme getHttpScheme() { + return httpScheme; + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/LogProxy.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/LogProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..f9a08da49ab42c2238e9d7e31711a9f9447a2296 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/LogProxy.java @@ -0,0 +1,53 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.log; + +/** + * A log proxy allowing redirecting output (e.g. in case that there is no slf4j available). + * + * @since 2.2 + */ +public class LogProxy +{ + + public void debug(final String message, Object... args) { + // does nothing + } + + public void info(final String message, final Object... args) { + // does nothing + } + + public void error(final String message, Object... args) { + // does nothing + } + + public void error(final String message, Throwable e) { + // does nothing + } + + public void warn(final String message, Object... args) { + // does nothing + } + + public static LogProxy getLogger(final Class clazz) { + try { + LogProxy.class.getClassLoader().loadClass("org.slf4j.Logger"); + return new Slf4jLogProxy(clazz); + } + catch (ClassNotFoundException e) { + return new SystemOutLogProxy(clazz); + } + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/Slf4jLogProxy.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/Slf4jLogProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..9d20b1c67ed89ba0cf24d358c0d50f86461f8c51 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/Slf4jLogProxy.java @@ -0,0 +1,62 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.log; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Logs to SLF4J. + * + * @since 2.2 + */ +public class Slf4jLogProxy + extends LogProxy +{ + + private Logger log = LoggerFactory.getLogger(getClass()); + + public Slf4jLogProxy(final Logger log) { + this.log = log; + } + + public Slf4jLogProxy(final Class clazz) { + this(LoggerFactory.getLogger(clazz)); + } + + @Override + public void debug(final String message, Object... args) { + log.debug(message, args); + } + + @Override + public void info(final String message, final Object... args) { + log.info(message, args); + } + + @Override + public void error(final String message, Object... args) { + log.error(message, args); + } + + @Override + public void error(final String message, Throwable e) { + log.error(message, e); + } + + @Override + public void warn(final String message, Object... args) { + log.warn(message, args); + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/SystemOutLogProxy.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/SystemOutLogProxy.java new file mode 100644 index 0000000000000000000000000000000000000000..573722ef31cc158b22fc8a351f26d20e6b276fa0 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/log/SystemOutLogProxy.java @@ -0,0 +1,67 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.log; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Logs to System.out. + * + * @since 2.2 + */ +public class SystemOutLogProxy + extends LogProxy +{ + + private Class clazz; + + public SystemOutLogProxy(final Class clazz) { + this.clazz = clazz; + } + + @Override + public void debug(final String message, Object... args) { + message("DEBUG", message, args); + } + + @Override + public void info(final String message, final Object... args) { + message("INFO", message, args); + } + + @Override + public void error(final String message, final Throwable e) { + error(message); + e.printStackTrace(System.out); + } + + @Override + public void error(final String message, Object... args) { + message("ERROR", message, args); + } + + @Override + public void warn(final String message, Object... args) { + message("WARN", message, args); + } + + private void message(final String level, final String message, Object... args) { + final String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + System.out.println( + timestamp + " [" + level + "] " + clazz.getSimpleName() + + " - " + String.format(message.replace("{}", "%s"), args) + ); + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/CommandMonitorTalker.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/CommandMonitorTalker.java new file mode 100644 index 0000000000000000000000000000000000000000..bbf82907f7c430e8070989f72abeeb5b8374754d --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/CommandMonitorTalker.java @@ -0,0 +1,128 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.monitor; + +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; + +import org.sonatype.nexus.bootstrap.log.LogProxy; + +/** + * Talks to the command monitor. + * + * @since 2.1 + */ +public class CommandMonitorTalker +{ + + /** + * Logger. Uses log proxy to be able to redirect log output to System.out if SLF4J is not available (Nexus < 2.1). + */ + private static LogProxy log = LogProxy.getLogger(CommandMonitorTalker.class); + + /** + * 5 seconds in milliseconds. Used as default timeout. + */ + private static final int FIVE_SECONDS = 5000; + + /** + * Host to send commands to. + * Never null. + */ + private final String host; + + /** + * Port on host to send commands to. + * Bigger then 1. + */ + private final int port; + + /** + * Constructor. + * + * @param host to send commands to. Cannot be null. + * @param port on host to send commands to. Must be bigger then 1. + */ + public CommandMonitorTalker(final String host, final int port) { + if (host == null) { + throw new NullPointerException(); + } + this.host = host; + if (port < 1) { + throw new IllegalArgumentException("Invalid port"); + } + this.port = port; + } + + /** + * Sends a command to a {@link CommandMonitorThread} on configured host/port. + * + * @param command to send. Cannot be null. + * @throws Exception Re-thrown if sending command fails + */ + public void send(final String command) + throws Exception + { + send(command, FIVE_SECONDS); + } + + /** + * Sends a command to a {@link CommandMonitorThread} on configured host/port, timing out after the specified number + * of milliseconds. + * + * @param command to send. Cannot be null. + * @param timeout number of milliseconds after which sending the command should timeout + * @throws Exception Re-thrown if sending command fails + */ + public void send(final String command, final int timeout) + throws Exception + { + if (command == null) { + throw new NullPointerException(); + } + + log.debug("Sending command: {}", command); + + Socket socket = new Socket(); + socket.setSoTimeout(timeout); + socket.connect(new InetSocketAddress(host, port)); + try { + OutputStream output = socket.getOutputStream(); + output.write(command.getBytes()); + output.close(); + } + finally { + socket.close(); + } + } + + /** + * Returns the host to send commands to. + * + * @return host to send commands to. Never null. + */ + public String getHost() { + return host; + } + + /** + * Returns the port on host to send commands to. + * + * @return port on host to send commands to. Bigger then 1. + */ + public String getPort() { + return host; + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/CommandMonitorThread.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/CommandMonitorThread.java new file mode 100644 index 0000000000000000000000000000000000000000..a252f4fe6505a11e4c1d935864941b8760d34d17 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/CommandMonitorThread.java @@ -0,0 +1,160 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.monitor; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; + +import org.sonatype.nexus.bootstrap.log.LogProxy; +import org.sonatype.nexus.bootstrap.monitor.commands.PingCommand; + +/** + * Thread which listens for command messages and executes them if they match one of available commands. + * + * @since 2.1 + */ +public class CommandMonitorThread + extends Thread +{ + + /** + * Local host IP (127.0.0.1). + */ + public static final String LOCALHOST = "127.0.0.1"; + + /** + * Logger. Uses log proxy to be able to redirect log output to System.out if SLF4J is not available (Nexus < 2.1). + */ + private static final LogProxy log = LogProxy.getLogger(CommandMonitorThread.class); + + /** + * Listening socket. + * Never null. + */ + private final ServerSocket socket; + + /** + * List of available commands. + */ + private final Map commands = new HashMap(); + + /** + * Constructor. + * + * @param port port on which to listen for commands. If zero, an random port will be chosen. + * @param commands available commands. Can be empty. + * @throws IOException Re-thrown while opening listening socket + */ + public CommandMonitorThread(final int port, final Command... commands) + throws IOException + { + setDaemon(true); + + if (commands != null) { + for (final Command command : commands) { + this.commands.put(command.getId(), command); + } + } + + setDaemon(true); + setName("Bootstrap Command Monitor"); + + // Only listen on local interface + this.socket = new ServerSocket(port, 1, InetAddress.getByName(LOCALHOST)); + } + + /** + * Listens for commands on configured port on local interface. + */ + @Override + public void run() { + log.debug("Listening for commands: {}", socket); + + boolean running = true; + while (running) { + try { + Socket client = socket.accept(); + log.debug("Accepted client: {}", client); + + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); + String commandId = reader.readLine(); + log.debug("Read command: {}", commandId); + client.close(); + + if (commandId == null) { + commandId = PingCommand.NAME; + } + final Command command = commands.get(commandId); + if (command == null) { + log.error("Unknown command: {}", commandId); + } + else { + running = !command.execute(); + } + } + catch (Exception e) { + log.error("Failed", e); + } + } + + try { + socket.close(); + } + catch (IOException e) { + // ignore + } + + log.debug("Stopped"); + } + + /** + * Returns the port, the monitor, it listens to. Is the provided one or the random generated one if port used in + * constructor was null. + * + * @return monitor port. Bigger then 0. + */ + public int getPort() { + return socket.getLocalPort(); + } + + /** + * A command to be executed in case that received command matches. + * + * @since 2.2 + */ + public static interface Command + { + + /** + * ID of command (when it should be executed). + * + * @return command id. Never null. + */ + String getId(); + + /** + * Executes the command. + * + * @return true, if command monitor thread should stop running + */ + boolean execute(); + + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/KeepAliveThread.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/KeepAliveThread.java new file mode 100644 index 0000000000000000000000000000000000000000..94beaa77567dd2d172029559ab7428f4503708a8 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/KeepAliveThread.java @@ -0,0 +1,151 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.monitor; + +import java.io.IOException; +import java.net.ConnectException; + +import org.sonatype.nexus.bootstrap.ShutdownHelper; +import org.sonatype.nexus.bootstrap.monitor.commands.PingCommand; + +/** + * Thread which pings a specified host:port at a configured interval and executes a task if + * the remote monitor is unreachable (ie. {@link ConnectException}). + * + * @since 2.2 + */ +public class KeepAliveThread + extends Thread +{ + // NOTE: Avoiding any logging our sysout usage by this class, this could lockup logging when its detected remote unreachable + + public static final String KEEP_ALIVE_PORT = KeepAliveThread.class.getName() + ".port"; + + public static final String KEEP_ALIVE_PING_INTERVAL = KeepAliveThread.class.getName() + ".pingInterval"; + + public static final String KEEP_ALIVE_TIMEOUT = KeepAliveThread.class.getName() + ".timeout"; + + private final CommandMonitorTalker talker; + + private final int interval; + + private final int timeout; + + private final Runnable task; + + private volatile boolean running; + + /** + * Execute custom {@link Runtime} when remote is unreachable. + * + * @param host host to be pinged + * @param port port on host to be pinged + * @param interval interval between pings + * @param timeout ping timeout + * @param task task to execute when remote is unreachable, this task should not log or write to syslog if + * possible to avoid locking up on shutdown + */ + // TestAccessible for most uses the task should be to HALT + public KeepAliveThread(final String host, + final int port, + final int interval, + final int timeout, + final Runnable task) + throws IOException + { + setDaemon(true); + setName(getClass().getName()); + + this.talker = new CommandMonitorTalker(host, port); + this.interval = interval; + this.timeout = timeout; + this.task = task; + this.running = true; + } + + /** + * Halt the JVM when remote is unreachable. + * + * @param host host to be pinged + * @param port port on host to be pinged + * @param interval interval between pings + * @param timeout ping timeout + */ + public KeepAliveThread(final String host, + final int port, + final int interval, + final int timeout) + throws IOException + { + this(host, port, interval, timeout, new Runnable() + { + @Override + public void run() { + ShutdownHelper.halt(666); + } + }); + } + + /** + * Continue pinging on configured port until there is a connection (refused) exception, case when a shutdown will be + * performed. + */ + @Override + public void run() { + while (running) { + try { + try { + ping(); + sleep(interval); + } + catch (final InterruptedException e) { + // re-ping if we were interrupted for any reason + ping(); + } + } + catch (ConnectException e) { + stopRunning(); + executeTask(); + } + } + } + + /** + * Pings the configured host/port. + * + * @throws ConnectException If ping fails + */ + private void ping() throws ConnectException { + try { + talker.send(PingCommand.NAME, timeout); + } + catch (ConnectException e) { + throw e; + } + catch (Exception e) { + // ignore + } + } + + // @TestAccessible + void executeTask() { + task.run(); + } + + /** + * Stops this thread from running (without running the shutdown code). + */ + public void stopRunning() { + running = false; + } +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/ExitCommand.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/ExitCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..b2739cf1639d59d03a6cef3c6590679c332217a1 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/ExitCommand.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.monitor.commands; + +import org.sonatype.nexus.bootstrap.ShutdownHelper; +import org.sonatype.nexus.bootstrap.monitor.CommandMonitorThread; + +/** + * Command to exit the JVM (via {@link ShutdownHelper#exit(int)}). + * + * @since 2.2 + */ +public class ExitCommand + implements CommandMonitorThread.Command +{ + + public static final String NAME = "EXIT"; + + @Override + public String getId() { + return NAME; + } + + @Override + public boolean execute() { + ShutdownHelper.exit(666); + + throw new Error("Unreachable statement"); + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/HaltCommand.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/HaltCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..1fb48c41b6af4fa0b9c6db14258bce571fc71f50 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/HaltCommand.java @@ -0,0 +1,41 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.monitor.commands; + +import org.sonatype.nexus.bootstrap.ShutdownHelper; +import org.sonatype.nexus.bootstrap.monitor.CommandMonitorThread; + +/** + * Command to forcibly halt the JVM (via {@link ShutdownHelper#halt(int)}). + * + * @since 2.2 + */ +public class HaltCommand + implements CommandMonitorThread.Command +{ + + public static final String NAME = "HALT"; + + @Override + public String getId() { + return NAME; + } + + @Override + public boolean execute() { + ShutdownHelper.halt(666); + + throw new Error("Unreachable statement"); + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/PingCommand.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/PingCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..41ff3cc2dcaeae9106ab3471566017e38a5a0846 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/PingCommand.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.monitor.commands; + +import org.sonatype.nexus.bootstrap.log.LogProxy; +import org.sonatype.nexus.bootstrap.monitor.CommandMonitorThread; + +/** + * Responds to pings (by doing nothing). + * + * @since 2.2 + */ +public class PingCommand + implements CommandMonitorThread.Command +{ + + private static final LogProxy log = LogProxy.getLogger(PingCommand.class); + + public static final String NAME = "PING"; + + @Override + public String getId() { + return NAME; + } + + @Override + public boolean execute() { + log.debug("Pinged"); + return false; + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/StopApplicationCommand.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/StopApplicationCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..f98cc5d06cbdfc074de5ff11b1de3c885494e935 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/StopApplicationCommand.java @@ -0,0 +1,54 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.monitor.commands; + +import org.sonatype.nexus.bootstrap.log.LogProxy; +import org.sonatype.nexus.bootstrap.monitor.CommandMonitorThread; + +/** + * Stop launcher. + * + * @since 2.2 + */ +public class StopApplicationCommand + implements CommandMonitorThread.Command +{ + + private static final LogProxy log = LogProxy.getLogger(StopApplicationCommand.class); + + public static final String NAME = "STOP"; + + private final Runnable shutdown; + + public StopApplicationCommand(final Runnable shutdown) { + if (shutdown == null) { + throw new NullPointerException(); + } + this.shutdown = shutdown; + } + + @Override + public String getId() { + return NAME; + } + + @Override + public boolean execute() { + log.debug("Requesting application stop"); + shutdown.run(); + + // Do not terminate the monitor on application stop, leave that to the jvm death + return false; + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/StopMonitorCommand.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/StopMonitorCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..4ba24e6a8e01fb40d3c1a72d9233cf01f4d5c663 --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/monitor/commands/StopMonitorCommand.java @@ -0,0 +1,42 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.monitor.commands; + +import org.sonatype.nexus.bootstrap.log.LogProxy; +import org.sonatype.nexus.bootstrap.monitor.CommandMonitorThread; + +/** + * Stops command monitor. + * + * @since 2.2 + */ +public class StopMonitorCommand + implements CommandMonitorThread.Command +{ + + private static final LogProxy log = LogProxy.getLogger(StopMonitorCommand.class); + + public static final String NAME = "STOP_MONITOR"; + + @Override + public String getId() { + return NAME; + } + + @Override + public boolean execute() { + log.debug("Requesting monitor stop"); + return true; + } + +} diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/osgi/BootstrapListener.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/osgi/BootstrapListener.java new file mode 100644 index 0000000000000000000000000000000000000000..e0cd20f65631a1290ca096c812d89b609176174f --- /dev/null +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/osgi/BootstrapListener.java @@ -0,0 +1,206 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.bootstrap.osgi; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.EnumSet; +import java.util.Map; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.sonatype.nexus.bootstrap.ConfigurationBuilder; +import org.sonatype.nexus.bootstrap.ConfigurationHolder; +import org.sonatype.nexus.bootstrap.EnvironmentVariables; +import org.sonatype.nexus.bootstrap.LockFile; + +import org.apache.karaf.features.Feature; +import org.apache.karaf.features.FeaturesService; +import org.apache.karaf.features.FeaturesService.Option; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.karaf.features.FeaturesService.Option.ContinueBatchOnFailure; +import static org.apache.karaf.features.FeaturesService.Option.NoAutoRefreshBundles; +import static org.apache.karaf.features.FeaturesService.Option.NoCleanIfFailure; + +/** + * {@link ServletContextListener} that bootstraps an OSGi-based application. + * + * @since 3.0 + */ +public class BootstrapListener + implements ServletContextListener +{ + private static final Logger log = LoggerFactory.getLogger(BootstrapListener.class); + + private LockFile lockFile; + + private ListenerTracker listenerTracker; + + private FilterTracker filterTracker; + + public void contextInitialized(final ServletContextEvent event) { + log.info("Initializing"); + + ServletContext servletContext = event.getServletContext(); + + try { + // Use bootstrap configuration if it exists, else load it + Map properties = ConfigurationHolder.get(); + if (properties != null) { + log.info("Using bootstrap launcher configuration"); + } + else { + log.info("Loading configuration for WAR deployment"); + + String baseDir = (String) servletContext.getAttribute("nexus-base"); + if (baseDir == null) { + baseDir = servletContext.getRealPath("/WEB-INF"); + } + + properties = new ConfigurationBuilder() + .defaults() + .set("nexus-base", new File(baseDir).getCanonicalPath()) + .properties("/org.sonatype.nexus.cfg", true) + .custom(new EnvironmentVariables()) + .override(System.getProperties()) + .build(); + + System.getProperties().putAll(properties); + ConfigurationHolder.set(properties); + } + + // Ensure required properties exist + requireProperty(properties, "nexus-base"); + requireProperty(properties, "nexus-work"); + requireProperty(properties, "nexus-app"); + requireProperty(properties, "application-conf"); + + // pass bootstrap properties to embedded servlet listener + servletContext.setAttribute("org.sonatype.nexus.cfg", properties); + + File workDir = new File(properties.get("nexus-work")).getCanonicalFile(); + mkdir(workDir.toPath()); + + // lock the work directory + lockFile = new LockFile(new File(workDir, "nexus.lock")); + if (!lockFile.lock()) { + throw new IllegalStateException("Nexus work directory already in use: " + workDir); + } + + // are we already running in OSGi or should we embed OSGi? + Bundle containingBundle = FrameworkUtil.getBundle(getClass()); + BundleContext bundleContext; + if (containingBundle != null) { + bundleContext = containingBundle.getBundleContext(); + } + else { + // when we support running in embedded mode this is where it'll go + throw new UnsupportedOperationException("Missing OSGi container"); + } + + // bootstrap our chosen Nexus edition + installNexusEdition(bundleContext, properties.get("nexus-edition")); + + // watch out for the real Nexus listener + listenerTracker = new ListenerTracker(bundleContext, "nexus", servletContext); + listenerTracker.open(); + + // watch out for the real Nexus filter + filterTracker = new FilterTracker(bundleContext, "nexus"); + filterTracker.open(); + + listenerTracker.waitForService(0); + filterTracker.waitForService(0); + } + catch (Exception e) { + log.error("Failed to start Nexus", e); + throw e instanceof RuntimeException ? ((RuntimeException) e) : new RuntimeException(e); + } + + log.info("Initialized"); + } + + private static void installNexusEdition(final BundleContext ctx, final String editionName) throws Exception { + if (editionName != null && editionName.length() > 0) { + log.info("Installing {}", editionName); + + final ServiceTracker tracker = new ServiceTracker<>(ctx, FeaturesService.class, null); + tracker.open(); + try { + FeaturesService featuresService = tracker.waitForService(1000); + Feature editionFeature = featuresService.getFeature(editionName); + + // edition might already be installed in the cache; if so then skip installation + if (!featuresService.isInstalled(editionFeature)) { + EnumSet