summaryrefslogtreecommitdiffstats
path: root/standalone-container
diff options
context:
space:
mode:
authorOlli Virtanen <olli.virtanen@oath.com>2018-05-31 10:58:25 +0200
committerOlli Virtanen <olli.virtanen@oath.com>2018-05-31 10:58:25 +0200
commit3eefb81679eb3b3da6b782583b060166b39befd7 (patch)
tree77ec82f306f8d5bf6289e460b2adf621e0ad2f16 /standalone-container
parent41fafa8edf8c7dda56b30050d5233b17f03babe1 (diff)
Scala code in standalone-container converted to Java
Diffstat (limited to 'standalone-container')
-rw-r--r--standalone-container/pom.xml39
-rw-r--r--standalone-container/src/main/java/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.java569
-rw-r--r--standalone-container/src/main/java/com/yahoo/application/container/impl/StandaloneContainerRunner.java34
-rw-r--r--standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java97
-rw-r--r--standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java304
-rw-r--r--standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java127
-rw-r--r--standalone-container/src/main/scala/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.scala206
-rw-r--r--standalone-container/src/main/scala/com/yahoo/application/container/impl/StandaloneContainerRunner.scala27
-rw-r--r--standalone-container/src/main/scala/com/yahoo/container/standalone/Converter.scala26
-rw-r--r--standalone-container/src/main/scala/com/yahoo/container/standalone/Environment.scala23
-rw-r--r--standalone-container/src/main/scala/com/yahoo/container/standalone/LocalFileDb.scala75
-rw-r--r--standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneContainerApplication.scala231
-rw-r--r--standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala78
-rw-r--r--standalone-container/src/test/java/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.java67
-rw-r--r--standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainer.java61
-rw-r--r--standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java5
-rw-r--r--standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerTest.java74
-rw-r--r--standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java52
-rw-r--r--standalone-container/src/test/scala/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.scala59
-rw-r--r--standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainer.scala64
-rw-r--r--standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainerTest.scala85
-rw-r--r--standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneSubscriberTest.scala41
22 files changed, 1387 insertions, 957 deletions
diff --git a/standalone-container/pom.xml b/standalone-container/pom.xml
index 0d66951f364..f31072aec6e 100644
--- a/standalone-container/pom.xml
+++ b/standalone-container/pom.xml
@@ -69,11 +69,6 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.scala-lang.modules</groupId>
- <artifactId>scala-xml_${scala.major-version}</artifactId>
- <scope>test</scope>
- </dependency>
</dependencies>
<build>
@@ -101,40 +96,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
-
- <plugin>
- <groupId>net.alchim31.maven</groupId>
- <artifactId>scala-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>compile</id>
- <goals>
- <goal>compile</goal>
- </goals>
- <phase>compile</phase>
- </execution>
- <execution>
- <id>test-compile</id>
- <goals>
- <goal>testCompile</goal>
- </goals>
- <phase>test-compile</phase>
- </execution>
- <execution>
- <phase>process-resources</phase>
- <goals>
- <goal>compile</goal>
- </goals>
- </execution>
- <execution>
- <phase>process-test-resources</phase>
- <id>early-test-compile</id>
- <goals>
- <goal>testCompile</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
</plugins>
</build>
</project>
diff --git a/standalone-container/src/main/java/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.java b/standalone-container/src/main/java/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.java
new file mode 100644
index 00000000000..8d4126a01e3
--- /dev/null
+++ b/standalone-container/src/main/java/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.java
@@ -0,0 +1,569 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.impl;
+
+import com.google.common.collect.Lists;
+import com.yahoo.container.standalone.StandaloneContainerApplication;
+import com.yahoo.jdisc.application.OsgiFramework;
+import com.yahoo.jdisc.application.OsgiHeader;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceObjects;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.Version;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Wire;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+
+/**
+ * A (mock) OSGI implementation which loads classes from the system classpath
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public final class ClassLoaderOsgiFramework implements OsgiFramework {
+ private BundleContextImpl bundleContextImpl = new BundleContextImpl();
+ private SystemBundleImpl systemBundleImpl = new SystemBundleImpl();
+ private BundleWiringImpl bundleWiringImpl = new BundleWiringImpl();
+
+ private List<URL> bundleLocations = new ArrayList<>();
+ private List<Bundle> bundleList = Lists.newArrayList(systemBundleImpl);
+ private ClassLoader classLoader = null;
+
+ private AtomicInteger nextBundleId = new AtomicInteger(1);
+
+ @Override
+ public List<Bundle> installBundle(String bundleLocation) {
+ if (bundleLocation != null && bundleLocation.isEmpty() == false) {
+ try {
+ URL url = new URL(bundleLocation);
+ bundleLocations.add(url);
+ bundleList.add(new JarBundleImpl(url));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return bundles();
+ }
+
+ private ClassLoader getClassLoader() {
+ if (bundleLocations.isEmpty()) {
+ return getClass().getClassLoader();
+ } else {
+ if (classLoader == null) {
+ classLoader = new URLClassLoader(bundleLocations.toArray(new URL[0]), getClass().getClassLoader());
+ }
+ return classLoader;
+ }
+ }
+
+ @Override
+ public void startBundles(List<Bundle> bundles, boolean privileged) {
+ }
+
+ @Override
+ public void refreshPackages() {
+ }
+
+ @Override
+ public BundleContext bundleContext() {
+ return bundleContextImpl;
+ }
+
+ @Override
+ public List<Bundle> bundles() {
+ return bundleList;
+ }
+
+ @Override
+ public void start() {
+ }
+
+ @Override
+ public void stop() {
+ }
+
+ private abstract class BundleImpl implements Bundle {
+ @Override
+ public int getState() {
+ return Bundle.ACTIVE;
+ }
+
+ @Override
+ public void start(int options) {
+ }
+
+ @Override
+ public void start() {
+ }
+
+ @Override
+ public void stop(int options) {
+ }
+
+ @Override
+ public void stop() {
+ }
+
+ @Override
+ public void update(InputStream input) {
+ }
+
+ @Override
+ public void update() {
+ }
+
+ @Override
+ public void uninstall() {
+ }
+
+ @Override
+ public Dictionary<String, String> getHeaders(String locale) {
+ return getHeaders();
+ }
+
+ @Override
+ public String getSymbolicName() {
+ return ClassLoaderOsgiFramework.this.getClass().getName();
+ }
+
+ @Override
+ public String getLocation() {
+ return getSymbolicName();
+ }
+
+ @Override
+ public ServiceReference<?>[] getRegisteredServices() {
+ return new ServiceReference<?>[0];
+ }
+
+ @Override
+ public ServiceReference<?>[] getServicesInUse() {
+ return getRegisteredServices();
+ }
+
+ @Override
+ public boolean hasPermission(Object permission) {
+ return true;
+ }
+
+ @Override
+ public URL getResource(String name) {
+ return getClassLoader().getResource(name);
+ }
+
+ @Override
+ public Class<?> loadClass(String name) throws ClassNotFoundException {
+ return getClassLoader().loadClass(name);
+ }
+
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ return getClassLoader().getResources(name);
+ }
+
+ @Override
+ public Enumeration<String> getEntryPaths(String path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URL getEntry(String path) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Enumeration<URL> findEntries(String path, String filePattern, boolean recurse) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public long getLastModified() {
+ return 1L;
+ }
+
+ @Override
+ public BundleContext getBundleContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(int signersType) {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T adapt(Class<T> clazz) {
+ if (clazz.equals(BundleRevision.class)) {
+ return (T) new BundleRevisionImpl();
+ } else if (clazz.equals(BundleWiring.class)) {
+ return (T) new BundleWiringImpl();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public File getDataFile(String filename) {
+ return null;
+ }
+
+ @Override
+ public int compareTo(Bundle o) {
+ return Long.compare(getBundleId(), o.getBundleId());
+ }
+ }
+
+ private class BundleRevisionImpl implements BundleRevision {
+ @Override
+ public String getSymbolicName() {
+ return this.getClass().getName();
+ }
+
+ @Override
+ public List<BundleRequirement> getDeclaredRequirements(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Version getVersion() {
+ return Version.emptyVersion;
+ }
+
+ @Override
+ public BundleWiring getWiring() {
+ return bundleWiringImpl;
+ }
+
+ @Override
+ public List<BundleCapability> getDeclaredCapabilities(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getTypes() {
+ return 0;
+ }
+
+ @Override
+ public Bundle getBundle() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Capability> getCapabilities(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Requirement> getRequirements(String p1) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private class BundleWiringImpl implements BundleWiring {
+ @Override
+ public List<URL> findEntries(String p1, String p2, int p3) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Wire> getRequiredResourceWires(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Capability> getResourceCapabilities(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isCurrent() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<BundleWire> getRequiredWires(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<BundleCapability> getCapabilities(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Wire> getProvidedResourceWires(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<BundleWire> getProvidedWires(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BundleRevision getRevision() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<Requirement> getResourceRequirements(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isInUse() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<String> listResources(String p1, String p2, int p3) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return ClassLoaderOsgiFramework.this.getClassLoader();
+ }
+
+ @Override
+ public List<BundleRequirement> getRequirements(String p1) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BundleRevision getResource() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle getBundle() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private class SystemBundleImpl extends BundleImpl {
+ @Override
+ public long getBundleId() {
+ return 0L;
+ }
+
+ @Override
+ public Version getVersion() {
+ return Version.emptyVersion;
+ }
+
+ @Override
+ public Dictionary<String, String> getHeaders() {
+ Hashtable<String, String> ret = new Hashtable<>();
+ ret.put(OsgiHeader.APPLICATION, StandaloneContainerApplication.class.getName());
+ return ret;
+ }
+ }
+
+ private class JarBundleImpl extends BundleImpl {
+ private final long bundleId;
+ private final Dictionary<String, String> headers;
+
+ JarBundleImpl(URL location) throws IOException {
+ this.bundleId = (long) nextBundleId.getAndIncrement();
+ this.headers = retrieveHeaders(location);
+ }
+
+ @Override
+ public long getBundleId() {
+ return bundleId;
+ }
+
+ @Override
+ public Dictionary<String, String> getHeaders() {
+ return headers;
+ }
+
+ @Override
+ public String getSymbolicName() {
+ return headers.get("Bundle-SymbolicName");
+ }
+
+ @Override
+ public Version getVersion() {
+ return Version.parseVersion(headers.get("Bundle-Version"));
+ }
+
+ private Dictionary<String, String> retrieveHeaders(URL location) throws IOException {
+ try (JarFile jarFile = new JarFile(location.getFile())) {
+ Attributes attributes = jarFile.getManifest().getMainAttributes();
+ Hashtable<String, String> ret = new Hashtable<>();
+ attributes.forEach((k, v) -> ret.put(k.toString(), v.toString()));
+ return ret;
+ }
+ }
+ }
+
+ private class BundleContextImpl implements BundleContext {
+ @Override
+ public String getProperty(String key) {
+ return null;
+ }
+
+ @Override
+ public Bundle getBundle() {
+ return systemBundleImpl;
+ }
+
+ @Override
+ public Bundle installBundle(String location, InputStream input) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle installBundle(String location) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Bundle getBundle(long id) {
+ return systemBundleImpl;
+ }
+
+ @Override
+ public Bundle[] getBundles() {
+ return new Bundle[] { systemBundleImpl };
+ }
+
+ @Override
+ public Bundle getBundle(String location) {
+ return systemBundleImpl;
+ }
+
+ @Override
+ public void addServiceListener(ServiceListener listener, String filter) {
+ }
+
+ @Override
+ public void addServiceListener(ServiceListener listener) {
+ }
+
+ @Override
+ public void removeServiceListener(ServiceListener listener) {
+ }
+
+ @Override
+ public void addBundleListener(BundleListener listener) {
+ }
+
+ @Override
+ public void removeBundleListener(BundleListener listener) {
+ }
+
+ @Override
+ public void addFrameworkListener(FrameworkListener listener) {
+ }
+
+ @Override
+ public void removeFrameworkListener(FrameworkListener listener) {
+ }
+
+ @Override
+ public ServiceRegistration<?> registerService(String[] classes, Object service, Dictionary<String, ?> properties) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ServiceRegistration<?> registerService(String clazz, Object service, Dictionary<String, ?> properties) {
+ return null;
+ }
+
+ @Override
+ public <S> ServiceRegistration<S> registerService(Class<S> clazz, S service, Dictionary<String, ?> properties) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ServiceReference<?>[] getServiceReferences(String clazz, String filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ServiceReference<?>[] getAllServiceReferences(String clazz, String filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ServiceReference<?> getServiceReference(String clazz) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <S> ServiceReference<S> getServiceReference(Class<S> clazz) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <S> Collection<ServiceReference<S>> getServiceReferences(Class<S> clazz, String filter) {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public <S> S getService(ServiceReference<S> reference) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean ungetService(ServiceReference<?> reference) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public File getDataFile(String filename) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Filter createFilter(String filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <S> ServiceRegistration<S> registerService(Class<S> aClass, ServiceFactory<S> serviceFactory,
+ Dictionary<String, ?> dictionary) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public <S> ServiceObjects<S> getServiceObjects(ServiceReference<S> serviceReference) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/standalone-container/src/main/java/com/yahoo/application/container/impl/StandaloneContainerRunner.java b/standalone-container/src/main/java/com/yahoo/application/container/impl/StandaloneContainerRunner.java
new file mode 100644
index 00000000000..a0fee3265df
--- /dev/null
+++ b/standalone-container/src/main/java/com/yahoo/application/container/impl/StandaloneContainerRunner.java
@@ -0,0 +1,34 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.impl;
+
+import com.yahoo.text.Utf8;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class StandaloneContainerRunner {
+ public static Path createApplicationPackage(String servicesXml) {
+ try {
+ return createApplicationDirectory(servicesXml);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Path createApplicationDirectory(String servicesXml) throws IOException {
+ Path applicationDir = Files.createTempDirectory("application");
+ Path servicesXmlFile = applicationDir.resolve("services.xml");
+ String content = servicesXml;
+
+ if (!servicesXml.startsWith("<?xml")) {
+ content = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" + servicesXml;
+ }
+ Files.write(servicesXmlFile, Utf8.toBytes(content));
+ return applicationDir;
+ }
+}
diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java b/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java
new file mode 100644
index 00000000000..4bbe9986d90
--- /dev/null
+++ b/standalone-container/src/main/java/com/yahoo/container/standalone/LocalFileDb.java
@@ -0,0 +1,97 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.standalone;
+
+import com.yahoo.config.FileReference;
+import com.yahoo.config.application.api.FileRegistry;
+import com.yahoo.filedistribution.fileacquirer.FileAcquirer;
+import com.yahoo.net.HostName;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * FileAcquirer and FileRegistry working on a local directory.
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class LocalFileDb implements FileAcquirer, FileRegistry {
+ private static final Constructor<FileReference> fileReferenceConstructor = createFileReferenceConstructor();
+
+ private final Map<FileReference, File> fileReferenceToFile = new HashMap<>();
+ private final Path appPath;
+
+ public LocalFileDb(Path appPath) {
+ this.appPath = appPath;
+ }
+
+ /* FileAcquirer overrides */
+ @Override
+ public File waitFor(FileReference reference, long l, TimeUnit timeUnit) {
+ synchronized (this) {
+ File file = fileReferenceToFile.get(reference);
+ if (file == null) {
+ throw new RuntimeException("Invalid file reference " + reference);
+ }
+ return file;
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ }
+
+ /* FileRegistry overrides */
+ public FileReference addFile(String relativePath) {
+ File file = appPath.resolve(relativePath).toFile();
+ if (!file.exists()) {
+ throw new RuntimeException("The file does not exist: " + file.getPath());
+ }
+
+ FileReference fileReference = null;
+ try {
+ fileReference = fileReferenceConstructor.newInstance("LocalFileDb:" + relativePath);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException("Unable to create new FileReference", e);
+ }
+ fileReferenceToFile.put(fileReference, file);
+ return fileReference;
+ }
+
+ @Override
+ public List<Entry> export() {
+ return fileReferenceToFile.entrySet().stream().map(entry -> new Entry(entry.getValue().getPath(), entry.getKey()))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public FileReference addUri(String uri) {
+ throw new RuntimeException("addUri(String uri) is not implemented here.");
+ }
+
+ public String fileSourceHost() {
+ return HostName.getLocalhost();
+ }
+
+ public Set<String> allRelativePaths() {
+ return fileReferenceToFile.values().stream().map(File::getPath).collect(Collectors.toSet());
+ }
+
+ private static Constructor<FileReference> createFileReferenceConstructor() {
+ try {
+ Constructor<FileReference> method = FileReference.class.getDeclaredConstructor(String.class);
+ method.setAccessible(true);
+ return method;
+ } catch (NoSuchMethodException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+}
diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java
new file mode 100644
index 00000000000..72937301954
--- /dev/null
+++ b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneContainerApplication.java
@@ -0,0 +1,304 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.standalone;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.ConfigurationException;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.ProvisionException;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+import com.yahoo.collections.Pair;
+import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.config.application.api.DeployLogger;
+import com.yahoo.config.application.api.FileRegistry;
+import com.yahoo.config.model.ApplicationConfigProducerRoot;
+import com.yahoo.config.model.ConfigModelRepo;
+import com.yahoo.config.model.application.provider.BaseDeployLogger;
+import com.yahoo.config.model.application.provider.FilesApplicationPackage;
+import com.yahoo.config.model.application.provider.StaticConfigDefinitionRepo;
+import com.yahoo.config.model.builder.xml.ConfigModelId;
+import com.yahoo.config.model.builder.xml.XmlHelper;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.container.di.config.SubscriberFactory;
+import com.yahoo.container.jdisc.ConfiguredApplication;
+import com.yahoo.io.IOUtils;
+import com.yahoo.jdisc.application.Application;
+import com.yahoo.text.XML;
+import com.yahoo.vespa.defaults.Defaults;
+import com.yahoo.vespa.model.HostResource;
+import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder;
+import com.yahoo.vespa.model.container.Container;
+import com.yahoo.vespa.model.container.ContainerModel;
+import com.yahoo.vespa.model.container.xml.ConfigServerContainerModelBuilder;
+import com.yahoo.vespa.model.container.xml.ContainerModelBuilder;
+import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking;
+import org.w3c.dom.Element;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static com.yahoo.collections.CollectionUtil.first;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public class StandaloneContainerApplication implements Application {
+ public static final String PACKAGE_NAME = "standalone_jdisc_container";
+ public static final String APPLICATION_LOCATION_INSTALL_VARIABLE = PACKAGE_NAME + ".app_location";
+ public static final String DEPLOYMENT_PROFILE_INSTALL_VARIABLE = PACKAGE_NAME + ".deployment_profile";
+ public static final String DISABLE_NETWORKING_ANNOTATION = "JDisc.disableNetworking";
+ public static final Named APPLICATION_PATH_NAME = Names.named(APPLICATION_LOCATION_INSTALL_VARIABLE);
+ public static final Named CONFIG_MODEL_REPO_NAME = Names.named("ConfigModelRepo");
+
+ private static final String DEFAULT_TMP_BASE_DIR = Defaults.getDefaults().underVespaHome("tmp");
+ private static final String TMP_DIR_NAME = "standalone_container";
+
+ private static final StaticConfigDefinitionRepo configDefinitionRepo = new StaticConfigDefinitionRepo();
+
+ private final Injector injector;
+ private final Path applicationPath;
+ private final LocalFileDb distributedFiles;
+ private final ConfigModelRepo configModelRepo;
+ private final Networking networkingOption;
+ private final VespaModel modelRoot;
+ private final Application configuredApplication;
+ private final Container container;
+
+ @Inject
+ public StandaloneContainerApplication(Injector injector) {
+ this.injector = injector;
+ ConfiguredApplication.ensureVespaLoggingInitialized();
+ this.applicationPath = injectedApplicationPath().orElseGet(this::installApplicationPath);
+ this.distributedFiles = new LocalFileDb(applicationPath);
+ this.configModelRepo = resolveConfigModelRepo();
+ this.networkingOption = resolveNetworkingOption();
+
+ try {
+ Pair<VespaModel, Container> tpl = withTempDir(preprocessedApplicationDir -> createContainerModel(applicationPath,
+ distributedFiles, preprocessedApplicationDir, networkingOption, configModelRepo));
+ this.modelRoot = tpl.getFirst();
+ this.container = tpl.getSecond();
+ } catch (RuntimeException r) {
+ throw r;
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create ContainerModel", e);
+ }
+ this.configuredApplication = createConfiguredApplication(container);
+ }
+
+ private ConfigModelRepo resolveConfigModelRepo() {
+ try {
+ return injector.getInstance(Key.get(ConfigModelRepo.class, CONFIG_MODEL_REPO_NAME));
+ } catch (Exception e) {
+ return new ConfigModelRepo();
+ }
+ }
+
+ private Networking resolveNetworkingOption() {
+ try {
+ Boolean networkingDisable = injector.getInstance(Key.get(Boolean.class, Names.named(DISABLE_NETWORKING_ANNOTATION)));
+ if (networkingDisable != null) {
+ return networkingDisable ? Networking.disable : Networking.enable;
+ }
+ } catch (Exception ignored) {
+ }
+ return Networking.enable;
+ }
+
+ private Application createConfiguredApplication(Container container) {
+ Injector augmentedInjector = injector.createChildInjector(new AbstractModule() {
+ @Override
+ public void configure() {
+ bind(SubscriberFactory.class).toInstance(new StandaloneSubscriberFactory(modelRoot));
+ }
+ });
+
+ System.setProperty("config.id", container.getConfigId());
+ return augmentedInjector.getInstance(ConfiguredApplication.class);
+ }
+
+ private Optional<Path> injectedApplicationPath() {
+ try {
+ return Optional.ofNullable(injector.getInstance(Key.get(Path.class, APPLICATION_PATH_NAME)));
+ } catch (ConfigurationException | ProvisionException ignored) {
+ }
+ return Optional.empty();
+ }
+
+ private Path installApplicationPath() {
+ Optional<String> variable = optionalInstallVariable(APPLICATION_LOCATION_INSTALL_VARIABLE);
+
+ return variable.map(Paths::get)
+ .orElseThrow(() -> new IllegalStateException("Environment variable not set: " + APPLICATION_LOCATION_INSTALL_VARIABLE));
+ }
+
+ @Override
+ public void start() {
+ try {
+ com.yahoo.container.Container.get().setCustomFileAcquirer(distributedFiles);
+ configuredApplication.start();
+ } catch (Exception e) {
+ com.yahoo.container.Container.resetInstance();
+ throw e;
+ }
+ }
+
+ @Override
+ public void stop() {
+ configuredApplication.stop();
+ }
+
+ @Override
+ public void destroy() {
+ com.yahoo.container.Container.resetInstance();
+ configuredApplication.destroy();
+ }
+
+ public Container container() {
+ return container;
+ }
+
+ private interface ThrowingFunction<T, U> {
+ U apply(T input) throws Exception;
+ }
+
+ private static <T> T withTempDir(ThrowingFunction<File, T> f) throws Exception {
+ File tmpDir = createTempDir();
+ try {
+ return f.apply(tmpDir);
+ } finally {
+ IOUtils.recursiveDeleteDir(tmpDir);
+ }
+ }
+
+ private static File createTempDir() {
+ Path basePath;
+ if (new File(DEFAULT_TMP_BASE_DIR).exists()) {
+ basePath = Paths.get(DEFAULT_TMP_BASE_DIR);
+ } else {
+ basePath = Paths.get(System.getProperty("java.io.tmpdir"));
+ }
+
+ try {
+ Path tmpDir = Files.createTempDirectory(basePath, TMP_DIR_NAME);
+ return tmpDir.toFile();
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot create temp directory", e);
+ }
+ }
+
+ private static void validateApplication(ApplicationPackage applicationPackage) {
+ try {
+ applicationPackage.validateXML();
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private static ContainerModelBuilder newContainerModelBuilder(Networking networkingOption) {
+ Optional<String> profile = optionalInstallVariable(DEPLOYMENT_PROFILE_INSTALL_VARIABLE);
+ if (profile.isPresent()) {
+ String profileName = profile.get();
+ if ("configserver".equals(profileName)) {
+ return new ConfigServerContainerModelBuilder(new CloudConfigInstallVariables());
+ } else {
+ throw new RuntimeException("Invalid deployment profile '" + profileName + "'");
+ }
+ } else {
+ return new ContainerModelBuilder(true, networkingOption);
+ }
+ }
+
+ static Pair<VespaModel, Container> createContainerModel(Path applicationPath, FileRegistry fileRegistry,
+ File preprocessedApplicationDir, Networking networkingOption, ConfigModelRepo configModelRepo) throws Exception {
+ DeployLogger logger = new BaseDeployLogger();
+ FilesApplicationPackage rawApplicationPackage = new FilesApplicationPackage.Builder(applicationPath.toFile())
+ .includeSourceFiles(true).preprocessedDir(preprocessedApplicationDir).build();
+ ApplicationPackage applicationPackage = rawApplicationPackage.preprocess(Zone.defaultZone(), logger);
+ validateApplication(applicationPackage);
+ DeployState deployState = new DeployState.Builder().applicationPackage(applicationPackage).fileRegistry(fileRegistry)
+ .deployLogger(logger).configDefinitionRepo(configDefinitionRepo).build(true);
+
+ VespaModel root = VespaModel.createIncomplete(deployState);
+ ApplicationConfigProducerRoot vespaRoot = new ApplicationConfigProducerRoot(root, "vespa", deployState.getDocumentModel(),
+ deployState.getProperties().vespaVersion(), deployState.getProperties().applicationId());
+
+ Element spec = containerRootElement(applicationPackage);
+ ContainerModel containerModel = newContainerModelBuilder(networkingOption).build(deployState, configModelRepo, vespaRoot, spec);
+ containerModel.getCluster().prepare();
+ initializeContainerModel(containerModel, configModelRepo);
+ Container container = first(containerModel.getCluster().getContainers());
+
+ // TODO: Separate out model finalization from the VespaModel constructor,
+ // such that the above and below code to finalize the container can be
+ // replaced by root.finalize();
+
+ initializeContainer(container, spec);
+
+ root.freezeModelTopology();
+ return new Pair<>(root, container);
+ }
+
+ private static void initializeContainer(Container container, Element spec) {
+ HostResource host = container.getRoot().getHostSystem().getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC);
+
+ container.setBasePort(VespaDomBuilder.getXmlWantedPort(spec));
+ container.setHostResource(host);
+ container.initService();
+ }
+
+ private static Element getJDiscInServices(Element element) {
+ List<Element> jDiscElements = new ArrayList<>();
+ for (ConfigModelId cid : ContainerModelBuilder.configModelIds) {
+ List<Element> children = XML.getChildren(element, cid.getName());
+ jDiscElements.addAll(children);
+ }
+
+ if (jDiscElements.size() == 1) {
+ return jDiscElements.get(0);
+ } else if (jDiscElements.isEmpty()) {
+ throw new RuntimeException("No jdisc element found under services.");
+ } else {
+ List<String> nameAndId = jDiscElements.stream().map(e -> e.getNodeName() + " id='" + e.getAttribute("id") + "'")
+ .collect(Collectors.toList());
+ throw new RuntimeException("Found multiple JDisc elements: " + String.join(", ", nameAndId));
+ }
+ }
+
+ private static Element containerRootElement(ApplicationPackage applicationPackage) {
+ Element element = XmlHelper.getDocument(applicationPackage.getServices()).getDocumentElement();
+ String nodeName = element.getNodeName();
+
+ if (ContainerModelBuilder.configModelIds.stream().anyMatch(id -> id.getName().equals(nodeName))) {
+ return element;
+ } else {
+ return getJDiscInServices(element);
+ }
+ }
+
+ @SuppressWarnings("deprecation") // TODO: what is the not-deprecated way?
+ private static void initializeContainerModel(ContainerModel containerModel, ConfigModelRepo configModelRepo) {
+ containerModel.initialize(configModelRepo);
+ }
+
+ private static Optional<String> optionalInstallVariable(String name) {
+ Optional<String> fromEnv = Optional.ofNullable(System.getenv((name.replace(".", "__"))));
+ if (fromEnv.isPresent()) {
+ return fromEnv;
+ }
+ return Optional.ofNullable(System.getProperty(name)); // for unit testing
+ }
+}
diff --git a/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java
new file mode 100644
index 00000000000..882bb5709f4
--- /dev/null
+++ b/standalone-container/src/main/java/com/yahoo/container/standalone/StandaloneSubscriberFactory.java
@@ -0,0 +1,127 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.standalone;
+
+import com.yahoo.config.ConfigBuilder;
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.config.subscription.ConfigInterruptedException;
+import com.yahoo.container.di.config.Subscriber;
+import com.yahoo.container.di.config.SubscriberFactory;
+import com.yahoo.vespa.config.ConfigKey;
+import com.yahoo.vespa.model.VespaModel;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+public class StandaloneSubscriberFactory implements SubscriberFactory {
+ private final VespaModel root;
+
+ public StandaloneSubscriberFactory(VespaModel root) {
+ this.root = root;
+ }
+
+ private class StandaloneSubscriber implements Subscriber {
+ private final Set<ConfigKey<ConfigInstance>> configKeys;
+ private long generation = -1L;
+
+ StandaloneSubscriber(Set<ConfigKey<ConfigInstance>> configKeys) {
+ this.configKeys = configKeys;
+ }
+
+ @Override
+ public boolean configChanged() {
+ return generation == 0;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public Map<ConfigKey<ConfigInstance>, ConfigInstance> config() {
+ Map<ConfigKey<ConfigInstance>, ConfigInstance> ret = new HashMap<>();
+ for (ConfigKey<ConfigInstance> key : configKeys) {
+ ConfigInstance.Builder builder = root.getConfig(newBuilderInstance(key), key.getConfigId());
+ if (builder == null) {
+ throw new RuntimeException("Invalid config id " + key.getConfigId());
+ }
+ ret.put(key, newConfigInstance(builder));
+ }
+ return ret;
+ }
+
+ @Override
+ public long waitNextGeneration() {
+ generation++;
+
+ if (generation != 0) {
+ try {
+ while (!Thread.interrupted()) {
+ Thread.sleep(10000);
+ }
+ } catch (InterruptedException e) {
+ throw new ConfigInterruptedException(e);
+ }
+ }
+
+ return generation;
+ }
+
+ // if waitNextGeneration has not yet been called, -1 should be returned
+ @Override
+ public long generation() {
+ return generation;
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Subscriber getSubscriber(Set<? extends ConfigKey<?>> configKeys) {
+ return new StandaloneSubscriber((Set<ConfigKey<ConfigInstance>>) configKeys);
+ }
+
+ public void reloadActiveSubscribers(long generation) {
+ throw new RuntimeException("unsupported");
+ }
+
+ private static ConfigInstance.Builder newBuilderInstance(ConfigKey<ConfigInstance> key) {
+ try {
+ return builderClass(key).getDeclaredConstructor().newInstance();
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new RuntimeException("ConfigInstance builder cannot be instantiated", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Class<ConfigInstance.Builder> builderClass(ConfigKey<ConfigInstance> key) {
+ Class<?> configClass = key.getConfigClass();
+ if (configClass != null) {
+ Class<?>[] nestedClasses = configClass.getClasses();
+ for (Class<?> clazz : nestedClasses) {
+ if (clazz.getName().equals(key.getConfigClass().getName() + "$Builder")) {
+ return (Class<ConfigInstance.Builder>) clazz;
+ }
+ }
+ }
+ throw new RuntimeException("Builder class for " + (configClass == null ? null : configClass.getName()) + " could not be located");
+ }
+
+ private static ConfigInstance newConfigInstance(ConfigBuilder builder) {
+ try {
+ return configClass(builder).getConstructor(builder.getClass()).newInstance(builder);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new RuntimeException("ConfigInstance cannot be instantiated", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Class<ConfigInstance> configClass(ConfigBuilder builder) {
+ return (Class<ConfigInstance>) builder.getClass().getEnclosingClass();
+ }
+}
diff --git a/standalone-container/src/main/scala/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.scala b/standalone-container/src/main/scala/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.scala
deleted file mode 100644
index ac8636de2cb..00000000000
--- a/standalone-container/src/main/scala/com/yahoo/application/container/impl/ClassLoaderOsgiFramework.scala
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.application.container.impl
-
-import java.io.InputStream
-import java.net.{URL, URLClassLoader}
-import java.util
-import java.util.concurrent.atomic.AtomicInteger
-import java.util.jar.JarFile
-import java.util.{Collections, Dictionary, Hashtable}
-
-import com.yahoo.container.standalone.StandaloneContainerApplication
-import com.yahoo.jdisc.application.{OsgiFramework, OsgiHeader}
-import org.osgi.framework._
-import org.osgi.framework.wiring._
-import org.osgi.resource.{Capability, Requirement, Wire}
-
-import scala.collection.JavaConverters._
-import scala.collection.mutable.ArrayBuffer
-
-/**
- * A (mock) OSGI implementation which loads classes from the system classpath
- *
- * @author tonytv
- */
-final class ClassLoaderOsgiFramework extends OsgiFramework {
- private val bundleLocations = new ArrayBuffer[URL]
- private val bundleList = ArrayBuffer[Bundle](SystemBundleImpl)
- private var classLoader: ClassLoader = null
-
- private val nextBundleId = new AtomicInteger(1)
-
- override def installBundle(bundleLocation: String) = {
- if (bundleLocation != "") {
- val url = new URL(bundleLocation)
- bundleLocations += url
- bundleList += new JarBundleImpl(url)
- }
-
- bundles()
- }
-
- def getClassLoader = {
- if (bundleLocations.isEmpty) {
- getClass.getClassLoader
- } else {
- if(classLoader == null)
- classLoader = new URLClassLoader(bundleLocations.toArray, getClass.getClassLoader)
-
- classLoader
- }
- }
-
- override def startBundles(bundles: util.List[Bundle], privileged: Boolean) {}
-
- override def refreshPackages() {}
-
- override def bundleContext():BundleContext = BundleContextImpl
-
- override def bundles() = bundleList.asJava
-
- override def start() {}
-
- override def stop() {}
-
- private abstract class BundleImpl extends Bundle {
- override def getState = Bundle.ACTIVE
-
- override def start(options: Int) {}
- override def start() {}
- override def stop(options: Int) {}
- override def stop() {}
- override def update(input: InputStream) {}
- override def update() {}
- override def uninstall() {}
-
- override def getHeaders(locale: String) = getHeaders
-
- override def getSymbolicName = ClassLoaderOsgiFramework.this.getClass.getName
- override def getLocation = getSymbolicName
-
- override def getRegisteredServices = Array[ServiceReference[_]]()
- override def getServicesInUse = getRegisteredServices
-
- override def hasPermission(permission: Any) = true
-
- override def getResource(name: String) = getClassLoader.getResource(name)
- override def loadClass(name: String) = getClassLoader.loadClass(name)
- override def getResources(name: String) = getClassLoader.getResources(name)
-
- override def getEntryPaths(path: String) = throw new UnsupportedOperationException
- override def getEntry(path: String) = throw new UnsupportedOperationException
- override def findEntries(path: String, filePattern: String, recurse: Boolean) = throw new UnsupportedOperationException
-
- override def getLastModified = 1L
-
- override def getBundleContext = throw new UnsupportedOperationException
- override def getSignerCertificates(signersType: Int) = Collections.emptyMap()
-
- override def adapt[A](`type`: Class[A]): A = {
- if (`type` == classOf[BundleRevision]) BundleRevisionImpl.asInstanceOf[A]
- else if (`type` == classOf[BundleWiring]) BundleWiringImpl.asInstanceOf[A]
- else null.asInstanceOf[A]
- }
-
- override def getDataFile(filename: String) = null
- override def compareTo(o: Bundle) = getBundleId compareTo o.getBundleId
- }
-
- private object BundleRevisionImpl extends BundleRevision {
- override def getSymbolicName: String = this.getClass.getName
- override def getDeclaredRequirements(p1: String): util.List[BundleRequirement] = throw new UnsupportedOperationException
- override def getVersion: Version = Version.emptyVersion
- override def getWiring: BundleWiring = BundleWiringImpl
- override def getDeclaredCapabilities(p1: String): util.List[BundleCapability] = throw new UnsupportedOperationException
- override def getTypes: Int = 0
- override def getBundle: Bundle = throw new UnsupportedOperationException
- override def getCapabilities(p1: String): util.List[Capability] = throw new UnsupportedOperationException
- override def getRequirements(p1: String): util.List[Requirement] = throw new UnsupportedOperationException
- }
-
- private object BundleWiringImpl extends BundleWiring {
- override def findEntries(p1: String, p2: String, p3: Int): util.List[URL] = ???
- override def getRequiredResourceWires(p1: String): util.List[Wire] = ???
- override def getResourceCapabilities(p1: String): util.List[Capability] = ???
- override def isCurrent: Boolean = ???
- override def getRequiredWires(p1: String): util.List[BundleWire] = ???
- override def getCapabilities(p1: String): util.List[BundleCapability] = ???
- override def getProvidedResourceWires(p1: String): util.List[Wire] = ???
- override def getProvidedWires(p1: String): util.List[BundleWire] = ???
- override def getRevision: BundleRevision = ???
- override def getResourceRequirements(p1: String): util.List[Requirement] = ???
- override def isInUse: Boolean = ???
- override def listResources(p1: String, p2: String, p3: Int): util.Collection[String] = ???
- override def getClassLoader: ClassLoader = ClassLoaderOsgiFramework.this.getClassLoader
- override def getRequirements(p1: String): util.List[BundleRequirement] = ???
- override def getResource: BundleRevision = ???
- override def getBundle: Bundle = ???
- }
-
- private object SystemBundleImpl extends BundleImpl {
- override val getBundleId = 0L
- override def getVersion = Version.emptyVersion
- override def getHeaders: Dictionary[String, String] = new Hashtable[String, String](Map(OsgiHeader.APPLICATION -> classOf[StandaloneContainerApplication].getName).asJava)
- }
-
-
- private class JarBundleImpl(location: URL) extends BundleImpl {
- override val getBundleId = nextBundleId.getAndIncrement.asInstanceOf[Long]
-
- private val headers = retrieveHeaders(location)
-
- override def getHeaders: Dictionary[String, String] = headers
- override val getSymbolicName = headers.get("Bundle-SymbolicName")
- override val getVersion = Version.parseVersion(headers.get("Bundle-Version"))
-
-
- private def retrieveHeaders(location: URL) = {
- val jarFile = new JarFile(location.getFile)
- try {
- val attributes = jarFile.getManifest.getMainAttributes
- new Hashtable[String, String](attributes.entrySet().asScala.map( entry => entry.getKey.toString -> entry.getValue.toString).toMap.asJava)
- } finally {
- jarFile.close()
- }
- }
- }
-
- private object BundleContextImpl extends BundleContext {
- private val bundleImpl = SystemBundleImpl
-
- override def getProperty(key: String) = null
- override def getBundle = bundleImpl
- override def installBundle(location: String, input: InputStream) = throw new UnsupportedOperationException
- override def installBundle(location: String) = throw new UnsupportedOperationException
-
- override def getBundle(id: Long) = bundleImpl
- override def getBundles = Array(bundleImpl)
- override def getBundle(location: String) = bundleImpl
-
- override def addServiceListener(listener: ServiceListener, filter: String) {}
- override def addServiceListener(listener: ServiceListener) {}
- override def removeServiceListener(listener: ServiceListener) {}
- override def addBundleListener(listener: BundleListener) {}
- override def removeBundleListener(listener: BundleListener) {}
-
- override def addFrameworkListener(listener: FrameworkListener) {}
- override def removeFrameworkListener(listener: FrameworkListener) {}
-
- override def registerService(clazzes: Array[String], service: Any, properties: Dictionary[String, _]) = throw new UnsupportedOperationException
- override def registerService(clazz: String, service: Any, properties: Dictionary[String, _]) = null
- override def registerService[S](clazz: Class[S], service: S, properties: Dictionary[String, _]) = throw new UnsupportedOperationException
- override def getServiceReferences(clazz: String, filter: String) = throw new UnsupportedOperationException
- override def getAllServiceReferences(clazz: String, filter: String) = throw new UnsupportedOperationException
- override def getServiceReference(clazz: String) = throw new UnsupportedOperationException
- override def getServiceReference[S](clazz: Class[S]) = throw new UnsupportedOperationException
- override def getServiceReferences[S](clazz: Class[S], filter: String) = Collections.emptyList()
- override def getService[S](reference: ServiceReference[S]) = throw new UnsupportedOperationException
- override def ungetService(reference: ServiceReference[_]) = throw new UnsupportedOperationException
- override def getDataFile(filename: String) = throw new UnsupportedOperationException
- override def createFilter(filter: String) = throw new UnsupportedOperationException
-
- override def registerService[S](aClass: Class[S], serviceFactory: ServiceFactory[S], dictionary: Dictionary[String, _]): ServiceRegistration[S] = throw new UnsupportedOperationException
- override def getServiceObjects[S](serviceReference: ServiceReference[S]): ServiceObjects[S] = throw new UnsupportedOperationException
- }
-
-}
diff --git a/standalone-container/src/main/scala/com/yahoo/application/container/impl/StandaloneContainerRunner.scala b/standalone-container/src/main/scala/com/yahoo/application/container/impl/StandaloneContainerRunner.scala
deleted file mode 100644
index 91634250fc5..00000000000
--- a/standalone-container/src/main/scala/com/yahoo/application/container/impl/StandaloneContainerRunner.scala
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.application.container.impl
-
-import java.nio.file.Files
-import com.yahoo.text.Utf8
-
-/**
- * @author tonytv
- */
-final class StandaloneContainerRunner {
-
-
-
-}
-
-object StandaloneContainerRunner {
- def createApplicationPackage(servicesXml: String) = {
- val applicationDir = Files.createTempDirectory("application")
-
- val servicesXmlFile = applicationDir.resolve("services.xml");
- var content = servicesXml;
- if ( ! servicesXml.startsWith("<?xml"))
- content = """<?xml version="1.0" encoding="utf-8" ?>""" + '\n' + servicesXml
- Files.write(servicesXmlFile, Utf8.toBytes(content))
- applicationDir
- }
-}
diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/Converter.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/Converter.scala
deleted file mode 100644
index 61128347319..00000000000
--- a/standalone-container/src/main/scala/com/yahoo/container/standalone/Converter.scala
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-/**
- * @author tonytv
- */
-trait Converter[T] {
- def convert(s: String): T
-}
-
-object Converter {
- def toConverter[T](f: String => T) = new Converter[T] {
- override def convert(s: String) = f(s)
- }
-
- implicit val intConverter = toConverter(_.toInt)
- implicit val longConverter = toConverter(_.toLong)
- implicit val boolConverter = toConverter(_.toBoolean)
- implicit val stringConverter = toConverter(identity)
-
- implicit val javaIntegerConverter:Converter[Integer] = toConverter(_.toInt)
- implicit val javaLongConverter:Converter[java.lang.Long] = toConverter(_.toLong)
- implicit val javaBooleanConverter:Converter[java.lang.Boolean] = toConverter(_.toBoolean)
-
-
-}
diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/Environment.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/Environment.scala
deleted file mode 100644
index 2aab88d8319..00000000000
--- a/standalone-container/src/main/scala/com/yahoo/container/standalone/Environment.scala
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-/**
- * @author tonytv
- * TODO: copied from standalone-container. Move to separate lib module instead.
- */
-object Environment {
- def optionalInstallVariable(name: String) = {
- env(name.replace(".", "__")).
- orElse(systemProperty(name)) //for unit testing
- }
-
- def installVariable(name: String) = {
- optionalInstallVariable(name).
- getOrElse {
- throw new IllegalStateException("Environment variable not set: " + name)
- }
- }
-
- def env(name: String) = Option(System.getenv(name))
- def systemProperty(name: String) = Option(System.getProperty(name))
-}
diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/LocalFileDb.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/LocalFileDb.scala
deleted file mode 100644
index 6507b4c72f0..00000000000
--- a/standalone-container/src/main/scala/com/yahoo/container/standalone/LocalFileDb.scala
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-import java.io.File
-import java.lang.reflect.Constructor
-import java.nio.file.Path
-import java.util
-import java.util.concurrent.TimeUnit
-
-import com.yahoo.config.FileReference
-import com.yahoo.config.application.api.FileRegistry
-import com.yahoo.config.application.api.FileRegistry.Entry
-import com.yahoo.container.standalone.LocalFileDb._
-import com.yahoo.filedistribution.fileacquirer.FileAcquirer
-import com.yahoo.net.HostName
-
-import scala.collection.JavaConverters._
-import scala.collection.mutable
-
-
-/**
- * FileAcquirer and FileRegistry working on a local directory.
- * @author tonytv
- */
-class LocalFileDb(appPath: Path) extends FileAcquirer with FileRegistry {
- private val fileReferenceToFile = mutable.Map[FileReference, File]()
-
- /** *** FileAcquirer overrides *****/
- def waitFor(reference: FileReference, l: Long, timeUnit: TimeUnit): File = {
- synchronized {
- fileReferenceToFile.get(reference).getOrElse {
- throw new RuntimeException("Invalid file reference " + reference)
- }
- }
- }
-
- override def shutdown() {}
-
- /** *** FileRegistry overrides *****/
- def addFile(relativePath: String): FileReference = {
- val file = appPath.resolve(relativePath).toFile
- if (!file.exists) {
- throw new RuntimeException("The file does not exist: " + file.getPath)
- }
-
- val fileReference: FileReference = fileReferenceConstructor.newInstance("LocalFileDb:" + relativePath)
- fileReferenceToFile.put(fileReference, file)
- fileReference
- }
-
- def fileSourceHost: String =
- HostName.getLocalhost
-
- def allRelativePaths: java.util.Set[String] = {
- new java.util.HashSet(fileReferenceToFile.values.map(_.getPath).asJavaCollection)
- }
-
- override def export(): util.List[Entry] = {
- new java.util.ArrayList(fileReferenceToFile.keys.map{ (ref: FileReference) => new Entry(fileReferenceToFile.get(ref).get.getPath, ref)}.asJavaCollection)
- }
-
- override def addUri(uri: String): FileReference = {
- throw new RuntimeException("addUri(uri: String) is not implemented here.");
- }
-}
-
-object LocalFileDb {
- private def createFileReferenceConstructor: Constructor[FileReference] = {
- val method: Constructor[FileReference] = classOf[FileReference].getDeclaredConstructor(classOf[String])
- method.setAccessible(true)
- method
- }
-
- private val fileReferenceConstructor: Constructor[FileReference] = createFileReferenceConstructor
-}
diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneContainerApplication.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneContainerApplication.scala
deleted file mode 100644
index 5271cd400d4..00000000000
--- a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneContainerApplication.scala
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-import java.io.{File, IOException}
-import java.lang.{Boolean => JBoolean}
-import java.nio.file.{FileSystems, Files, Path, Paths}
-
-import com.google.inject.name.Names
-import com.google.inject.{AbstractModule, Inject, Injector, Key}
-import com.yahoo.collections.CollectionUtil.first
-import com.yahoo.config.application.api.{ApplicationPackage, FileRegistry}
-import com.yahoo.config.model.application.provider._
-import com.yahoo.config.model.builder.xml.XmlHelper
-import com.yahoo.config.model.deploy.DeployState
-import com.yahoo.config.model.{ApplicationConfigProducerRoot, ConfigModelRepo}
-import com.yahoo.config.provision.Zone
-import com.yahoo.container.di.config.SubscriberFactory
-import com.yahoo.container.jdisc.ConfiguredApplication
-import com.yahoo.container.standalone.Environment._
-import com.yahoo.container.standalone.StandaloneContainerApplication._
-import com.yahoo.io.IOUtils
-import com.yahoo.jdisc.application.Application
-import com.yahoo.text.XML
-import com.yahoo.vespa.defaults.Defaults
-import com.yahoo.vespa.model.VespaModel
-import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder
-import com.yahoo.vespa.model.container.{Container, ContainerModel}
-import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking
-import com.yahoo.vespa.model.container.xml.{ConfigServerContainerModelBuilder, ContainerModelBuilder}
-import org.w3c.dom.Element
-
-import scala.collection.JavaConverters._
-import scala.util.Try
-
-/**
- * @author Tony Vaagenes
- * @author gjoranv
- */
-class StandaloneContainerApplication @Inject()(injector: Injector) extends Application {
-
- ConfiguredApplication.ensureVespaLoggingInitialized()
-
- val applicationPath: Path = injectedApplicationPath.getOrElse(installApplicationPath)
-
- val distributedFiles = new LocalFileDb(applicationPath)
-
- val configModelRepo = Try { injector.getInstance(Key.get(classOf[ConfigModelRepo], configModelRepoName))}.getOrElse(new ConfigModelRepo)
-
- val networkingOption = Try {
- injector.getInstance(Key.get(classOf[JBoolean], Names.named(disableNetworkingAnnotation)))
- }.map {
- case JBoolean.TRUE => Networking.disable
- case JBoolean.FALSE => Networking.enable
- }.getOrElse(Networking.enable)
-
- val (modelRoot, container) = withTempDir(
- preprocessedApplicationDir => createContainerModel(applicationPath, distributedFiles, preprocessedApplicationDir, networkingOption, configModelRepo))
-
- val configuredApplication = createConfiguredApplication(container)
-
- def createConfiguredApplication(container: Container): Application = {
- val augmentedInjector = injector.createChildInjector(new AbstractModule {
- def configure() {
- bind(classOf[SubscriberFactory]).toInstance(new StandaloneSubscriberFactory(modelRoot))
- }
- })
-
- System.setProperty("config.id", container.getConfigId) //TODO: DRY
- augmentedInjector.getInstance(classOf[ConfiguredApplication])
- }
-
- def injectedApplicationPath = Try {
- injector.getInstance(Key.get(classOf[Path], applicationPathName))
- }.toOption
-
- def installApplicationPath = path(installVariable(applicationLocationInstallVariable))
-
- override def start() {
- try {
- com.yahoo.container.Container.get().setCustomFileAcquirer(distributedFiles)
- configuredApplication.start()
- }
- catch {
- case e: Exception => { com.yahoo.container.Container.resetInstance(); throw e; }
- }
- }
-
- override def stop() {
- configuredApplication.stop()
- }
-
- override def destroy() {
- com.yahoo.container.Container.resetInstance()
- configuredApplication.destroy()
- }
-}
-
-object StandaloneContainerApplication {
- val packageName = "standalone_jdisc_container"
- val applicationLocationInstallVariable = s"$packageName.app_location"
- val deploymentProfileInstallVariable = s"$packageName.deployment_profile"
-
- val applicationPathName = Names.named(applicationLocationInstallVariable)
-
- val disableNetworkingAnnotation = "JDisc.disableNetworking"
- val configModelRepoName = Names.named("ConfigModelRepo")
- val configDefinitionRepo = new StaticConfigDefinitionRepo()
-
- val defaultTmpBaseDir = Defaults.getDefaults().underVespaHome("tmp")
- val tmpDirName = "standalone_container"
-
- private def withTempDir[T](f: File => T): T = {
- val tmpDir = createTempDir()
- try {
- f(tmpDir)
- } finally {
- IOUtils.recursiveDeleteDir(tmpDir)
- }
- }
-
- private def createTempDir(): File = {
- def getBaseDir: Path = {
- val tmpBaseDir =
- if (new File(defaultTmpBaseDir).exists())
- defaultTmpBaseDir
- else
- System.getProperty("java.io.tmpdir")
-
- Paths.get(tmpBaseDir)
- }
-
- val basePath: Path = getBaseDir
- val tmpDir = Files.createTempDirectory(basePath, tmpDirName)
- tmpDir.toFile
- }
-
- private def validateApplication(applicationPackage: ApplicationPackage) = {
- try {
- applicationPackage.validateXML()
- } catch {
- case e: IOException => throw new IllegalArgumentException(e)
- }
- }
-
- def newContainerModelBuilder(networkingOption: Networking): ContainerModelBuilder = {
- optionalInstallVariable(deploymentProfileInstallVariable) match {
- case None => new ContainerModelBuilder(true, networkingOption)
- case Some("configserver") => new ConfigServerContainerModelBuilder(new CloudConfigInstallVariables)
- case profileName => throw new RuntimeException(s"Invalid deployment profile '$profileName'")
- }
- }
-
- def createContainerModel(applicationPath: Path,
- fileRegistry: FileRegistry,
- preprocessedApplicationDir: File,
- networkingOption: Networking,
- configModelRepo: ConfigModelRepo = new ConfigModelRepo): (VespaModel, Container) = {
- val logger = new BaseDeployLogger
- val rawApplicationPackage = new FilesApplicationPackage.Builder(applicationPath.toFile).includeSourceFiles(true).preprocessedDir(preprocessedApplicationDir).build()
- val applicationPackage = rawApplicationPackage.preprocess(Zone.defaultZone(), logger)
- validateApplication(applicationPackage)
- val deployState = new DeployState.Builder().
- applicationPackage(applicationPackage).
- fileRegistry(fileRegistry).
- deployLogger(logger).
- configDefinitionRepo(configDefinitionRepo).
- build(true)
-
- val root = VespaModel.createIncomplete(deployState)
- val vespaRoot = new ApplicationConfigProducerRoot(root,
- "vespa",
- deployState.getDocumentModel,
- deployState.getProperties.vespaVersion(),
- deployState.getProperties.applicationId())
-
- val spec = containerRootElement(applicationPackage)
- val containerModel = newContainerModelBuilder(networkingOption).build(deployState, configModelRepo, vespaRoot, spec)
- containerModel.getCluster().prepare()
- DeprecationSuppressor.initializeContainerModel(containerModel, configModelRepo)
- val container = first(containerModel.getCluster().getContainers)
-
- // TODO: Separate out model finalization from the VespaModel constructor,
- // such that the above and below code to finalize the container can be
- // replaced by root.finalize();
-
- initializeContainer(container, spec)
-
- root.freezeModelTopology()
- (root, container)
- }
-
- def initializeContainer(container: Container, spec: Element) {
- val host = container.getRoot.getHostSystem.getHost(Container.SINGLENODE_CONTAINER_SERVICESPEC)
-
- container.setBasePort(VespaDomBuilder.getXmlWantedPort(spec))
- container.setHostResource(host)
- container.initService()
- }
-
- def getJDiscInServices(element: Element): Element = {
- def nameAndId(elements: List[Element]): List[String] = {
- elements map { e => s"${e.getNodeName} id='${e.getAttribute("id")}'" }
- }
-
- val jDiscElements = ContainerModelBuilder.configModelIds.asScala flatMap { name => XML.getChildren(element, name.getName).asScala }
- jDiscElements.toList match {
- case List(e) => e
- case Nil => throw new RuntimeException("No jdisc element found under services.")
- case multipleElements: List[Element] => throw new RuntimeException("Found multiple JDisc elements: " + nameAndId(multipleElements).mkString(", "))
- }
- }
-
- def containerRootElement(applicationPackage: ApplicationPackage) : Element = {
- val element = XmlHelper.getDocument(applicationPackage.getServices).getDocumentElement
- val nodeName = element.getNodeName
-
- if (ContainerModelBuilder.configModelIds.asScala.map(_.getName).contains(nodeName)) element
- else getJDiscInServices(element)
- }
-
- def path(s: String) = FileSystems.getDefault.getPath(s)
-
- // Ugly hack required since Scala cannot suppress warnings. https://issues.scala-lang.org/browse/SI-7934
- private object DeprecationSuppressor extends DeprecationSuppressor
- @deprecated("", "")
- private class DeprecationSuppressor {
- def initializeContainerModel(containerModel: ContainerModel, configModelRepo: ConfigModelRepo): Unit = {
- containerModel.initialize(configModelRepo)
- }
- }
-}
diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala
deleted file mode 100644
index 99cc8259ab3..00000000000
--- a/standalone-container/src/main/scala/com/yahoo/container/standalone/StandaloneSubscriberFactory.scala
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-import com.yahoo.config.{ConfigBuilder, ConfigInstance}
-import com.yahoo.container.di.ConfigKeyT
-import com.yahoo.container.di.config.{Subscriber, SubscriberFactory}
-import com.yahoo.container.standalone.StandaloneSubscriberFactory._
-import com.yahoo.vespa.config.ConfigKey
-import com.yahoo.vespa.model.VespaModel
-
-import scala.collection.JavaConverters._
-
-/**
- * @author tonytv
- * @author gjoranv
- */
-class StandaloneSubscriberFactory(root: VespaModel) extends SubscriberFactory {
- class StandaloneSubscriber(configKeys: Set[ConfigKeyT]) extends Subscriber {
- override def configChanged =
- generation == 0
-
- override def close() {}
-
- override def config = {
-
- def getConfig(key: ConfigKeyT) = {
- val builderWithModelConfig = root.getConfig(newBuilderInstance(key), key.getConfigId)
-
- require(builderWithModelConfig != null, "Invalid config id " + key.getConfigId )
- (key.asInstanceOf[ConfigKey[ConfigInstance]], newConfigInstance(builderWithModelConfig))
- }
-
- (configKeys map getConfig).toMap.asJava
- }
-
- override def waitNextGeneration() = {
- generation += 1
-
- if (generation != 0) {
- while (!Thread.interrupted())
- Thread.sleep(10000)
- }
-
- generation
- }
-
- //if waitNextGeneration has not yet been called, -1 should be returned
- var generation = -1L
- }
-
- override def getSubscriber(configKeys: java.util.Set[_ <: ConfigKey[_]]) =
- new StandaloneSubscriber(configKeys.asScala.toSet.asInstanceOf[Set[ConfigKeyT]])
-
- def reloadActiveSubscribers(generation: Long) {
- throw new RuntimeException("unsupported")
- }
-}
-
-object StandaloneSubscriberFactory {
-
- private def newBuilderInstance(key: ConfigKeyT) =
- builderClass(key).getDeclaredConstructor().newInstance()
-
- private def builderClass(key: ConfigKeyT) = {
- val nestedClasses = key.getConfigClass.getClasses
- nestedClasses.
- filter {_.getName.equals(key.getConfigClass.getName + "$Builder")}.
- head.
- asInstanceOf[Class[ConfigInstance.Builder]]
- }
-
- private def newConfigInstance(builder: ConfigBuilder) =
- configClass(builder).getConstructor(builder.getClass).newInstance(builder)
-
- private def configClass(builder: ConfigBuilder) =
- builder.getClass.getEnclosingClass.asInstanceOf[Class[ConfigInstance]]
-
-}
diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.java
new file mode 100644
index 00000000000..1cd110d8106
--- /dev/null
+++ b/standalone-container/src/test/java/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.java
@@ -0,0 +1,67 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.standalone;
+
+import com.yahoo.vespa.model.container.configserver.option.CloudConfigOptions;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.yahoo.container.standalone.CloudConfigInstallVariables.toConfigModelsPluginDir;
+import static com.yahoo.container.standalone.CloudConfigInstallVariables.toConfigServer;
+import static com.yahoo.container.standalone.CloudConfigInstallVariables.toConfigServers;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Ulf Lilleengen
+ * @author Tony Vaagenes
+ */
+public class CloudConfigInstallVariablesTest {
+
+ @Test
+ public void test_configserver_parsing() {
+ CloudConfigOptions.ConfigServer[] parsed = toConfigServers("myhost.mydomain.com");
+ assertThat(parsed.length, is(1));
+ }
+
+ @Test
+ public void port_can_be_configured() {
+ CloudConfigOptions.ConfigServer[] parsed = toConfigServers("myhost:123");
+ int port = parsed[0].port.get();
+ assertThat(port, is(123));
+ }
+
+ @Test
+ public void multiple_spaces_are_supported() {
+ CloudConfigOptions.ConfigServer[] parsed = toConfigServers("test1 test2");
+ assertThat(parsed.length, is(2));
+
+ List<String> hostNames = Arrays.stream(parsed).map(cs -> cs.hostName).collect(Collectors.toList());
+ assertThat(hostNames, contains("test1", "test2"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void missing_port_gives_exception() {
+ toConfigServer("myhost:");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void non_numeric_port_gives_exception() {
+ toConfigServer("myhost:non-numeric");
+ }
+
+ @Test
+ public void string_arrays_are_split_on_spaces() {
+ String[] parsed = toConfigModelsPluginDir("/home/vespa/foo /home/vespa/bar ");
+ assertThat(parsed.length, is(2));
+ }
+
+ @Test
+ public void string_arrays_are_split_on_comma() {
+ String[] parsed = toConfigModelsPluginDir("/home/vespa/foo,/home/vespa/bar,");
+ assertThat(parsed.length, is(2));
+ }
+}
diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainer.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainer.java
new file mode 100644
index 00000000000..a00ffd8b985
--- /dev/null
+++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainer.java
@@ -0,0 +1,61 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.standalone;
+
+import com.yahoo.collections.Pair;
+import com.yahoo.config.model.ConfigModelRepo;
+import com.yahoo.config.model.producer.AbstractConfigProducerRoot;
+import com.yahoo.io.IOUtils;
+import com.yahoo.vespa.model.VespaModel;
+import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Creates a local application from vespa-services fragments.
+ *
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class StandaloneContainer {
+ public static String firstContainerId(AbstractConfigProducerRoot root) {
+ return root.getConfigProducer("container").get().getConfigId();
+ }
+
+ interface ThrowingFunction<T, U> {
+ U apply(T input) throws Exception;
+ }
+
+ static <T> T withContainerModel(String servicesXml, ThrowingFunction<VespaModel, T> f) throws Exception {
+ return withTempDirectory(applicationPath -> {
+ writeServicesXml(applicationPath, servicesXml);
+
+ LocalFileDb distributedFiles = new LocalFileDb(applicationPath);
+ VespaModel root;
+ Pair<VespaModel, com.yahoo.vespa.model.container.Container> rc = StandaloneContainerApplication.createContainerModel(
+ applicationPath, distributedFiles, applicationPath.resolve("preprocesedApp").toFile(), Networking.enable,
+ new ConfigModelRepo());
+ root = rc.getFirst();
+ return f.apply(root);
+ });
+ }
+
+ private static <T> T withTempDirectory(ThrowingFunction<Path, T> f) throws Exception {
+ Path directory = Files.createTempDirectory("application");
+ try {
+ return f.apply(directory);
+ } finally {
+ IOUtils.recursiveDeleteDir(directory.toFile());
+ }
+ }
+
+ private static void writeServicesXml(Path applicationPath, String servicesXml) throws IOException {
+ Path path = applicationPath.resolve("services.xml");
+ List<String> output = Arrays.asList("<?xml version=\"1.0\" encoding=\"utf-8\"?>", servicesXml);
+ Files.write(path, output, StandardCharsets.UTF_8);
+ }
+}
diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java
index 8d413ade0f0..71668b595a0 100644
--- a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java
+++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerActivatorTest.java
@@ -1,4 +1,4 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.standalone;
import com.google.inject.Module;
@@ -25,7 +25,6 @@ import static org.junit.Assert.assertThat;
/**
* @author Einar M R Rosenvinge
- * @since 5.22.0
*/
public class StandaloneContainerActivatorTest {
@@ -100,7 +99,7 @@ public class StandaloneContainerActivatorTest {
private static Module newAppDirBinding(final Path applicationDir) {
return binder -> binder.bind(Path.class)
- .annotatedWith(StandaloneContainerApplication.applicationPathName())
+ .annotatedWith(StandaloneContainerApplication.APPLICATION_PATH_NAME)
.toInstance(applicationDir);
}
diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerTest.java
new file mode 100644
index 00000000000..6d4abc84dbc
--- /dev/null
+++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneContainerTest.java
@@ -0,0 +1,74 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.standalone;
+
+import com.yahoo.vespa.model.AbstractService;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Tony Vaagenes
+ * @author gjoranv
+ * @author ollivir
+ */
+
+public class StandaloneContainerTest {
+ private static final String PLAIN_XML = "<container version=\"1.0\" />";
+
+ @Test
+ public void container_is_allowed_root_element() throws Exception {
+ StandaloneContainer.withContainerModel(PLAIN_XML, root -> null);
+ }
+
+ @Test
+ public void services_is_allowed_root_element() throws Exception {
+ String servicesXml = "<services>" + //
+ "<container version=\"1.0\" />" + //
+ "</services>";
+
+ StandaloneContainer.withContainerModel(servicesXml, root -> null);
+ }
+
+ @Test(expected = Exception.class)
+ public void multiple_container_elements_cannot_be_deployed() throws Exception {
+ String twoContainersXml = "<services>" + //
+ "<container id=\"container-1\" version=\"1.0\" />" + //
+ "<container id=\"container-2\" version=\"1.0\" />" + //
+ "</services>";
+
+ StandaloneContainer.withContainerModel(twoContainersXml, root -> null);
+ }
+
+ @Test
+ public void application_preprocessor_is_run() throws Exception {
+ String servicesXml = "<services xmlns:preprocess=\"properties\">" + //
+ "<preprocess:properties>" + //
+ "<container_id>container-1</container_id>" + //
+ "</preprocess:properties>" + //
+ "<container id=\"${container_id}\" version=\"1.0\" />" + //
+ "</services>";
+
+ StandaloneContainer.withContainerModel(servicesXml, root -> {
+ assertTrue(root.getConfigProducer("container-1/standalone").isPresent());
+ return null;
+ });
+ }
+
+ @Test
+ public void no_default_ports_are_enabled_when_using_http() throws Exception {
+ String xml = "<jdisc version=\"1.0\">" + //
+ "<http>" + //
+ "<server port=\"4000\" id=\"server1\" />" + //
+ "</http>" + //
+ "</jdisc>";
+
+ StandaloneContainer.withContainerModel(xml, root -> {
+ AbstractService container = (AbstractService) root.getConfigProducer("jdisc/standalone").get();
+ System.out.println("portCnt: " + container.getPortCount());
+ System.out.println("numPorts: " + container.getNumPortsAllocated());
+ assertEquals(1, container.getNumPortsAllocated());
+ return null;
+ });
+ }
+}
diff --git a/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java
new file mode 100644
index 00000000000..dd755e8e6dd
--- /dev/null
+++ b/standalone-container/src/test/java/com/yahoo/container/standalone/StandaloneSubscriberTest.java
@@ -0,0 +1,52 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.container.standalone;
+
+import com.yahoo.config.ConfigInstance;
+import com.yahoo.container.BundlesConfig;
+import com.yahoo.container.ComponentsConfig;
+import com.yahoo.container.di.config.Subscriber;
+import com.yahoo.vespa.config.ConfigKey;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static com.yahoo.container.standalone.StandaloneContainer.withContainerModel;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.number.OrderingComparison.greaterThan;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author Tony Vaagenes
+ * @author ollivir
+ */
+public class StandaloneSubscriberTest {
+ private static ConfigKey<ConfigInstance> bundlesKey = key("bundles");
+ private static ConfigKey<ConfigInstance> componentsKey = key("components");
+
+ private static ConfigKey<ConfigInstance> key(String name) {
+ return new ConfigKey<>(name, "container", "container");
+ }
+
+ @Test
+ @Ignore
+ public void standalone_subscriber() throws Exception {
+ withContainerModel("<container version=\"1.0\"></container>", root -> {
+ Set<ConfigKey<ConfigInstance>> keys = new HashSet<>();
+ keys.add(bundlesKey);
+ keys.add(componentsKey);
+ Subscriber subscriber = new StandaloneSubscriberFactory(root).getSubscriber(keys);
+ Map<ConfigKey<ConfigInstance>, ConfigInstance> config = subscriber.config();
+ assertThat(config.size(), is(2));
+
+ BundlesConfig bundlesConfig = (BundlesConfig) config.get(bundlesKey);
+ ComponentsConfig componentsConfig = (ComponentsConfig) config.get(componentsKey);
+
+ assertThat(bundlesConfig.bundle().size(), is(0));
+ assertThat(componentsConfig.components().size(), greaterThan(10));
+ return null;
+ });
+ }
+}
diff --git a/standalone-container/src/test/scala/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.scala b/standalone-container/src/test/scala/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.scala
deleted file mode 100644
index d4baea43ba2..00000000000
--- a/standalone-container/src/test/scala/com/yahoo/container/standalone/CloudConfigInstallVariablesTest.scala
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-import com.yahoo.container.standalone.CloudConfigInstallVariables.{toConfigModelsPluginDir, toConfigServer, toConfigServers}
-import org.hamcrest.CoreMatchers.is
-import org.hamcrest.Matchers.arrayContaining
-import org.junit.Assert.assertThat
-import org.junit.Test
-
-/**
- * @author Ulf Lilleengen
- * @author Tony Vaagenes
- */
-class CloudConfigInstallVariablesTest {
-
- @Test
- def test_configserver_parsing {
- val parsed = toConfigServers("myhost.mydomain.com")
- assertThat(parsed.length, is(1))
- }
-
- @Test
- def port_can_be_configured {
- val parsed = toConfigServers("myhost:123")
- val port: Int = parsed(0).port.get()
- assertThat(port, is(123))
- }
-
- @Test
- def multiple_spaces_are_supported {
- val parsed = toConfigServers("test1 test2")
- assertThat(parsed.size, is(2))
-
- val hostNames = parsed.map(_.hostName)
- assertThat(hostNames, arrayContaining("test1", "test2"))
- }
-
- @Test(expected = classOf[IllegalArgumentException])
- def missing_port_gives_exception {
- toConfigServer("myhost:")
- }
-
- @Test(expected = classOf[IllegalArgumentException])
- def non_numeric_port_gives_exception {
- toConfigServer("myhost:non-numeric")
- }
-
- @Test
- def string_arrays_are_split_on_spaces {
- val parsed = toConfigModelsPluginDir("/home/vespa/foo /home/vespa/bar ")
- assertThat(parsed.size, is(2))
- }
-
- @Test
- def string_arrays_are_split_on_comma {
- val parsed = toConfigModelsPluginDir("/home/vespa/foo,/home/vespa/bar,")
- assertThat(parsed.size, is(2))
- }
-}
diff --git a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainer.scala b/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainer.scala
deleted file mode 100644
index 33f9a2e8594..00000000000
--- a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainer.scala
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-import com.yahoo.config.model.producer.AbstractConfigProducerRoot
-import com.yahoo.config.model.test.MockRoot
-import com.yahoo.container.Container
-import com.yahoo.jdisc.test.TestDriver
-import scala.xml.Node
-import com.yahoo.vespa.model.VespaModel
-import com.yahoo.io.IOUtils
-import java.nio.file.{Files, Path}
-import com.yahoo.vespa.model.container.xml.ContainerModelBuilder.Networking
-
-/**
- * Creates a local application from vespa-services fragments.
- *
- * @author tonytv
- */
-object StandaloneContainer {
- def firstContainerId(root: AbstractConfigProducerRoot): String = {
- root.getConfigProducer("container").get().getConfigId
- }
-
- def withStandaloneContainer[T](containerNode: Node) {
- withTempDirectory { applicationDirectory =>
- System.setProperty(StandaloneContainerApplication.applicationLocationInstallVariable, applicationDirectory.toString)
- createServicesXml(applicationDirectory, containerNode)
-
- val driver = TestDriver.newInjectedApplicationInstance(classOf[StandaloneContainerApplication])
- driver.close()
- Container.resetInstance()
- }
- }
-
- def withContainerModel[T](containerNode: Node)(f: VespaModel => T) {
- withTempDirectory { applicationPath =>
- createServicesXml(applicationPath, containerNode)
-
- val distributedFiles = new LocalFileDb(applicationPath)
- val (root, container) = StandaloneContainerApplication.createContainerModel(
- applicationPath,
- distributedFiles,
- applicationPath.resolve("preprocesedApp").toFile,
- networkingOption = Networking.enable)
- f(root)
- }
- }
-
- private def withTempDirectory[T](f : Path => T) : T = {
- val directory = Files.createTempDirectory("application")
- try {
- f(directory)
- } finally {
- IOUtils.recursiveDeleteDir(directory.toFile)
- }
- }
-
- private def createServicesXml(applicationPath : Path,
- containerNode: Node) {
-
- scala.xml.XML.save(applicationPath.resolve("services.xml").toFile.getAbsolutePath,
- containerNode, xmlDecl = true)
- }
-}
diff --git a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainerTest.scala b/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainerTest.scala
deleted file mode 100644
index 87bef2efd95..00000000000
--- a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneContainerTest.scala
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-
-import com.yahoo.container.standalone.StandaloneContainerTest._
-import com.yahoo.vespa.model.AbstractService
-import org.junit.Assert._
-import org.junit.Test
-
-import scala.util.Try
-
-
-/**
- * @author tonytv
- * @author gjoranv
- */
-
-class StandaloneContainerTest {
- @Test
- def container_is_allowed_root_element() {
- StandaloneContainer.withContainerModel(plainXml) { root => }
- }
-
- @Test
- def services_is_allowed_root_element() {
- val servicesXml =
- <services>
- <container version="1.0" />
- </services>
-
- StandaloneContainer.withContainerModel(servicesXml) { root => }
- }
-
- @Test
- def multiple_container_elements_cannot_be_deployed() {
- val twoContainersXml =
- <services>
- <container id="container-1" version="1.0" />
- <container id="container-2" version="1.0" />
- </services>
-
- assertTrue(
- Try {
- StandaloneContainer.withContainerModel(twoContainersXml) { root => }
- }.isFailure)
- }
-
- @Test
- def application_preprocessor_is_run() {
- val servicesXml =
- <services xmlns:preprocess="properties">
- <preprocess:properties>
- <container_id>container-1</container_id>
- </preprocess:properties>
- <container id="${container_id}" version="1.0" />
- </services>
- StandaloneContainer.withContainerModel(servicesXml) {
- root =>
- assertTrue(root.getConfigProducer("container-1/standalone").isPresent)
- }
- }
-
- @Test
- def no_default_ports_are_enabled_when_using_http() {
- val xml =
- <jdisc version="1.0">
- <http>
- <server port="4000" id="server1" />
- </http>
- </jdisc>
-
- StandaloneContainer.withContainerModel(xml) { root =>
- val container = root.getConfigProducer("jdisc/standalone").get().asInstanceOf[AbstractService]
- println("portCnt: " + container.getPortCount)
- println("numPorts: " + container.getNumPortsAllocated)
- assertEquals(1, container.getNumPortsAllocated)
- }
- }
-
-}
-
-object StandaloneContainerTest {
-
- val plainXml = <container version="1.0" />
-}
diff --git a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneSubscriberTest.scala b/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneSubscriberTest.scala
deleted file mode 100644
index ab6f486c748..00000000000
--- a/standalone-container/src/test/scala/com/yahoo/container/standalone/StandaloneSubscriberTest.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.container.standalone
-
-import org.junit.{Ignore, Test}
-import org.junit.Assert.assertThat
-import org.hamcrest.CoreMatchers.is
-import org.hamcrest.number.OrderingComparison.greaterThan
-
-import StandaloneContainer.withContainerModel
-import com.yahoo.vespa.config.ConfigKey
-import com.yahoo.config.ConfigInstance
-import com.yahoo.container.{ComponentsConfig, BundlesConfig, di}
-import scala.collection.JavaConverters._
-
-/**
- * @author tonytv
- */
-class StandaloneSubscriberTest {
- val bundlesKey = key("bundles")
- val componentsKey = key("components")
-
- def key(name: String) = new ConfigKey(name, "container", "container").asInstanceOf[ConfigKey[ConfigInstance]]
-
- def box(i: Int) = java.lang.Integer.valueOf(i)
-
- @Test
- @Ignore
- def standalone_subscriber() {
- withContainerModel(<container version="1.0"> </container>) { root =>
- val subscriber = new StandaloneSubscriberFactory(root).getSubscriber(Set(bundlesKey, componentsKey).asJava)
- val config = subscriber.config.asScala
- assertThat(config.size, is(2))
-
- val bundlesConfig = config(bundlesKey).asInstanceOf[BundlesConfig]
- val componentsConfig = config(componentsKey).asInstanceOf[ComponentsConfig]
-
- assertThat(bundlesConfig.bundle().size(), is(0))
- assertThat(box(componentsConfig.components().size()), greaterThan(box(10)))
- }
- }
-}