diff --git a/project/pom.xml b/project/pom.xml index 787c34bb16..a5c95d51c7 100644 --- a/project/pom.xml +++ b/project/pom.xml @@ -565,6 +565,7 @@ pageTracker._trackPageview(); ${jclouds.osgi.import} ${jclouds.osgi.dynamic} ${jclouds.osgi.fragment} + ${jclouds.osgi.activator} diff --git a/scriptbuilder/pom.xml b/scriptbuilder/pom.xml index 66626a3138..d320847bd4 100644 --- a/scriptbuilder/pom.xml +++ b/scriptbuilder/pom.xml @@ -35,6 +35,7 @@ + org.jclouds.scriptbuilder.osgi.Activator org.jclouds.scriptbuilder*;version="${project.version}" org.jclouds*;version="${project.version}",* @@ -52,6 +53,12 @@ 1.46 true + + org.osgi + org.osgi.core + 4.2.0 + provided + diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/BasicFunctionLoader.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/BasicFunctionLoader.java new file mode 100644 index 0000000000..1c4c011b2b --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/BasicFunctionLoader.java @@ -0,0 +1,51 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.scriptbuilder; + +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; +import com.google.common.io.Resources; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.ShellToken; +import org.jclouds.util.ClassLoadingUtils; + +import java.io.IOException; + +/** + * A {@link FunctionLoader} implementation which loads the target function from the classpath. + */ +public class BasicFunctionLoader implements FunctionLoader { + + /** + * Loads a function from the classpath using the current or the Thread Context Class Loader. + * @param function The function name to load. + * @param family This operating system family of the function. + * @return The function as {@link String} + * @throws FunctionNotFoundException + */ + @Override + public String loadFunction(String function, OsFamily family) throws FunctionNotFoundException { + try { + return CharStreams.toString(Resources.newReaderSupplier(ClassLoadingUtils.loadResource(BasicFunctionLoader.class, String + .format("/functions/%s.%s", function, ShellToken.SH.to(family))), Charsets.UTF_8)); + } catch (IOException e) { + throw new FunctionNotFoundException(function, family, e); + } + } +} diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/FunctionLoader.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/FunctionLoader.java new file mode 100644 index 0000000000..1396ed736d --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/FunctionLoader.java @@ -0,0 +1,37 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.scriptbuilder; + +import org.jclouds.scriptbuilder.domain.OsFamily; + +/** + * A function loader interface. + */ +public interface FunctionLoader { + + /** + * Loads a function and returns it as {@link String}. + * @param function The function name to load. + * @param family This operating system family of the function. + * @return The function as {@link String}. + * @throws FunctionNotFoundException + */ + public String loadFunction(String function, OsFamily family) throws FunctionNotFoundException; + +} diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/FunctionNotFoundException.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/FunctionNotFoundException.java new file mode 100644 index 0000000000..861add54d5 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/FunctionNotFoundException.java @@ -0,0 +1,36 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.scriptbuilder; + +import org.jclouds.scriptbuilder.domain.OsFamily; + +public class FunctionNotFoundException extends RuntimeException { + /** + * The serialVersionUID + */ + private static final long serialVersionUID = 1L; + + public FunctionNotFoundException(String functionName, OsFamily family) { + super("function: " + functionName + " not found for famiy: " + family); + } + + public FunctionNotFoundException(String functionName, OsFamily family, Throwable cause) { + super("function: " + functionName + " not found for famiy: " + family, cause); + } +} diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/Activator.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/Activator.java new file mode 100644 index 0000000000..4643be4618 --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/Activator.java @@ -0,0 +1,76 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.scriptbuilder.osgi; + +import org.jclouds.scriptbuilder.util.Utils; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + BundleFunctionLoader functionLoader; + + /** + * Called when this bundle is started so the Framework can perform the + * bundle-specific activities necessary to start this bundle. This method + * can be used to register services or to allocate any resources that this + * bundle needs. + *

+ *

+ * This method must complete and return to its caller in a timely manner. + * + * @param context The execution context of the bundle being started. + * @throws Exception If this method throws an exception, this + * bundle is marked as stopped and the Framework will remove this + * bundle's listeners, unregister all services registered by this + * bundle, and release all services used by this bundle. + */ + @Override + public void start(BundleContext context) throws Exception { + functionLoader = new BundleFunctionLoader(context); + functionLoader.start(); + Utils.functionLoader = new ServiceFunctionLoader(context); + + } + + /** + * Called when this bundle is stopped so the Framework can perform the + * bundle-specific activities necessary to stop the bundle. In general, this + * method should undo the work that the BundleActivator.start + * method started. There should be no active threads that were started by + * this bundle when this bundle returns. A stopped bundle must not call any + * Framework objects. + *

+ *

+ * This method must complete and return to its caller in a timely manner. + * + * @param context The execution context of the bundle being stopped. + * @throws Exception If this method throws an exception, the + * bundle is still marked as stopped, and the Framework will remove + * the bundle's listeners, unregister all services registered by the + * bundle, and release all services used by the bundle. + */ + @Override + public void stop(BundleContext context) throws Exception { + if (functionLoader != null) { + functionLoader.stop(); + functionLoader = null; + } + } +} diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/BundleFunctionLoader.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/BundleFunctionLoader.java new file mode 100644 index 0000000000..18e2da772e --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/BundleFunctionLoader.java @@ -0,0 +1,105 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.scriptbuilder.osgi; + +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; +import com.google.common.io.Resources; +import org.jclouds.scriptbuilder.FunctionLoader; +import org.jclouds.scriptbuilder.FunctionNotFoundException; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.ShellToken; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +import java.io.IOException; +import java.net.URL; +import java.util.Enumeration; +import java.util.Properties; + +/** + * A {@link FunctionLoader} which searches for functions in the {@link Bundle} resources. + */ +public class BundleFunctionLoader implements FunctionLoader { + + private final BundleContext bundleContext; + private ServiceRegistration registration; + + /** + * Constructor + * + * @param bundleContext + */ + public BundleFunctionLoader(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + /** + * Starts the loader. + * Looks up for {@link Bundle} resources and registers itself in the service registry. + * It adds a property to the service which advertise all the functions found in the local resources. + */ + public void start() { + Bundle bundle = bundleContext.getBundle(); + Enumeration entries = bundle.findEntries("/functions/", "*.*", false); + StringBuilder sb = new StringBuilder(); + while (entries.hasMoreElements()) { + URL url = (URL) entries.nextElement(); + String function = url.getFile(); + sb.append(function); + if (entries.hasMoreElements()) { + sb.append(" "); + } + } + String functions = sb.toString(); + registerFunction(functions); + } + + /** + * Unregisters itself from the service registry. + */ + public void stop() { + registration.unregister(); + } + + + /** + * Loads the function from the {@link Bundle} resources. + * @param function The function name to load. + * @param family This operating system family of the function. + * @return + * @throws FunctionNotFoundException + */ + @Override + public String loadFunction(String function, OsFamily family) throws FunctionNotFoundException { + try { + return CharStreams.toString(Resources.newReaderSupplier(bundleContext.getBundle().getResource(String.format("/functions/%s.%s", function, ShellToken.SH.to(family))), Charsets.UTF_8)); + } catch (IOException e) { + throw new FunctionNotFoundException(function, family, e); + } + } + + private void registerFunction(String functions) { + Properties properties = new Properties(); + properties.put("function",functions); + registration = bundleContext.registerService(FunctionLoader.class.getName(), this,properties); + } + +} diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/ServiceFunctionLoader.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/ServiceFunctionLoader.java new file mode 100644 index 0000000000..44ac9c725b --- /dev/null +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/osgi/ServiceFunctionLoader.java @@ -0,0 +1,73 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.scriptbuilder.osgi; + +import com.google.common.base.Strings; +import org.jclouds.scriptbuilder.FunctionLoader; +import org.jclouds.scriptbuilder.FunctionNotFoundException; +import org.jclouds.scriptbuilder.domain.OsFamily; +import org.jclouds.scriptbuilder.domain.ShellToken; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +/** + * A {@link FunctionLoader} implementation which delegates loading to the OSGi service registry. + */ +public class ServiceFunctionLoader implements FunctionLoader { + + private final BundleContext bundleContext; + + public ServiceFunctionLoader(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + /** + * Looks up the service registry for an applicable {@link FunctionLoader} and delegates to it. + * @param function The function name to load. + * @param family This operating system family of the function. + * @return The function as {@link String} + * @throws FunctionNotFoundException + */ + @Override + public String loadFunction(String function, OsFamily family) throws FunctionNotFoundException { + ServiceReference[] references = null; + String filter = String.format("(function=*%s.%s*)", function, ShellToken.SH.to(family)); + try { + references = bundleContext.getServiceReferences(FunctionLoader.class.getName(), filter); + for (ServiceReference reference : references) { + FunctionLoader loader = (FunctionLoader) bundleContext.getService(reference); + String f = loader.loadFunction(function, family); + if (!Strings.isNullOrEmpty(f)) { + return f; + } + } + } catch (InvalidSyntaxException e) { + throw new FunctionNotFoundException(function, family, e); + } finally { + if (references != null) { + for (ServiceReference reference : references) { + bundleContext.ungetService(reference); + } + } + } + throw new FunctionNotFoundException(function, family); + } + +} diff --git a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java index 4f11529088..cf76382cdf 100644 --- a/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java +++ b/scriptbuilder/src/main/java/org/jclouds/scriptbuilder/util/Utils.java @@ -18,25 +18,22 @@ */ package org.jclouds.scriptbuilder.util; -import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.jclouds.scriptbuilder.BasicFunctionLoader; +import org.jclouds.scriptbuilder.FunctionLoader; import org.jclouds.scriptbuilder.domain.OsFamily; import org.jclouds.scriptbuilder.domain.ShellToken; -import org.jclouds.util.ClassLoadingUtils; import com.google.common.base.CaseFormat; -import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; -import com.google.common.io.CharStreams; -import com.google.common.io.Resources; /** * Utilities used to build init scripts. @@ -47,6 +44,8 @@ public class Utils { public static final LowerCamelToUpperUnderscore FUNCTION_LOWER_CAMEL_TO_UPPER_UNDERSCORE = new LowerCamelToUpperUnderscore(); + public static FunctionLoader functionLoader = new BasicFunctionLoader(); + public static final class LowerCamelToUpperUnderscore implements Function { @Override public String apply(String from) { @@ -135,30 +134,12 @@ public class Utils { } public static String writeFunctionFromResource(String function, OsFamily family) { - try { - String toReturn = CharStreams.toString(Resources.newReaderSupplier(ClassLoadingUtils.loadResource(Utils.class, String - .format("/functions/%s.%s", function, ShellToken.SH.to(family))), Charsets.UTF_8)); + String toReturn = functionLoader.loadFunction(function,family); String lf = ShellToken.LF.to(family); return toReturn.endsWith(lf) ? toReturn : new StringBuilder(toReturn).append(lf).toString(); - } catch (IOException e) { - throw new FunctionNotFoundException(function, family, e); - } } - public static class FunctionNotFoundException extends RuntimeException { - /** The serialVersionUID */ - private static final long serialVersionUID = 1L; - - public FunctionNotFoundException(String functionName, OsFamily family) { - super("function: " + functionName + " not found for famiy: " + family); - } - - public FunctionNotFoundException(String functionName, OsFamily family, Throwable cause) { - super("function: " + functionName + " not found for famiy: " + family, cause); - } - } - - public static String writeFunction(String function, String source) { + public static String writeFunction(String function, String source) { return String.format("{fncl}%s{fncr}%s{fnce}", function, source.replaceAll("^", " ")); }