http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/custom-distribution.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/custom-distribution.adoc b/manual/src/main/asciidoc/developer-guide/custom-distribution.adoc new file mode 100644 index 0000000..8a6eb83 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/custom-distribution.adoc @@ -0,0 +1,650 @@ +// +// Licensed 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. +// + +== Custom distributions + +As Karaf is an OSGi container, it's heavily used as as application and middleware kernel. + +You may wish to construct your own Karaf distribution preconfigured to your requirements. + +This custom distribution could contain: + +* branding to change the Karaf console look-and-feel +* configuration files (in the etc folder) altered to your requirements +* pre-packaged artifacts in the deploy folder +* a pre-populated system repository (containing your own bundle and features descriptor) +* renamed or specific scripts in the bin folder +* system documentation files + +=== Maven assembly + +The recommended way to create a Karaf server assembly is to use the karaf-assembly packaging with the karaf-maven-plugin. +This assembles a server from the maven dependencies in the project pom. After explanation of the configuration options +we present an example. + +The Karaf project effectively uses this packaging to assemble the official Karaf distributions, but due to maven +limitations we have to simulate rather than use the karaf-assembly packaging. + +This packaging creates tar.gz and zip archives containing the assembled server. +They are identical except that zip archives don't unpack with appropriate unix file permissions for the scripts. + +==== Maven dependencies + +Maven dependencies in a karaf-assembly project can be feature repositories (classifier "features") or kar archives. +Feature repositories are installed in the internal "system" Maven structured repository. +Kar archives have their content unpacked on top of the server as well as contained feature repositories installed. + +The Maven scope of a dependency determines whether its feature repository is listed in the features service +configuration file `etc/org.apache.karaf.features.cfg` `featuresRepositories` property: + +* compile (default): All the features in the repository (or for a kar repositories) will be installed into the +`startup.properties`. The feature repo is not listed in the features service configuration file. +* runtime: feature installation is controlled by `<startupFeature>`, `<bootFeature>`, and `<installedFeature>` elements +in the karaf-maven-plugin configuration. The feature repo uri is listed in the features service configuration file. + +==== Plugin configuration + +Control how features are installed using these elements referring to features from installed feature repositories: + +* `<startupFeature>foo</startupFeature>` - This will result in the feature bundles being listed in startup.properties at +the appropriate start level and the bundles being copied into the "system" internal repository. +You can use feature_name or feature_name/feature_version formats. +* `<bootFeature>bar</bootFeature>` - This will result in the feature name added to boot-features in the features +service configuration file and all the bundles in the feature copied into the "system" internal repository. +You can use feature_name or feature_name/feature_version formats. +* `<installedFeature>baz</installedFeature>` - This will result in all the bundles in the feature being installed in +the "system" internal repository. Therefore at runtime the feature may be installed without access to external repositories. +You can use feature_name or feature_name/feature_version formats. + +==== Minimal Distribution Example + +This is the minimal assembly pom changed to use the packaging and annotated + +---- +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> +... + </parent> + + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf-minimal</artifactId> + <version>${project.version}</version> + <packaging>karaf-assembly</packaging> + <name>Apache Karaf :: Assemblies :: Minimal Distribution</name> + + <dependencies> + <dependency> + <!-- scope is compile so all features (there is only one) are installed into startup.properties and the feature repo itself is not added in etc/org.apache.karaf.features.cfg file --> + <groupId>org.apache.karaf.features</groupId> + <artifactId>framework</artifactId> + <version>${project.version}</version> + <type>kar</type> + </dependency> + <dependency> + <!-- scope is runtime so the feature repo is listed in etc/org.apache.karaf.features.cfg file, and features will installed into the system directory --> + <groupId>org.apache.karaf.features</groupId> + <artifactId>standard</artifactId> + <classifier>features</classifier> + <type>xml</type> + <scope>runtime</scope> + </dependency> + </dependencies> + + <build> + <!-- if you want to include resources in the distribution --> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>false</filtering> + <includes> + <include>**/*</include> + </includes> + </resource> + <resource> + <directory>src/main/filtered-resources</directory> + <filtering>true</filtering> + <includes> + <include>**/*</include> + </includes> + </resource> + </resources> + + <plugins> + <!-- if you want to include resources in the distribution --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>process-resources</id> + <goals> + <goal>resources</goal> + </goals> + </execution> + </executions> + </plugin> + <!-- karaf-maven-plugin will call both assembly and archive goals --> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <!-- no startupFeatures --> + <bootFeatures> + <feature>standard</feature> + </bootFeatures> + <!-- no installedFeatures --> + </configuration> + </plugin> + </plugins> + </build> +</project> +---- + +==== Custom Distribution Example + +It's possible to specify feature versions using the name/version format. + +For instance, to pre-install Spring 4.0.7.RELEASE_1 feature in your custom distribution, you can use the following pom.xml: + +---- +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <groupId>my.custom</groupId> + <artifactId>my.distribution</artifactId> + <version>1.0</version> + <packaging>karaf-assembly</packaging> + + <dependencies> + <dependency> + <!-- scope is compile so all features (there is only one) are installed into startup.properties and the feature repo itself is not added in etc/org.apache.karaf.features.cfg file --> + <groupId>org.apache.karaf.features</groupId> + <artifactId>framework</artifactId> + <version>4.0.0</version> + <type>kar</type> + </dependency> + <dependency> + <!-- scope is runtime so the feature repo is listed in etc/org.apache.karaf.features.cfg file, and features will installed into the system directory if specify in the plugin configuration --> + <groupId>org.apache.karaf.features</groupId> + <artifactId>standard</artifactId> + <classifier>features</classifier> + <type>xml</type> + <scope>runtime</scope> + </dependency> + <dependency> + <!-- scope is runtime so the feature repo is listed in etc/org.apache.karaf.features.cfg file, and features will installed into the system directory if specify in the plugin configuration --> + <groupId>org.apache.karaf.features</groupId> + <artifactId>spring</artifactId> + <classifier>features</classifier> + <type>xml</type> + <scope>runtime</scope> + </dependency> + </dependencies> + + <build> + <!-- if you want to include resources in the distribution --> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>false</filtering> + <includes> + <include>**/*</include> + </includes> + </resource> + <resource> + <directory>src/main/filtered-resources</directory> + <filtering>true</filtering> + <includes> + <include>**/*</include> + </includes> + </resource> + </resources> + + <plugins> + <!-- if you want to include resources in the distribution --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <version>2.6</version> + <executions> + <execution> + <id>process-resources</id> + <goals> + <goal>resources</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>4.0.0</version> + <extensions>true</extensions> + <configuration> + <!-- no startupFeatures --> + <bootFeatures> + <feature>minimal</feature> + </bootFeatures> + <installedFeatures> + <feature>wrapper</feature> + <feature>spring/4.0.7.RELEASE_1</feature> + </installedFeatures> + </configuration> + </plugin> + </plugins> + </build> +</project> +---- + +=== (deprecated old style) Maven assembly + +Basically a Karaf custom distribution involves: + +. Uncompressing a standard Karaf distribution in a given directory. +. Populating the system repo with your features. +. Populating the lib directory with your branding or other system bundle jar files. +. Overriding the configuration files in the etc folder. + +These tasks could be performed using scripting, or more easily and portable, using Apache Maven and a set of Maven plugins. + +For instance, the Maven POM could look like: + +---- +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <groupId>my.company</groupId> + <artifactId>mycustom-karaf</artifactId> + <version>1.0</version> + <packaging>pom</packaging> + <name>My Unix Custom Karaf Distribution</name> + + <properties> + <karaf.version>${project.version}</karaf.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf</artifactId> + <version>${karaf.version}</version> + <type>tar.gz</type> + </dependency> + <dependency> + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf</artifactId> + <version>${karaf.version}</version> + <type>xml</type> + <classifier>features</classifier> + </dependency> + </dependencies> + + <build> + <resources> + <resource> + <directory>${project.basedir}/src/main/filtered-resources</directory> + <filtering>true</filtering> + <includes> + <include>**/*</include> + </includes> + </resource> + </resources> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <executions> + <execution> + <id>filter</id> + <phase>generate-resources</phase> + <goals> + <goal>resources</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>${karaf.version}</version> + <executions> + <execution> + <id>add-features-to-repo</id> + <phase>generate-resources</phase> + <goals> + <goal>features-add-to-repo</goal> + </goals> + <configuration> + <descriptors> + <descriptor>mvn:org.apache.karaf/apache-karaf/${karaf.version}/xml/features</descriptor> + <descriptor>file:${project.basedir}/target/classes/my-features.xml</descriptor> + </descriptors> + <features> + <feature>my-feature</feature> + </features> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy</id> + <phase>generate-resources</phase> + <goals> + <goal>copy</goal> + </goals> + <configuration> + <!-- Define here the artifacts which should be considered in the assembly --> + <!-- For instance, the branding jar --> + <artifactItems> + <artifactItem> + <groupId>my.groupId</groupId> + <artifactId>my.branding.id</artifactId> + <version>1.0</version> + <outputDirectory>target/dependencies</outputDirectory> + <destFileName>mybranding.jar</destFileName> + </artifactItem> + </artifactItems> + </configuration> + </execution> + <execution> + <!-- Uncompress the standard Karaf distribution --> + <id>unpack</id> + <phase>generate-resources</phase> + <goals> + <goal>unpack</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>org.apache.karaf</groupId> + <artifactId>apache-karaf</artifactId> + <type>tar.gz</type> + <outputDirectory>target/dependencies</outputDirectory> + </artifactItem> + </artifactItems> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>bin</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <descriptors> + <descriptor>src/main/descriptors/bin.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + <tarLongFileMode>gnu</tarLongFileMode> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> +---- + +The Maven POM will download the Karaf standard distribution and prepare resources to be processed by the Maven assembly plugin. + +Your Maven project structure should look like: + +* pom.xml: the previous POM file +* src/main/descriptors/bin.xml: the assembly Maven plugin descriptor (see below) +* src/main/filtered-resources: contains all resource files that have Maven property values to be filtered/replaced. Typically, this will include features descriptor and configuration files. +* src/main/distribution: contains all raw files which will be copied as-is into your custom distribution. + +For instance, `src/main/filtered-resources` could contain: + +* `my-features.xml` where Maven properties will be replaced +* `etc/org.apache.karaf.features.cfg` file containing your my-features descriptor: + +---- +# +# Comma separated list of features repositories to register by default +# +featuresRepositories=mvn:org.apache.karaf/apache-karaf/${karaf.version}/xml/features,mvn:my.groupId/my-features/${project.version}/xml/features + +# +# Comma separated list of features to install at startup +# +featuresBoot=config,ssh,management,my-feature +---- + +The `src/main/distribution` contains all your custom Karaf configuration files and script, as, for examples: + +* etc/org.ops4j.pax.logging.cfg + +---- +# Root logger +log4j.rootLogger=INFO, out, osgi:VmLogAppender +log4j.throwableRenderer=org.apache.log4j.OsgiThrowableRenderer + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32C %4L | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n + +# File appender +log4j.appender.out=org.apache.log4j.RollingFileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32C %4L | %X{bundle.id} - %X{bundle.name} - %X{bundle.version} | %m%n +log4j.appender.out.file=${karaf.home}/log/my-customer-distribution.log +log4j.appender.out.append=true +log4j.appender.out.maxFileSize=1MB +log4j.appender.out.maxBackupIndex=10 + +# Sift appender +log4j.appender.sift=org.apache.log4j.sift.MDCSiftingAppender +log4j.appender.sift.key=bundle.name +log4j.appender.sift.default=my-custom +log4j.appender.sift.appender=org.apache.log4j.FileAppender +log4j.appender.sift.appender.layout=org.apache.log4j.PatternLayout +log4j.appender.sift.appender.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n +log4j.appender.sift.appender.file=${karaf.data}/log/$\\{bundle.name\\}.log +log4j.appender.sift.appender.append=true +---- + +* etc/system.properties + +---- +# +# The properties defined in this file will be made available through system +# properties at the very beginning of the FAS boot process. +# + +# Log level when the pax-logging service is not available +# This level will only be used while the pax-logging service bundle +# is not fully available. +# To change log levels, please refer to the org.ops4j.pax.logging.cfg file +# instead. +org.ops4j.pax.logging.DefaultServiceLog.level=ERROR + +# +# Name of this custom instance. +# +karaf.name=my-custom + +# +# Default repository where bundles will be loaded from before using +# other Maven repositories. For the full Maven configuration, see the +# org.ops4j.pax.url.mvn.cfg file. +# +karaf.default.repository=system + +# +# Location of a shell script that will be run when starting a shell +# session. This script can be used to create aliases and define +# additional commands. +# +karaf.shell.init.script=${karaf.home}/etc/shell.init.script + +# +# Set this empty property to avoid errors when validating xml documents. +# +xml.catalog.files= + +# +# Suppress the bell in the console when hitting backspace to many times +# for example +# +jline.nobell=true + +# +# Default port for the OSGi HTTP Service +# +org.osgi.service.http.port=8181 + +# +# Allow usage of ${custom.home} as an alias for ${karaf.home} +# +custom.home=${karaf.home} +---- +* etc/users.properties +---- +admin=admin,admin +---- +* You can add a `etc/custom.properties`, it's a placeholder for any override you may need. For instance: +---- +karaf.systemBundlesStartLevel=50 +obr.repository.url=http://svn.apache.org/repos/asf/servicemix/smx4/obr-repo/repository.xml +org.osgi.framework.system.packages.extra = \ + org.apache.karaf.branding; \ + com.sun.org.apache.xalan.internal.xsltc.trax; \ + com.sun.org.apache.xerces.internal.dom; \ + com.sun.org.apache.xerces.internal.jaxp; \ + com.sun.org.apache.xerces.internal.xni; \ + com.sun.jndi.ldap +---- + +Now, we can "assemble" our custom distribution using the Maven assembly plugin. The Maven assembly plugin uses an +assembly descriptor, configured in POM above to be `src/main/descriptors/bin.xml`: + +---- +<assembly> + + <id>bin</id> + + <formats> + <format>tar.gz</format> + </formats> + + <fileSets> + + <!-- Expanded Karaf Standard Distribution --> + <fileSet> + <directory>target/dependencies/apache-karaf-${karaf.version}</directory> + <outputDirectory>/</outputDirectory> + <excludes> + <exclude>**/demos/**</exclude> + <exclude>bin/**</exclude> + <exclude>etc/system.properties</exclude> + <exclude>etc/users.properties</exclude> + <exclude>etc/org.apache.karaf.features.cfg</exclude> + <exclude>etc/org.ops4j.pax.logging.cfg</exclude> + <exclude>LICENSE</exclude> + <exclude>NOTICE</exclude> + <exclude>README</exclude> + <exclude>RELEASE-NOTES</exclude> + <exclude>karaf-manual*.html</exclude> + <exclude>karaf-manual*.pdf</exclude> + </excludes> + </fileSet> + + <!-- Copy over bin/* separately to get the correct file mode --> + <fileSet> + <directory>target/dependencies/apache-karaf-${karaf.version}</directory> + <outputDirectory>/</outputDirectory> + <includes> + <include>bin/admin</include> + <include>bin/karaf</include> + <include>bin/start</include> + <include>bin/stop</include> + </includes> + <fileMode>0755</fileMode> + </fileSet> + + <!-- Copy over jar files --> + <fileSet> + <directory>target/dependencies</directory> + <includes> + <include>my-custom.jar</include> + </includes> + <outputDirectory>/lib/</outputDirectory> + </fileSet> + + <fileSet> + <directory>src/main/distribution</directory> + <outputDirectory>/</outputDirectory> + <fileMode>0644</fileMode> + </fileSet> + <fileSet> + <directory>target/classes/etc</directory> + <outputDirectory>/etc/</outputDirectory> + <lineEnding>unix</lineEnding> + <fileMode>0644</fileMode> + </fileSet> + + <fileSet> + <directory>target/features-repo</directory> + <outputDirectory>/system</outputDirectory> + </fileSet> + + </fileSets> + + <files> + <file> + <source>${basedir}/target/dependencies/apache-karaf-${karaf.version}/bin/karaf</source> + <outputDirectory>/bin/</outputDirectory> + <destName>my-custom</destName> + <fileMode>0755</fileMode> + <lineEnding>unix</lineEnding> + </file> + <file> + <source>${basedir}/target/classes/features.xml</source> + <outputDirectory>/system/my.groupid/my-features/${project.version}</outputDirectory> + <destName>my-features-${project.version}-features.xml</destName> + <fileMode>0644</fileMode> + <lineEnding>unix</lineEnding> + </file> + </files> + +</assembly> +---- + +To build your custom Karaf distribution, just run: + +---- +mvn install +---- + +You will find your Karaf custom distribution tar.gz in the target directory. \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/debugging.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/debugging.adoc b/manual/src/main/asciidoc/developer-guide/debugging.adoc new file mode 100644 index 0000000..1664438 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/debugging.adoc @@ -0,0 +1,199 @@ +// +// Licensed 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. +// + +== Troubleshooting, Debugging, Profiling, and Monitoring + +=== Troubleshooting + +==== Logging + +Logging is easy to control through the console, with commands grouped under _log_ shell. To learn about the available +logging commands type: + +---- +karaf@root> log<tab> + +log:display log:display-exception log:get log:set +karaf@root> +---- + +Typical usage is: + +. Use `log:set` to dynamically change the global log level +. Execute the problematic operation +. Use `log:display` (or `log:display-exception` to display the log + +==== Worst Case Scenario + +If you end up with a Karaf in a really bad state (i.e. you can not boot it anymore) or you just want to revert to a +clean state quickly, you can safely remove the `data` directory just in the installation directory. This folder +contains transient data and will be recreated if removed when you relaunch Karaf. +You may also want to remove the files in the `deploy` folder to avoid them being automatically installed when Karaf +is started the first time. + +=== Debugging + +Usually, the easiest way to debug Karaf or any application deployed onto it is to use remote debugging. +Remote debugging can be easily activated by using the `debug` parameter on the command line. + +---- +> bin/karaf debug +---- + +or on Windows + +---- +> bin\karaf.bat debug +---- + +Another option is to set the `KARAF_DEBUG` environment variable to `TRUE`. + +This can be done using the following command on Unix systems: + +---- +export KARAF_DEBUG=true +---- + +On Windows, use the following command + +---- +set KARAF_DEBUG=true +---- + +Then, you can launch Karaf using the usual way: + +---- +bin/karaf +---- + +or + +---- +bin\karaf.bat +---- + +Last, inside your IDE, connect to the remote application (the default port to connect to is 5005). + +This option works fine when it is needed to debug a project deployed top of Apache Karaf. Nervertheless, you will be blocked +if you would like to debug the server Karaf. In this case, you can change the following parameter suspend=y in the +karaf.bat script file. That will cause the JVM to pause just before running main() until you attach a debugger then it +will resume the execution. This way you can set your breakpoints anywhere in the code and you should hit them no matter +how early in the startup they are. + +---- +export DEFAULT_JAVA_DEBUG_OPTS='-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005' +---- + +and on Windows, + +---- +set DEFAULT_JAVA_DEBUG_OPTS='-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005' +---- + +==== Debugging Environment Variables + +|=== +|Variable |Description |Default + +|KARAF_DEBUG +|Set to TRUE to enable debugging. +| + +|EXTRA_JAVA_OPTS +|Java options append to JAVA_OPTS +| + +|JAVA_DEBUG_OPTS +|Java options to enable debuging. +|Calculated based on the OS + +|JAVA_DEBUG_PORT +|Port used by the debugger +|5005 +|=== + +=== Profiling + +==== jVisualVM + +You have to edit the `etc/config.properties` configuration file to add the jVisualVM package: + +---- +org.osgi.framework.bootdelegation=...,org.netbeans.lib.profiler.server +---- + +Run Karaf from the console, and you should now be able to connect using jVisualVM. + +==== YourKit + +You need a few steps to be able to profile Karaf using YourKit. + +The first one is to edit the `etc/config.properties` configuration file and add the following property: + +---- +org.osgi.framework.bootdelegation=...,com.yourkit.* +---- + +Then, set the `JAVA_OPTS` environment variable: + +---- +export JAVA_OPTS='-Xmx512M -agentlib:yjpagent' +---- + +or, on Windows + +---- +set JAVA_OPTS='-Xmx512M -agentlib:yjpagent' +---- + +Run Karaf from the console, and you should now be able to connect using YourKit standalone or from your favorite IDE. + +=== Monitoring + +Karaf uses JMX for monitoring and management of all Karaf components. + +The JMX connection could be: + +* local using the process id + +image:jconsole_connect.jpg[] + +* remote using the `rmiRegistryPort` property defined in `etc/org.apache.karaf.management.cfg` file. + +Using JMX, you can have a clean overview of the running Karaf instance: + +* A overview with graphics displaying the load in terms of thread, heap/GC, etc: + +image:jconsole_overview.jpg[] + +* A thread overview: + +image:jconsole_threads.jpg[] + +* A memory heap consumption, including "Perform GC" button: + +image:jconsole_memory.jpg[] + +* A complete JVM summary, with all number of threads, etc: + +image:jconsole_summary.jpg[] + +You can manage Karaf features like you are in the shell. For example, you have access to the Admin service MBean, +allowing you to create, rename, destroy, change SSH port, etc. Karaf instances: + +image:jconsole_admin.jpg[] + +You can also manage Karaf features MBean to list, install, and uninstall Karaf features: + +image:jconsole_features.jpg[] http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/dev-cdi.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/dev-cdi.adoc b/manual/src/main/asciidoc/developer-guide/dev-cdi.adoc new file mode 100644 index 0000000..f41d8e4 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/dev-cdi.adoc @@ -0,0 +1,15 @@ +// +// Licensed 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. +// + +== CDI \ No newline at end of file http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/developer-commands.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/developer-commands.adoc b/manual/src/main/asciidoc/developer-guide/developer-commands.adoc new file mode 100644 index 0000000..18437b4 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/developer-commands.adoc @@ -0,0 +1,242 @@ +// +// Licensed 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. +// + +== Developer commands + +As you can see in the users guide, Apache Karaf is an enterprise ready OSGi container. + +It's also a container designed to simplify the life for developers and administrators to get details about the +running container. + +=== Dump + +If you encounter issues like performance degradations, weird behaviour, it could be helpful to have a kind of snapshot +about the current activity of the container. + +The `dev:dump-create` command creates a dump file containing: + +* the `bundles.txt` file contains the list of all OSGi bundles, with id, symbolic name, version, current status +* the `features.txt` file contains the list of all features, including current status +* the `environment.txt` file contains details about Apache Karaf, OSGi framework, Operating System, JVM, system + properties, threads count, classes loaded +* the `memory.txt` file contains the status of the JVM memory at the dump time +* the `heapdump.txt` file contains a memory heap dump, with all objects instances, space usage, etc. +* the `threads.txt` file contains a thread dump, with all threads, waiting status, etc. +* the `log` folder contains the `data/log` folder, with all log files. + +By default, the `dev:dump-create` command creates a zip file in the `KARAF_BASE` folder, with the timestamp of the +dump creation: + +---- +karaf@root()> dev:dump-create +Created dump zip: 2015-07-01_171434.zip +---- + +We can see the file generated in the `KARAF_BASE` folder: + +---- +$ cd /opt/apache-karaf-4.0.0 +$ ls -lh *.zip +-rw-rw-r-- 1 user group 17M Jul 1 17:14 2015-07-01_171434.zip +---- + +You can specify the file name of the zip archive: + +---- +karaf@root()> dev:dump-create mydump.zip +Diagnostic dump created. +---- + +Instead of a zip archive, you can create the dump (exploded) in a directory using the `-d` (`--directory`) option: + +---- +karaf@root()> dev:dump-create -d /tmp/mydump +Diagnostic dump created. +---- + +=== Diagnostic + +It's not always easy for the developers to understand why a bundle is not active. + +It could be because the Activator failed, the Blueprint container start failed, etc. + +The `bundle:diag` command gives you details about a bundle is not active: + +---- +karaf@root()> bundle:diag +Apache ServiceMix :: Bundles :: avro-ipc (81) +--------------------------------------------- +Status: Installed +Unsatisfied Requirements: +[81.0] osgi.wiring.package; (&(osgi.wiring.package=javax.servlet)(version>=2.5.0)(!(version>=3.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=javax.servlet.http)(version>=2.5.0)(!(version>=3.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.apache.avro)(version>=1.7.0)(!(version>=2.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.apache.avro.data)(version>=1.7.0)(!(version>=2.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.apache.avro.file)(version>=1.7.0)(!(version>=2.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.apache.avro.generic)(version>=1.7.0)(!(version>=2.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.apache.avro.io)(version>=1.7.0)(!(version>=2.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.apache.avro.reflect)(version>=1.7.0)(!(version>=2.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.apache.avro.specific)(version>=1.7.0)(!(version>=2.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.apache.avro.util)(version>=1.7.0)(!(version>=2.0.0))) +[81.0] osgi.wiring.package; (osgi.wiring.package=org.apache.velocity) +[81.0] osgi.wiring.package; (osgi.wiring.package=org.apache.velocity.app) +[81.0] osgi.wiring.package; (osgi.wiring.package=org.apache.velocity.context) +[81.0] osgi.wiring.package; (osgi.wiring.package=org.apache.velocity.exception) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.netty.bootstrap)(version>=3.4.0)(!(version>=4.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.netty.buffer)(version>=3.4.0)(!(version>=4.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.netty.channel)(version>=3.4.0)(!(version>=4.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.netty.channel.group)(version>=3.4.0)(!(version>=4.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.netty.channel.socket.nio)(version>=3.4.0)(!(version>=4.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.netty.handler.codec.frame)(version>=3.4.0)(!(version>=4.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.netty.handler.codec.oneone)(version>=3.4.0)(!(version>=4.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.jboss.netty.handler.execution)(version>=3.4.0)(!(version>=4.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.mortbay.jetty)(version>=6.1.0)(!(version>=7.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.mortbay.jetty.bio)(version>=6.1.0)(!(version>=7.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.mortbay.jetty.nio)(version>=6.1.0)(!(version>=7.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.mortbay.jetty.servlet)(version>=6.1.0)(!(version>=7.0.0))) +[81.0] osgi.wiring.package; (&(osgi.wiring.package=org.mortbay.resource)(version>=6.1.0)(!(version>=7.0.0))) +---- + +=== Dynamic import + +The `bundle:dynamic-import` command allows you to enable or disable the dynamic import of a given bundle: + +---- +karaf@root()> bundle:dynamic-import 50 +Enabling dynamic imports on bundle org.ops4j.pax.url.wrap [50] +---- + +The purpose of dynamic import is to allow a bundle to be wired up to packages that may not be knwon about in advance. +When a class is requested, if it cannot be solved via the bundle's existing imports, the dynamic import allows other +bundles to be considered for a wiring import to be added. + +The `bundle:dynamic-import` command allows or doesn't allow this behaviour. + +=== OSGi framework + +The `system:framework` command allows to display the current OSGi framework in use, and enable/disable debugging inside the OSGi framework. + +---- +karaf@root()> system:framework +Current OSGi framework is felix +karaf@root()> system:framework -debug +Enabling debug for OSGi framework (felix) +karaf@root()> system:framework -nodebug +Disabling debug for OSGi framework (felix) +---- + +=== Stack traces printout + +The `shell:stack-traces-print` command prints the full stack trace when the execution of a command +throws an exception. + +You can enable or disable this behaviour by passing true (to enable) or false (to disable) on the command on the fly: + +---- +karaf@root()> stack-traces-print +Printing of stacktraces set to true +karaf@root()> bundle:start +java.lang.RuntimeException: Access to system bundle 0 denied. You can override with -f + at org.apache.karaf.bundle.command.BundlesCommand.assertNoSystemBundles(BundlesCommand.java:57) + at org.apache.karaf.bundle.command.BundlesCommand.doExecute(BundlesCommand.java:48) + at org.apache.karaf.bundle.command.BundlesCommandWithConfirmation.doExecute(BundlesCommandWithConfirmation.java:41) + at org.apache.karaf.shell.console.AbstractAction.execute(AbstractAction.java:33) + at org.apache.karaf.shell.console.OsgiCommandSupport.execute(OsgiCommandSupport.java:39) + at org.apache.karaf.shell.commands.basic.AbstractCommand.execute(AbstractCommand.java:33) + at sun.reflect.GeneratedMethodAccessor30.invoke(Unknown Source) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.lang.reflect.Method.invoke(Method.java:601) + at org.apache.aries.proxy.impl.ProxyHandler$1.invoke(ProxyHandler.java:54) + at org.apache.aries.proxy.impl.ProxyHandler.invoke(ProxyHandler.java:119) + at org.apache.karaf.shell.console.commands.$BlueprintCommand14083304.execute(Unknown Source) + at sun.reflect.GeneratedMethodAccessor30.invoke(Unknown Source) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) + at java.lang.reflect.Method.invoke(Method.java:601) + at org.apache.aries.proxy.impl.ProxyHandler$1.invoke(ProxyHandler.java:54) + at org.apache.aries.proxy.impl.ProxyHandler.invoke(ProxyHandler.java:119) + at org.apache.karaf.shell.console.commands.$BlueprintCommand14083304.execute(Unknown Source) + at org.apache.felix.gogo.runtime.CommandProxy.execute(CommandProxy.java:78) + at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:477) + at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:403) + at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108) + at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:183) + at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:120) + at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:89) + at org.apache.karaf.shell.console.impl.jline.ConsoleImpl$DelegateSession.execute(ConsoleImpl.java:497) + at org.apache.karaf.shell.console.impl.jline.ConsoleImpl.run(ConsoleImpl.java:198) + at java.lang.Thread.run(Thread.java:722) + at org.apache.karaf.shell.console.impl.jline.ConsoleFactoryService$3.doRun(ConsoleFactoryService.java:118) + at org.apache.karaf.shell.console.impl.jline.ConsoleFactoryService$3$1.run(ConsoleFactoryService.java:109) + at java.security.AccessController.doPrivileged(Native Method) + at org.apache.karaf.jaas.modules.JaasHelper.doAs(JaasHelper.java:47) + at org.apache.karaf.shell.console.impl.jline.ConsoleFactoryService$3.run(ConsoleFactoryService.java:107) +karaf@root()> stack-traces-print false +Printing of stacktraces set to false +karaf@root()> bundle:start +Error executing command: Access to system bundle 0 denied. You can override with -f +---- + +=== Bundle tree + +The `bundle:tree-show` command shows the bundle dependency tree based on the wiring information of a given single bundle +ID. + +---- +karaf@root()> bundle:tree-show 40 +Bundle org.ops4j.pax.url.wrap [40] is currently ACTIVE + +org.ops4j.pax.url.wrap [40] ++- org.ops4j.base.util.property [14] ++- org.ops4j.pax.url.commons [49] +| +- org.ops4j.base.util.property [14] +| +- org.ops4j.pax.logging.pax-logging-api [23] +| +- org.ops4j.pax.swissbox.property [31] +| | +- org.ops4j.base.util.property [14] +| | +- org.ops4j.base.lang [41] +| +- org.apache.felix.configadmin [43] +| | +- org.ops4j.pax.logging.pax-logging-api [23] +| +- org.ops4j.base.lang [41] ++- org.ops4j.pax.logging.pax-logging-api [23] ++- org.ops4j.pax.swissbox.bnd [25] +| +- biz.aQute.bndlib [30] +| | +- org.apache.servicemix.bundles.junit [36] +| +- org.ops4j.pax.logging.pax-logging-api [23] +| +- org.ops4j.base.lang [41] ++- org.apache.felix.configadmin [43] ++- org.ops4j.base.net [29] +| +- org.ops4j.base.monitors [37] +| +- org.ops4j.base.lang [41] ++- org.ops4j.base.lang [41] +---- + +=== Watch + +The `bundle:watch` command enables watching the local Maven repository for updates on bundles. +If the bundle file changes on the Maven repository, Apache Karaf will automatically update the bundle. + +The `bundle:watch` allows you to configure a set of URLs to monitore. All bundles bundles whose location matches the +given URL will be automatically updated. It avoids needing to manually update the bundles or even copy the bundle to the +system folder. + +{warning} +Only Maven based URLs and Maven SNAPSHOTs will actually be updated automatically. +{warning} + +The following command: + +---- +karaf@root()> bundle:watch * +---- + +will monitor all bundles that have a location matching mvn:* and '-SNAPSHOT' in their URL. http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/ds.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/ds.adoc b/manual/src/main/asciidoc/developer-guide/ds.adoc new file mode 100644 index 0000000..bb34893 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/ds.adoc @@ -0,0 +1,15 @@ +// +// Licensed 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. +// + +== Declarative Service (DS) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/extending.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/extending.adoc b/manual/src/main/asciidoc/developer-guide/extending.adoc new file mode 100644 index 0000000..62c56a4 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/extending.adoc @@ -0,0 +1,359 @@ +// +// Licensed 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. +// + +== Extending + +Apache Karaf is a very flexible container that you can extend very easily. + +=== Console + +In this section, you will see how to extend the console by adding your own command. + +We will leverage Apache Maven to create and build the OSGi bundle. +This OSGi bundle will use Blueprint. We don't cover the details of OSGi bundle and Blueprint, see the specific +sections for details. + +==== Create the Maven project + +To create the Maven project, we can: + +* use a Maven archetype +* create by hand + +===== Using archetype + +The Maven Quickstart archetype can create an empty Maven project where you can put your project definition. + +You can directly use: + +---- +mvn archetype:create \ + -DarchetypeArtifactId=maven-archetype-quickstart \ + -DgroupId=org.apache.karaf.shell.samples \ + -DartifactId=shell-sample-commands \ + -Dversion=1.0-SNAPSHOT +---- + +It results to a ready to use project, including a `pom.xml`. + +You can also use Maven archetype in interactive mode. You will have to answer to some questions used to generate +the project with the `pom.xml`: + +---- +mvn archetype:generate +Choose a number: (1/2/3/4/5/6/7/.../32/33/34/35/36) 15: : 15 +Define value for groupId: : org.apache.karaf.shell.samples +Define value for artifactId: : shell-sample-commands +Define value for version: 1.0-SNAPSHOT: : +Define value for package: : org.apache.karaf.shell.samples +---- + +==== By hand + +Alternatively, you can simply create the directory `shell-sample-commands` and create the `pom.xml` file inside it: + +---- +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <groupId>org.apache.karaf.shell.samples</groupId> + <artifactId>shell-sample-commands<artifactId> + <packaging>bundle</packaging> + <version>1.0-SNAPSHOT</version> + <name>shell-sample-commmands</name> + + + <dependencies> + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>${felix.plugin.version}</version> + <configuration> + <instructions> + <Import-Package> + org.apache.felix.service.command, + org.apache.karaf.shell.commands, + org.apache.karaf.shell.console, + * + </Import-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + +</project> +---- + +==== Configuring for Java 6/7 + +We are using annotations to define commands, so we need to ensure Maven will actually use JDK 1.6 or 1.7 to compile the jar. +Just add the following snippet after the `dependencies` section. + +---- +<build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <target>1.6</target> + <source>1.6</source> + </configuration> + </plugin> + </plugins> +</build> +---- + +==== Loading the project in your IDE + +We can use Maven to generate the needed files for your IDE: + +Inside the project, run the following command + +---- +mvn eclipse:eclipse +---- + +or + +---- +mvn idea:idea +---- + +The project files for your IDE should now be created. Just open the IDE and load the project. + +==== Creating a basic command class + +We can now create the command class `HelloShellCommand.java` + +---- +package org.apache.karaf.shell.samples; + +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Command(scope = "test", name = "hello", description="Says hello") +@Service +public class HelloShellCommand implements Action { + + @Override + public Object execute() throws Exception { + System.out.println("Executing Hello command"); + return null; + } +} +---- + +==== Manifest + +In order for Karaf to find your command, you need to add the `Karaf-Commands=*` manifest header. + +This is usually done by modifying the maven bundle plugin configuration + +---- +<plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Karaf-Commands>*</Karaf-Commands> + </instructions> + </configuration> +</plugin> +---- + +==== Compile + +Let's try to build the jar. Remove the test classes and sample classes if you used the artifact, then from the command line, run: + +---- +mvn install +---- + +The end of the maven output should look like: + +---- +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESSFUL +[INFO] ------------------------------------------------------------------------ +---- + +==== Test + +Launch Apache Karaf and install your bundle: + +---- +karaf@root()> bundle:install -s mvn:org.apache.karaf.shell.samples/shell-sample-commands/1.0-SNAPSHOT +---- + +Let's try running the command: + +---- +karaf@root()> test:hello +Executing Hello command +---- + +==== Command completer + +A completer allows you to automatically complete a command argument using <tab>. A completer is simply a bean which is +injected to a command. + +Of course to be able to complete it, the command should require an argument. + +==== Command argument + +We add an argument to the HelloCommand: + +---- +package org.apache.karaf.shell.samples; + +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Completion; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Command(scope = "test", name = "hello", description="Says hello") +@Service +public class HelloShellCommand implements Action { + + @Argument(index = 0, name = "name", description = "The name that sends the greet.", required = true, multiValued = false) + @Completion(SimpleNameCompleter.class) + String name = null; + + @Override + public Object execute() throws Exception { + System.out.println("Hello " + name); + return null; + } +} +---- + +==== Completer bean + +A completer is a bean which implements the Completer interface: + +---- +package org.apache.karaf.shell.samples; + +import org.apache.karaf.shell.api.action.lifecycle.Service; +import org.apache.karaf.shell.api.console.CommandLine; +import org.apache.karaf.shell.api.console.Completer; +import org.apache.karaf.shell.api.console.Session; +import org.apache.karaf.shell.support.completers.StringsCompleter; + +/** + * <p> + * A very simple completer. + * </p> + */ +@Service +public class SimpleNameCompleter implements Completer { + + public int complete(Session session, CommandLine commandLine, List<String> candidates) { + StringsCompleter delegate = new StringsCompleter(); + delegate.getStrings().add("Mike"); + delegate.getStrings().add("Eric"); + delegate.getStrings().add("Jenny"); + return delegate.complete(buffer, cursor, candidates); + } + +} +---- + +==== Completers for option values + +Quite often your commands will not have just arguments, but also options. You can provide completers for option values. +The snippet below shows the HelloShellCommand with an option to specify what the greet message will be. + +---- +package org.apache.karaf.shell.samples; + +import org.apache.karaf.shell.api.action.Action; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Completion; +import org.apache.karaf.shell.api.action.Option; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Command(scope = "test", name = "hello", description="Says hello") +@Service +public class HelloShellCommand implements Action { + + @Argument(index = 0, name = "name", description = "The name that sends the greet.", required = true, multiValued = false) + @Completion(SimpleNameCompleter.class) + String name = null; + + @Option(name = "-g", aliases = "--greet", description = "The configuration pid", required = false, multiValued = false) + @Completion(GreetCompleter.class) + String greet = "Hello; + + @Override + public Object execute() throws Exception { + System.out.println(greet + " " + name); + return null; + } +} +---- + +==== Completers with state + +Some times we want to tune the behavior of the completer depending on the commands already executed, in the current shell +or even the rest of the arguments that have been already passed to the command. Such example is the config:set-property +command which will provide auto completion for only for the properties of the pid specified by a previously issued config:edit +command or by the option --pid. + +The Session object provides map like methods for storing key/value pairs and can be used to put/get the state. +The pre-parsed CommandLine objects allows you to check the previous arguments and options on the command line and to fine tune +the behavior of the Completer. +Those two objects are given to the Completer when calling the `complete` method. + +==== Test + +Launch a Karaf instance and run the following command to install the newly created bundle: + +---- +karaf@root()> bundle:install -s mvn:org.apache.karaf.shell.samples/shell-sample-commands/1.0-SNAPSHOT +---- + +Let's try running the command: + +---- +karaf@root> test:hello <tab> + one two three +---- + +=== WebConsole + +You can also extend the Apache Karaf WebConsole by providing and installing a webconsole plugin. + +A plugin is an OSGi bundle that register a Servlet as an OSGi service with some webconsole properties. http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/github-contributions.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/github-contributions.adoc b/manual/src/main/asciidoc/developer-guide/github-contributions.adoc new file mode 100644 index 0000000..de3c098 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/github-contributions.adoc @@ -0,0 +1,81 @@ +// +// Licensed 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. +// + +== Github Contributions + +Some people prefer to make contributions to karaf source via github. If you are one of them, this is for you! + +=== Introduction + +Apache Karaf is available as a periodically replicated mirror on: https://github.com/apache/karaf + +=== Suggested Workflow + +. Make a fork of karaf repo github mirror +. Do all your new work on your own karaf fork +. When ready, file a Jira issue https://issues.apache.org/jira/browse/KARAF, attach the link to your github pull request, and ask for a review +. One of karaf committers will discuss your pull request on github; and at some point your pull request will be accepted +. When your pull request is accepted, squash it into a single commit and attach single patch file to the original jira, with ASF grant check box selected +. Now pray to your favorite ASF committer to really accept the patch :-) +. When your patch is committed to the svn, and you can verify it in the latest karaf snapshot, close your pull request on github + +=== License Reminder + +In order for your contributions to be accepted: + +* All files must contain ASL license grant header +* You must select ASF grant check box when attaching patch to the jira + +=== How to Generate a One-File-Patch Via Throw-Away Branch + +Here is one way to generate squash of your commits: + +http://stackoverflow.com/questions/616556/how-do-you-squash-commits-into-one-patch-with-git-format-patch + +---- +# +# 'archon' referers to karaf mirror +# 'origin' referers to your own fork +# + +# attach karaf mirror as remote, if not done yet +git remote add archon https://github.com/apache/karaf + +# fetch latest karaf mirror +git fetch archon + +# ensure you are on your fork trunk +git checkout origin/trunk + +# kill previous patch delivery, if you had one +git branch -D delivery + +# make new delivery throw-away branch, based on latest karaf mirror +git branch delivery archon/trunk + +# use it +git checkout delivery + +# squash all your local development into a single commit +git merge --squash trunk + +# commit it to the delivery branch +git commit -m "delivery" + +# generate a patch file against the mirror +git format-patch archon/trunk + +---- + +root of your Karaf source now contains a file named "0001-delivery.patch.txt" (please attach the .txt ending;this will allow commiters to open your patch directly in the browser and give it a short look there) which you should attach to your karaf jira, and ask to commit to the svn trunk http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/karaf-maven-plugin.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/karaf-maven-plugin.adoc b/manual/src/main/asciidoc/developer-guide/karaf-maven-plugin.adoc new file mode 100644 index 0000000..e4ed117 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/karaf-maven-plugin.adoc @@ -0,0 +1,486 @@ +// +// Licensed 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. +// + +== Using the karaf-maven-plugin + +The Karaf Maven plugin allows you: + +* to work with Karaf features: validate a features descriptor, add features bundle into a repository, create a KAR archive from a features descriptor, etc. +* to create Karaf commands help: it generates help from Karaf commands +* to modify Karaf instances and create distributions + +=== Packaging + +The most generally useful features of the karaf-maven-plugin are exposed as packagings. To use the packagings the pom or an ancestor must configure the karaf-maven-plugin with extensions: + +---- + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>${project.version}</version> + <extensions>true</extensions> + </plugin> + </plugins> + </pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + </plugin> + </plugins> + </build> +---- + +Then specify the packaging in your project as usual, e.g. + +---- + <packaging>kar</packaging> +---- + +|=== +|Packaging |Description + +|feature +|The feature packaging generates a features.xml descriptor using the `karaf:features-generate-descriptor` + +|kar +|The kar packaging generates a features.xml descriptor using the `karaf:features-generate-descriptor` and then packages a kar using the `karaf:features-create-kar` + +|karaf-assembly +|Assembles a Karaf server based on the features descriptors and kar files listed as Maven dependencies. +|=== + +=== Commands goals + +The `karaf-maven-plugin` is able to generate documentation for Karaf commands + +==== `karaf:commands-generate-help` + +The `karaf:commands-generate-help` goal generates documentation containing Karaf commands help. + +It looks for Karaf commands in the current project class loader and generates the help as displayed with the `--help` +option in the Karaf shell console. + +===== Example + +The example below generates help for the commands in the current project: + +---- +<project> + <build> + <plugins> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>${project.version}</version> + + <executions> + <execution> + <id>document-commands</id> + <phase>generate-resources</phase> + <goals> + <goal>commands-generate-help</goal> + </goals> + <configuration> + <targetFolder>${project.build.directory}/docbook/sources</targetFolder> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> +---- + +===== Parameters + +|=== +|Name |Type |Description + +|`targetFolder` +|`File` +|The directory where the documentation output files are to be generated. Default value: ${project.build.directory}/docbkx/sources + +|`format` +|`String` +|The output format (docbx, asciidoc, or conf) of the commands documentation. Default value: docbx + +|`classLoader` +|`String` +|The class loader to use in loading the commands. Default value: ${project} +|=== + +=== Features and kar goals + +[NOTE] +==== +You should use the features or kar packaging instead of these individual goals. +==== + +The `karaf-maven-plugin` provides several goals to help you create and validate features XML descriptors as well as leverage your features to create a custom Karaf distribution. + +==== `karaf:features-generate-descriptor` + +Except in unusual circumstances, use the `<packaging>feature</packaging>` to run this goal. + +The `karaf:features-generate-descriptor` goal generates a features XML file based on the Maven dependencies. +By default, it will follow Maven transitive dependencies, stopping when it encounters bundles already present in features that are Maven dependencies. + +A record of the dependency tree search can be found in target/history/treeListing.txt. + +You can track dependency changes and warn or fail on change. + +===== Configuration + +Specify the packaging as a top level element + +---- +<packaging>feature</packaging> +---- + +You can supply a feature descriptor to extend in `src/main/feature/feature.xml`. + +|=== +|Parameter Name |Type |Description + +|aggregateFeatures +|boolean (false) +|Specifies processing of feature repositories that are (transitive) Maven dependencies. +If false, all features in these repositories become dependencies of the generated feature. +If true, all features in these repositories are copied into the generated feature repository. + +|startLevel +|int +|The start level for the bundles determined from Maven dependencies. +This can be overridden by specifying the bundle in the source feature.xml with the desired startlevel. + +|includeTransitiveDependency +|boolean (true) +|Whether to follow Maven transitive dependencies. + +|checkDependencyChange +|boolean (false) +|Whether to record dependencies in `src/main/history/dependencies.xml` for change tracking. + +|warnOnDependencyChange +|boolean (false) +|whether to fail on changed dependencies (false, default) or warn in the build output (true). + +|logDependencyChanges +|boolean (false) +|If true, added and removed dependencies are shown in `target/history`. + +|overwriteChangedDependencies +|boolean (false) +|If true, the `src/main/history/dependencies.xml` file will be overwritten if it has changed. +|=== + +===== Example + +---- +<project> +... + <packaging>feature</packaging> + <dependencies> + <dependency> + <groupId>org.apache</groupId> + <artifactId>bundle1</artifactId> + <version>1.0</version> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>${project.version}</version> + <extensions>true</extensions> + <configuration> + <startLevel>80</startLevel> + <aggregateFeatures>true</aggregateFeatures> + </configuration> + </plugin> + </plugins> + </build> +</project> +---- + +==== `karaf:features-validate-descriptor` + +The `karaf:features-validate-descriptor` goal validates a features XML descriptor by checking if all the required imports +for the bundles defined in the features can be matched to a provided export. + +By default, the plugin tries to add the Karaf core features (standard and enterprise) in the repositories set. +It means that it's not required to explicitly define the Karaf features descriptor in the repository section of +your features descriptor. + +===== Example + +The example below validates the features defined in the `target/features.xml` by checking all the imports and exports. +It reads the definition for the packages that are exported by the system bundle from the `src/main/resources/config.properties` file. + +---- +<project> + <build> + <plugins> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <id>validate</id> + <phase>process-resources</phase> + <goals> + <goal>features-validate-descriptor</goal> + </goals> + <configuration> + <file>target/features.xml</file> + <karafConfig>src/main/resources/config.properties</karafConfig> + </configuration> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>1.4.3</version> + </dependency> + </dependencies> + </plugin> + </plugins> + </build> +</project> +---- + +===== Parameters + +|=== +|Name |Type |Description + +|`file` +|`File` +|The features XML descriptor file to validate. Default value: `${project.build.directory}/classes/features.xml` + +|`karafConfig` +|`String` +|The Karaf `config.properties` file to use during the validation process. Default value: `config.properties` + +|`jreVersion` +|`String` +|The JRE version that is used during the validation process. Default value: `jre-1.5` + +|`karafVersion` +|`String` +|The target Karaf version used to get the Karaf core features (standard and enterprise). Default is the version of the plugin + +|`repositories` +|`String[]` +|Additional features XML descriptors that will be used during the validation process +|=== + +==== `karaf:features-add-to-repository` + +Consider using the karaf-assembly packaging which makes it easy to assemble a custom distribution in one step instead +of this individual goal. + +The `karaf:features-add-to-repository` goal adds all the required bundles for a given set of features into directory. +You can use this goal to create a `/system` directory for building your own Karaf-based distribution. + +By default, the Karaf core features descriptors (standard and enterprise) are automatically included in the descriptors set. + +===== Example + +The example below copies the bundles for the `spring` and `war` features defined in the Karaf features XML descriptor +into the `target/features-repo` directory. + +---- +<project> + <build> + <plugins> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>${project.version}</version> + + <executions> + <execution> + <id>features-add-to-repo</id> + <phase>generate-resources</phase> + <goals> + <goal>features-add-to-repository</goal> + </goals> + <configuration> + <descriptors> + <descriptor>mvn:org.apache.karaf.features/standard/4.0.0/xml/features</descriptor> + <descriptor>mvn:my.groupid/my.artifactid/1.0.0/xml/features</descriptor> + </descriptors> + <features> + <feature>spring</feature> + <feature>war</feature> + <feature>my</feature> + </features> + <repository>target/features-repo</repository> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> +---- + +===== Parameters + +|=== +|Name |Type |Description + +|`descriptors` +|`String[]` +|List of features XML descriptors where the features are defined + +|`features` +|`String[]` +|List of features that bundles should be copied to the repository directory + +|`repository` +|`File` +|The directory where the bundles will be copied by the plugin goal +|=== + +==== `karaf:create-kar` + +[NOTE] +==== +Except in unusual circumstances, use the `<packaging>kar</packaging>` to run this goal. +==== + +The `karaf:kar` goal assembles a KAR archive from a features XML descriptor file, normally generated in the same project +with the `karaf:features-generate-descriptor` goal. + +There are two important directories in a kar: + +* `repository/` contains a Maven structured repository of artifacts to be copied into the Karaf repository. +The features descriptor and all the bundles mentioned in it are installed in this directory. +* `resources/` contains other resources to be copied over the Karaf installation. + +Everything in `target/classes` is copied into the kar. +Therefore resources you want installed into Karaf need to be in e.g. `src/main/resources/resources`. +This choice is so other resources such as legal files from the maven-remote-resources-plugin can be included under +META-INF in the kar, without getting installed into Karaf. + +===== Example + +---- +<project> +... + <packaging>kar</packaging> + <build> + <plugins> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>${project.version}</version> + <extensions>true</extensions> + <!-- There is no useful configuration for the kar mojo. The features-generate-descriptor mojo configuration may be useful --> + </plugin> + </plugins> + </build> +</project> +---- + +==== `karaf:install-kar` + +=== Instances and distributions goals + +[NOTE] +==== +You should use the karaf-assembly packaging instead of this individual goal. +==== + +The `karaf-maven-plugin` helps you to build custom Karaf distributions or archives existing Karaf instances: + +==== `karaf:archive` + +[NOTE] +==== +This goal is run as part of the karaf-assembly packaging. +==== + +The `karaf:archive` goal packages a Karaf instance archive from a given assembled instance. + +Both tar.gz and zip formats are generated in the destination folder. + +===== Example + +The example below create archives for the given Karaf instance: + +---- +<project> + <build> + <plugins> + <plugin> + <groupId>org.apache.karaf.tooling</groupId> + <artifactId>karaf-maven-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <id>generate</id> + <phase>package</phase> + <goals> + <goal>archive</goal> + </goals> + <configuration> + <destDir>${project.build.directory}</destDir> + <targetServerDirectory>${project.build.directory}/assembly</targetServerDirectory> + <targetFile>${project.file}</targetFile> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> +---- + +===== Parameters + +|=== +|Name |Type |Description + +|`destDir` +|`File` +| The target directory of the project. Default value: ${project.build.directory} + +|`targetServerDirectory` +|`File` +|The location of the server repository. Default value: ${project.build.directory}/assembly + +|`targetFile` +|`File` +|The target file to set as the project's artifact. Default value: ${project.file} +|=== + +==== `karaf:assembly` + +=== Run, client, deploy goals + +==== `karaf:run` + +==== `karaf:client` + +==== `karaf:deploy` \ No newline at end of file http://git-wip-us.apache.org/repos/asf/karaf/blob/3de05ae9/manual/src/main/asciidoc/developer-guide/scripting.adoc ---------------------------------------------------------------------- diff --git a/manual/src/main/asciidoc/developer-guide/scripting.adoc b/manual/src/main/asciidoc/developer-guide/scripting.adoc new file mode 100644 index 0000000..64dae18 --- /dev/null +++ b/manual/src/main/asciidoc/developer-guide/scripting.adoc @@ -0,0 +1,406 @@ +// +// Licensed 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. +// + +== Scripting + +In the console section of the users guide, we introduced the scripting support. + +=== Assignation + +You already know the first usage of scripting: execution of command. + +---- +karaf@root()> echo hello world +hello world +---- + +You can also assign value to session variables: + +---- +karaf@root()> msg = "hello world" +hello world +---- + +Once you have assigned a value to a variable, you can display this value using the "resolved" variable name: + +---- +karaf@root()> echo $msg +hello world +---- + +The () are execution quotes (like the backquotes when you use bash on Unix). + +---- +karaf@root()> ($.context bundle 1) location +mvn:org.apache.karaf.jaas/org.apache.karaf.jaas.modules/4.0.0 +---- + +The `$.context` access the context variables in the current session. +We access to the `bundle` variable (an array containing all bundles), and we want to display the bundle location for +the bundle at the index 1 in the bundle array. + +=== Expressions + +The shell has a built-in expression parser. Expressions must be enclosed with the `%(...)` syntax. + +Examples: + +---- +karaf@root()> %(1+2) +3 +karaf@root()> a = 0 +0 +karaf@root()> %(a+=1) +1 +karaf@root()> %(a+=1) +2 +karaf@root()> b=1 +1 +karaf@root()> %(SQRT(a^2 + b^2)) +1.7320508 +---- + +==== Mathematical Operators + +|=== +|Operator |Description + +|+ +| Additive operator + +|- +| Subtraction operator + +|* +| Multiplication operator + +|/ +| Division operator + +|% +| Remainder operator (Modulo) + +|^ +| Power operator +|=== + +==== Boolean Operators + +|=== +|Operator |Description + +|= +| Equals + +|== +| Equals + +|!= +|Not equals + +|<> +| Not equals + +|< +| Less than + +|<= +| Less than or equal to + +|> +| Greater than + +|>= +| Greater than or equal to + +|&& +| Boolean and + +|\|\| +|Boolean or +|=== + +==== Supported Functions + +|=== +|Function |Description + +|NOT(_expression_) +|Boolean negation, 1 (means true) if the expression is not zero + +|IF(_condition_,_value_if_true_,_value_if_false_) +|Returns one value if the condition evaluates to true or the other if it evaluates to false + +|RANDOM() +|Produces a random number between 0 and 1 + +|MIN(_e1_,_e2_) +|Returns the smaller of both expressions + +|MAX(_e1_,_e2_) +|Returns the bigger of both expressions + +|ABS(_expression_) +|Returns the absolute (non-negative) value of the expression + +|ROUND(_expression_,precision) +|Rounds a value to a certain number of digits, uses the current rounding mode + +|FLOOR(_expression_) +|Rounds the value down to the nearest integer + +|CEILING(_expression_) +|Rounds the value up to the nearest integer + +|LOG(_expression_) +|Returns the natural logarithm (base e) of an expression + +|SQRT(_expression_) +|Returns the square root of an expression + +|SIN(_expression_) +|Returns the trigonometric sine of an angle (in degrees) + +|COS(_expression_) +|Returns the trigonometric cosine of an angle (in degrees) + +|TAN(_expression_) +|Returns the trigonometric tangens of an angle (in degrees) + +|SINH(_expression_) +|Returns the hyperbolic sine of a value + +|COSH(_expression_) +|Returns the hyperbolic cosine of a value + +|TANH(_expression_) +|Returns the hyperbolic tangens of a value + +|RAD(_expression_) +|Converts an angle measured in degrees to an approximately equivalent angle measured in radians + +|DEG(_expression_) +|Converts an angle measured in radians to an approximately equivalent angle measured in degrees +|=== + +Functions names are case insensitive. + +==== Supported Constants + +|=== +|Constant |Description + +|PI +|The value of _PI_, exact to 100 digits + +|TRUE +|The value one + +|FALSE +|The value zero +|=== + +=== List, maps, pipes and closures + +Using [], you can define array variable: + +---- +karaf@root()> list = [1 2 a b] +1 +2 +a +b + +---- + +You can also create a map if you put variables assignation in the array: + +---- +karaf@root()> map = [Jan=1 Feb=2 Mar=3] +Jan 1 +Feb 2 +Mar 3 +---- + +Using the | character, you can pipe output from a command as an input to another one. + +For instance, you can access to the bundles context variables and send it as input to the grep command: + +---- +karaf@root()> ($.context bundles) | grep -i felix + 0|Active | 0|org.apache.felix.framework (4.2.1) + 21|Active | 11|org.apache.felix.fileinstall (3.2.6) + 43|Active | 10|org.apache.felix.configadmin (1.6.0) + 51|Active | 30|org.apache.felix.gogo.runtime (0.10.0) +---- + +You can assign name to script execution. It's what we use for alias: + +---- +karaf@root()> echo2 = { echo xxx $args yyy } +echo xxx $args yyy +karaf@root()> echo2 hello world +xxx hello world yyy +---- + +=== Startup + +The `etc/shell.init.script` file is executed at startup in each shell session, allowing the definition of additional +variables or aliases or even complex functions. It's like the bashrc or profile on Unix. + +=== Constants and variables + +Apache Karaf console provides a set of implicit constants and variables that you can use in your script. + +* `$.context` to access a bundle context +* `$.variables` to access the list of defined variables +* `$.commands` to access the list of defined commands + +The variables starting with a # that are defined as Function (such as closures) will be executed automatically: + +---- +karaf@root> \#inc = { var = "${var}i" ; $var } +var = "${var}i" ; $var +karaf@root> echo $inc +i +karaf@root> echo $inc +ii +karaf@root> +---- + +=== Built-in variables and commands + +Apache Karaf console provides built-in variable very useful for scripting: + +* `$args` retrieves the list of script parameters, given to the closure being executed +* `$1 .. $999` retrieves the nth argument of the closure +* `$it` (same as `$1`) is used in a loop to access the current iterator value + +Apache Karaf console provides commands for scripting: + +* `shell:if` +* `shell:new` +* `shell:each` +* ... + +See the link:commands[full list of `shell` commands] for details. + +=== Leveraging existing Java capabilities (via reflection) + +Apache Karaf console supports loading and execution of Java classes. + +The `$karaf.lastException` implicit variable contains the latest Exception thrown. + +---- +karaf@root()> ($.context bundle) loadClass foo +Error executing command: foo not found by org.apache.karaf.shell.console [17] +karaf@root()> $karaf.lastException printStackTrace +java.lang.ClassNotFoundException: foo not found by org.apache.karaf.shell.console [17] + at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1460) + at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:72) + at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1843) + at java.lang.ClassLoader.loadClass(ClassLoader.java:247) + at org.apache.felix.framework.Felix.loadBundleClass(Felix.java:1723) + at org.apache.felix.framework.BundleImpl.loadClass(BundleImpl.java:926) + at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) + at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) + at java.lang.reflect.Method.invoke(Method.java:597) + at org.apache.felix.gogo.runtime.Reflective.invoke(Reflective.java:137) + at org.apache.felix.gogo.runtime.Closure.executeMethod(Closure.java:527) + at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:403) + at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108) + at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:183) + at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:120) + at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:89) + at org.apache.karaf.shell.console.jline.Console.run(Console.java:166) + at java.lang.Thread.run(Thread.java:680) +---- + +It's possible to create objects to create commands "on the fly": + +---- +karaf@root()> addcommand system (($.context bundle) loadClass java.lang.System) +karaf@root()> system:getproperty karaf.name +root +---- + +It means that you can create object using the `new` directive, and call methods on the objects: + +---- +karaf@root> map = (new java.util.HashMap) +karaf@root> $map put 0 0 +karaf@root> $map +0 0 +---- + +=== Examples + +The following examples show some scripts defined in `etc/shell.init.script`. + +The first example show a script to add a value into a configuration list: + +---- +# +# Add a value at the end of a property in the given OSGi configuration +# +# For example: +# > config-add-to-list org.ops4j.pax.url.mvn org.ops4j.pax.url.mvn.repositories http://scala-tools.org/repo-releases +# +config-add-to-list = { + config:edit $1 ; + a = (config:property-list | grep --color never $2 | tac) ; + b = (echo $a | grep --color never "\b$3\b" | tac) ; + if { ($b trim) isEmpty } { + if { $a isEmpty } { + config:property-set $2 $3 + } { + config:property-append $2 ", $3" + } ; + config:update + } { + config:cancel + } +} +---- + +This second example shows a script to wait for an OSGi service, up to a given timeout, and combine this script in +other scripts: + +---- +# +# Wait for the given OSGi service to be available +# +wait-for-service-timeout = { + _filter = $.context createFilter $1 ; + _tracker = shell:new org.osgi.util.tracker.ServiceTracker $.context $_filter null ; + $_tracker open ; + _service = $_tracker waitForService $2 ; + $_tracker close +} +# +# Wait for the given OSGi service to be available with a timeout of 10 seconds +# +wait-for-service = { + wait-for-service-timeout $1 10000 +} +# +# Wait for the given command to be available with a timeout of 10 seconds +# For example: +# > wait-for-command dev watch +# +wait-for-command = { + wait-for-service "(&(objectClass=org.apache.felix.service.command.Function)(osgi.command.scope=$1)(osgi.command.function=$2))" +} +----
