From 3fff3ad240e57b6fc8096581b8c7f0e84a9163c3 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Thu, 4 Mar 2010 22:53:05 +0100 Subject: [PATCH 01/33] Added OSGi proof of concept Very basic example Starting point to kick of discussions --- akka-core/pom.xml | 16 ++ akka-osgi/akka-dependencies-bundle/pom.xml | 56 +++++ akka-osgi/akka-sample-osgi/pom.xml | 49 +++++ .../src/main/scala/Activator.scala | 36 +++ .../src/main/scala/Actors.scala | 18 ++ akka-osgi/deployer/pom.xml | 54 +++++ .../akka/osgi/deployer/Activator.java | 43 ++++ .../akka/osgi/deployer/DirWatcher.java | 207 ++++++++++++++++++ akka-osgi/karaf/pom.xml | 127 +++++++++++ akka-osgi/karaf/src/main/assembly/runtime.xml | 37 ++++ akka-osgi/pom.xml | 45 ++++ 11 files changed, 688 insertions(+) create mode 100644 akka-osgi/akka-dependencies-bundle/pom.xml create mode 100644 akka-osgi/akka-sample-osgi/pom.xml create mode 100644 akka-osgi/akka-sample-osgi/src/main/scala/Activator.scala create mode 100644 akka-osgi/akka-sample-osgi/src/main/scala/Actors.scala create mode 100644 akka-osgi/deployer/pom.xml create mode 100644 akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/Activator.java create mode 100644 akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/DirWatcher.java create mode 100644 akka-osgi/karaf/pom.xml create mode 100644 akka-osgi/karaf/src/main/assembly/runtime.xml create mode 100644 akka-osgi/pom.xml diff --git a/akka-core/pom.xml b/akka-core/pom.xml index d6ca57ebfe..b9753200c8 100644 --- a/akka-core/pom.xml +++ b/akka-core/pom.xml @@ -108,4 +108,20 @@ test + + + + + org.apache.felix + maven-bundle-plugin + + + + se.scalablesolutions.akka.*;version=${project.version};-split-package:=merge-first + + + + + + diff --git a/akka-osgi/akka-dependencies-bundle/pom.xml b/akka-osgi/akka-dependencies-bundle/pom.xml new file mode 100644 index 0000000000..74d1792be4 --- /dev/null +++ b/akka-osgi/akka-dependencies-bundle/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + + se.scalablesolutions.akka + akka-osgi-parent + 0.7-SNAPSHOT + + + akka-dependencies-bundle + Akka Dependencies Bundle + + + + akka-kernel + se.scalablesolutions.akka + ${project.version} + + + + + + + org.apache.felix + maven-bundle-plugin + + + + *;resolution:=optional + + + + !test.*, + + + !scala.*, + !org.apache.commons.io.*, + !org.codehaus.jackson.*, + !org.codehaus.jettison.*, + !org.jboss.netty.*, + + + !se.scalablesolutions.akka.*, + + + *;-split-package:=merge-first + + + + + + + + diff --git a/akka-osgi/akka-sample-osgi/pom.xml b/akka-osgi/akka-sample-osgi/pom.xml new file mode 100644 index 0000000000..bd48ca2392 --- /dev/null +++ b/akka-osgi/akka-sample-osgi/pom.xml @@ -0,0 +1,49 @@ + + 4.0.0 + + akka-sample-osgi + Akka OSGi Sample Module + + jar + + + akka-osgi-parent + se.scalablesolutions.akka + 0.7-SNAPSHOT + + + + + akka-core + se.scalablesolutions.akka + ${project.version} + + + org.osgi + org.osgi.core + + + org.osgi + org.osgi.compendium + + + + + + + org.apache.felix + maven-bundle-plugin + + + se.scalablesolutions.akka.osgi.sample.Activator + + se.scalablesolutions.akka.osgi.sample + + + + + + + + diff --git a/akka-osgi/akka-sample-osgi/src/main/scala/Activator.scala b/akka-osgi/akka-sample-osgi/src/main/scala/Activator.scala new file mode 100644 index 0000000000..7925a2705f --- /dev/null +++ b/akka-osgi/akka-sample-osgi/src/main/scala/Activator.scala @@ -0,0 +1,36 @@ + +package se.scalablesolutions.akka.osgi.sample + +import org.osgi.framework.{BundleContext, BundleActivator} +import se.scalablesolutions.akka.config.ScalaConfig._ +import se.scalablesolutions.akka.actor.{Supervisor, SupervisorFactory} + +class Activator extends BundleActivator { + + var supervisor: Supervisor = _ + + val ping = new Ping + + def start(context: BundleContext) { + println("Starting Akka OSGi sample") + + supervisor = SupervisorFactory( + SupervisorConfig( + RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])), + Supervise(ping, LifeCycle(Permanent)) :: Nil)).newInstance + + supervisor.start + println("Supervisor: " + supervisor) + + println("Sending ping") + ping send CounterMessage(0) + + } + + def stop(context: BundleContext) { + println("Stopping Akka OSGi sample") + + supervisor.stop + } + +} \ No newline at end of file diff --git a/akka-osgi/akka-sample-osgi/src/main/scala/Actors.scala b/akka-osgi/akka-sample-osgi/src/main/scala/Actors.scala new file mode 100644 index 0000000000..008ea98ef1 --- /dev/null +++ b/akka-osgi/akka-sample-osgi/src/main/scala/Actors.scala @@ -0,0 +1,18 @@ + +package se.scalablesolutions.akka.osgi.sample + +import se.scalablesolutions.akka.actor.Actor + +case class CounterMessage(counter: Int) + +class Ping extends Actor { + def receive = { + case CounterMessage(i) => println("Got message " + i) + } +} + +class Pong extends Actor { + def receive = { + case _ => + } +} diff --git a/akka-osgi/deployer/pom.xml b/akka-osgi/deployer/pom.xml new file mode 100644 index 0000000000..e94aa441f3 --- /dev/null +++ b/akka-osgi/deployer/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + + se.scalablesolutions.akka + akka-osgi-parent + 0.7-SNAPSHOT + + + akka-deployer + Akka Deployer + + + + org.osgi + org.osgi.core + + + org.osgi + org.osgi.compendium + + + + + src/main/java + src/test/java + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + **/* + + + + + org.apache.felix + maven-bundle-plugin + + + se.scalablesolutions.akka.osgi.deployer.Activator + se.scalablesolutions.akka.osgi.deployer.* + + + + + + + diff --git a/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/Activator.java b/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/Activator.java new file mode 100644 index 0000000000..8cc1dca792 --- /dev/null +++ b/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/Activator.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Roman Roelofsen + * + * 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. + */ +package se.scalablesolutions.akka.osgi.deployer; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + private static final String DIRINSTALLER_POLL = "dirinstaller.poll"; + private static final String DIRINSTALLER_DIR = "dirinstaller.dir"; + + private DirWatcher watcher; + + public void start(BundleContext context) throws Exception { + String bundlesDir = context.getProperty(DIRINSTALLER_DIR); + bundlesDir = bundlesDir == null ? "akka" : bundlesDir; + + String intervalStr = context.getProperty(DIRINSTALLER_POLL); + Integer interval = intervalStr == null ? 2000 : Integer.valueOf(intervalStr); + + watcher = new DirWatcher(context, bundlesDir, interval); + watcher.startWatching(); + } + + public void stop(BundleContext context) throws Exception { + watcher.stopWatching(); + } + +} diff --git a/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/DirWatcher.java b/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/DirWatcher.java new file mode 100644 index 0000000000..37d65e4c53 --- /dev/null +++ b/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/DirWatcher.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2010 Roman Roelofsen + * + * 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. + */ +package se.scalablesolutions.akka.osgi.deployer; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.util.tracker.ServiceTracker; + +public class DirWatcher { + + private Thread thread; + + private ServiceTracker packageAdminTracker; + + private final BundleContext context; + private final String bundlesDir; + private final int interval; + + private final Map timestamps = new HashMap(); + private boolean modifiedSinceLastRun = false; + + private boolean warningMissingLoadDirPresented = false; + + public DirWatcher(BundleContext context, String bundlesDir, int interval) { + this.packageAdminTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null); + this.packageAdminTracker.open(); + + this.context = context; + this.bundlesDir = bundlesDir; + this.interval = interval; + } + + public void startWatching() { + thread = new Thread() { + @Override + public void run() { + try { + while (!Thread.interrupted()) { + modifiedSinceLastRun = false; + List found = new ArrayList(); + getAllFiles(found, bundlesDir); + analyseNewState(found); + Thread.sleep(interval); + } + } catch (InterruptedException e) { + } + } + }; + thread.start(); + } + + public void stopWatching() { + thread.interrupt(); + } + + private void getAllFiles(List found, String dirName) { + File dir = new File(dirName); + File[] files = dir.listFiles(); + if (files == null) { + if (!warningMissingLoadDirPresented) { + System.out.println("DirInstaller WARNING: Directory '" + dirName + "' does not exist!"); + warningMissingLoadDirPresented = true; + } + return; + } + + for (File f : files) { + try { + if (f.isFile()) + if (f.getName().endsWith(".cfg")) { + found.add(0, f); + } else { + found.add(f); + } + else + getAllFiles(found, f.getCanonicalPath()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + private void analyseNewState(List found) { + // check for new or updated bundles + for (File file : found) { + try { + String string = file.getCanonicalPath(); + Long time = timestamps.get(string); + + // time == null: system startup + // time < lastModified: updated file + if (time == null || time < file.lastModified()) { + timestamps.put(string, file.lastModified()); + modifiedSinceLastRun = true; + + if (string.endsWith(".jar")) + installOrUpdateBundle(string, time == null); + } + } catch (IOException e) { + System.out.println("DirInstaller: Problems accessing file " + file.getName()); + e.printStackTrace(); + } catch (BundleException e) { + System.out.println("DirInstaller: Problems installing or updating bundle. File: " + + file.getName()); + e.printStackTrace(); + } + } + + // check removed bundles + Iterator it = timestamps.keySet().iterator(); + while (it.hasNext()) { + String s = it.next(); + try { + if (!containsFilename(s, found)) { + for (Bundle b : context.getBundles()) { + if (b.getLocation().equals("file:" + s)) { + System.out.println("Removing bundle '" + b.getSymbolicName() + "'"); + b.uninstall(); + modifiedSinceLastRun = true; + } + } + it.remove(); + timestamps.remove(s); + } + } catch (BundleException e) { + System.out.println("DirInstaller: Problems uninstalling bundle: " + s); + } catch (IOException e) { + System.out.println("DirInstaller: Problems processing file: " + e); + } + } + + if (modifiedSinceLastRun) + startAllAndRefresh(); + } + + private boolean containsFilename(String string, List fileList) throws IOException { + for (File f : fileList) { + if (f.getCanonicalPath().equals(string)) + return true; + } + return false; + } + + private void startAllAndRefresh() { + for (Bundle b : context.getBundles()) { + try { + if (b.getState() != Bundle.ACTIVE && !isFragment(b)) { + b.start(); + } + } catch (BundleException e) { + System.out.println("Problems starting bundle: " + b); + e.printStackTrace(); + } + } + PackageAdmin admin = (PackageAdmin) this.packageAdminTracker.getService(); + System.out.println("DirInstaller: Refreshing packages"); + admin.refreshPackages(null); + } + + private boolean isFragment(Bundle b) { + PackageAdmin admin = (PackageAdmin) this.packageAdminTracker.getService(); + return admin.getBundleType(b) == PackageAdmin.BUNDLE_TYPE_FRAGMENT; + } + + private void installOrUpdateBundle(String s, boolean startup) throws BundleException { + // Check if bundle is already installed + // Perform bundle update in this case + for (Bundle b : context.getBundles()) { + if (b.getLocation().endsWith(s)) { + if (startup) // Don't update bundles on startup + return; + + System.out.println("DirInstaller: Updating bundle [" + b.getSymbolicName() + "]"); + b.stop(); + b.update(); + return; + } + } + // If the bundle is not installed, perform bundle install + System.out.println("DirInstaller: Installing bundle [" + s + "]"); + context.installBundle("file:" + s); + } + +} diff --git a/akka-osgi/karaf/pom.xml b/akka-osgi/karaf/pom.xml new file mode 100644 index 0000000000..ffc9dc8697 --- /dev/null +++ b/akka-osgi/karaf/pom.xml @@ -0,0 +1,127 @@ + + + + 4.0.0 + + + akka-osgi-parent + se.scalablesolutions.akka + 0.7-SNAPSHOT + + + akka-karaf + Akka OSGi Karaf Distribution + pom + + + http://www.apache.org/dist/felix/apache-felix-karaf-1.2.0.tar.gz + apache-felix-karaf-1.2.0 + + + + + + akka-dependencies-bundle + se.scalablesolutions.akka + ${project.version} + + + + + scala-library + org.scala-lang-osgi + 2.7.7 + + + commons-io + commons-io + 1.4 + + + org.codehaus.jackson + jackson-core-asl + 1.2.1 + + + org.codehaus.jackson + jackson-mapper-asl + 1.2.1 + + + org.jboss.netty + netty + 3.2.0.ALPHA3 + + + + + akka-core + se.scalablesolutions.akka + ${project.version} + + + akka-deployer + se.scalablesolutions.akka + ${project.version} + + + akka-sample-osgi + se.scalablesolutions.akka + ${project.version} + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + generate-resources + generate-resources + + + + + + + + + run + + + + + + maven-assembly-plugin + + + src/main/assembly/runtime.xml + + + + + make-distribution-dir + package + + directory-single + + + + make-distribution-zip + package + + single + + + + + + + + diff --git a/akka-osgi/karaf/src/main/assembly/runtime.xml b/akka-osgi/karaf/src/main/assembly/runtime.xml new file mode 100644 index 0000000000..2fe4ef12cd --- /dev/null +++ b/akka-osgi/karaf/src/main/assembly/runtime.xml @@ -0,0 +1,37 @@ + + runtime + + zip + + false + + + src/main/resources/runtime + + + + target/generated/runtime + + + + + + + se.scalablesolutions.akka:akka-deployer + + ${karaf.root.dir}/akka + false + false + false + + + + se.scalablesolutions.akka:akka-deployer + + ${karaf.root.dir}/deploy + false + false + false + + + diff --git a/akka-osgi/pom.xml b/akka-osgi/pom.xml new file mode 100644 index 0000000000..9748335c9b --- /dev/null +++ b/akka-osgi/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + + se.scalablesolutions.akka + akka + 0.7-SNAPSHOT + + + akka-osgi-parent + Akka OSGi Parent + + pom + + + 4.2.0 + + + + deployer + akka-dependencies-bundle + akka-sample-osgi + karaf + + + + + + org.osgi + org.osgi.core + ${osgi.version} + provided + + + org.osgi + org.osgi.compendium + ${osgi.version} + provided + + + + + From d52a9cb276a5172d10c968cd718ba12d987fa9da Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Fri, 5 Mar 2010 11:51:23 +0100 Subject: [PATCH 02/33] added akka-osgi module to parent pom --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 3cfc2839a8..ea7edf729e 100644 --- a/pom.xml +++ b/pom.xml @@ -59,6 +59,7 @@ akka-security akka-patterns akka-kernel + akka-osgi akka-fun-test-java akka-samples From ee66023a252b926a0139ab6f27fcc864bb46bba7 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Fri, 12 Mar 2010 12:28:53 +0100 Subject: [PATCH 03/33] rewriting deployer in scala ... work in progress! --- akka-osgi/deployer/pom.xml | 18 +- .../akka/osgi/deployer/Activator.java | 43 ---- .../akka/osgi/deployer/DirWatcher.java | 207 ------------------ .../deployer/src/main/scala/Activator.scala | 36 +++ .../deployer/src/main/scala/DirWatcher.scala | 112 ++++++++++ 5 files changed, 153 insertions(+), 263 deletions(-) delete mode 100644 akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/Activator.java delete mode 100644 akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/DirWatcher.java create mode 100644 akka-osgi/deployer/src/main/scala/Activator.scala create mode 100644 akka-osgi/deployer/src/main/scala/DirWatcher.scala diff --git a/akka-osgi/deployer/pom.xml b/akka-osgi/deployer/pom.xml index e94aa441f3..db35d10dc1 100644 --- a/akka-osgi/deployer/pom.xml +++ b/akka-osgi/deployer/pom.xml @@ -13,6 +13,11 @@ Akka Deployer + + org.scala-lang + scala-library + ${scala.version} + org.osgi org.osgi.core @@ -24,20 +29,7 @@ - src/main/java - src/test/java - - org.apache.maven.plugins - maven-compiler-plugin - - 1.5 - 1.5 - - **/* - - - org.apache.felix maven-bundle-plugin diff --git a/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/Activator.java b/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/Activator.java deleted file mode 100644 index 8cc1dca792..0000000000 --- a/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/Activator.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2010 Roman Roelofsen - * - * 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. - */ -package se.scalablesolutions.akka.osgi.deployer; - -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; - -public class Activator implements BundleActivator { - - private static final String DIRINSTALLER_POLL = "dirinstaller.poll"; - private static final String DIRINSTALLER_DIR = "dirinstaller.dir"; - - private DirWatcher watcher; - - public void start(BundleContext context) throws Exception { - String bundlesDir = context.getProperty(DIRINSTALLER_DIR); - bundlesDir = bundlesDir == null ? "akka" : bundlesDir; - - String intervalStr = context.getProperty(DIRINSTALLER_POLL); - Integer interval = intervalStr == null ? 2000 : Integer.valueOf(intervalStr); - - watcher = new DirWatcher(context, bundlesDir, interval); - watcher.startWatching(); - } - - public void stop(BundleContext context) throws Exception { - watcher.stopWatching(); - } - -} diff --git a/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/DirWatcher.java b/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/DirWatcher.java deleted file mode 100644 index 37d65e4c53..0000000000 --- a/akka-osgi/deployer/src/main/java/se/scalablesolutions/akka/osgi/deployer/DirWatcher.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2010 Roman Roelofsen - * - * 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. - */ -package se.scalablesolutions.akka.osgi.deployer; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; -import org.osgi.service.packageadmin.PackageAdmin; -import org.osgi.util.tracker.ServiceTracker; - -public class DirWatcher { - - private Thread thread; - - private ServiceTracker packageAdminTracker; - - private final BundleContext context; - private final String bundlesDir; - private final int interval; - - private final Map timestamps = new HashMap(); - private boolean modifiedSinceLastRun = false; - - private boolean warningMissingLoadDirPresented = false; - - public DirWatcher(BundleContext context, String bundlesDir, int interval) { - this.packageAdminTracker = new ServiceTracker(context, PackageAdmin.class.getName(), null); - this.packageAdminTracker.open(); - - this.context = context; - this.bundlesDir = bundlesDir; - this.interval = interval; - } - - public void startWatching() { - thread = new Thread() { - @Override - public void run() { - try { - while (!Thread.interrupted()) { - modifiedSinceLastRun = false; - List found = new ArrayList(); - getAllFiles(found, bundlesDir); - analyseNewState(found); - Thread.sleep(interval); - } - } catch (InterruptedException e) { - } - } - }; - thread.start(); - } - - public void stopWatching() { - thread.interrupt(); - } - - private void getAllFiles(List found, String dirName) { - File dir = new File(dirName); - File[] files = dir.listFiles(); - if (files == null) { - if (!warningMissingLoadDirPresented) { - System.out.println("DirInstaller WARNING: Directory '" + dirName + "' does not exist!"); - warningMissingLoadDirPresented = true; - } - return; - } - - for (File f : files) { - try { - if (f.isFile()) - if (f.getName().endsWith(".cfg")) { - found.add(0, f); - } else { - found.add(f); - } - else - getAllFiles(found, f.getCanonicalPath()); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private void analyseNewState(List found) { - // check for new or updated bundles - for (File file : found) { - try { - String string = file.getCanonicalPath(); - Long time = timestamps.get(string); - - // time == null: system startup - // time < lastModified: updated file - if (time == null || time < file.lastModified()) { - timestamps.put(string, file.lastModified()); - modifiedSinceLastRun = true; - - if (string.endsWith(".jar")) - installOrUpdateBundle(string, time == null); - } - } catch (IOException e) { - System.out.println("DirInstaller: Problems accessing file " + file.getName()); - e.printStackTrace(); - } catch (BundleException e) { - System.out.println("DirInstaller: Problems installing or updating bundle. File: " - + file.getName()); - e.printStackTrace(); - } - } - - // check removed bundles - Iterator it = timestamps.keySet().iterator(); - while (it.hasNext()) { - String s = it.next(); - try { - if (!containsFilename(s, found)) { - for (Bundle b : context.getBundles()) { - if (b.getLocation().equals("file:" + s)) { - System.out.println("Removing bundle '" + b.getSymbolicName() + "'"); - b.uninstall(); - modifiedSinceLastRun = true; - } - } - it.remove(); - timestamps.remove(s); - } - } catch (BundleException e) { - System.out.println("DirInstaller: Problems uninstalling bundle: " + s); - } catch (IOException e) { - System.out.println("DirInstaller: Problems processing file: " + e); - } - } - - if (modifiedSinceLastRun) - startAllAndRefresh(); - } - - private boolean containsFilename(String string, List fileList) throws IOException { - for (File f : fileList) { - if (f.getCanonicalPath().equals(string)) - return true; - } - return false; - } - - private void startAllAndRefresh() { - for (Bundle b : context.getBundles()) { - try { - if (b.getState() != Bundle.ACTIVE && !isFragment(b)) { - b.start(); - } - } catch (BundleException e) { - System.out.println("Problems starting bundle: " + b); - e.printStackTrace(); - } - } - PackageAdmin admin = (PackageAdmin) this.packageAdminTracker.getService(); - System.out.println("DirInstaller: Refreshing packages"); - admin.refreshPackages(null); - } - - private boolean isFragment(Bundle b) { - PackageAdmin admin = (PackageAdmin) this.packageAdminTracker.getService(); - return admin.getBundleType(b) == PackageAdmin.BUNDLE_TYPE_FRAGMENT; - } - - private void installOrUpdateBundle(String s, boolean startup) throws BundleException { - // Check if bundle is already installed - // Perform bundle update in this case - for (Bundle b : context.getBundles()) { - if (b.getLocation().endsWith(s)) { - if (startup) // Don't update bundles on startup - return; - - System.out.println("DirInstaller: Updating bundle [" + b.getSymbolicName() + "]"); - b.stop(); - b.update(); - return; - } - } - // If the bundle is not installed, perform bundle install - System.out.println("DirInstaller: Installing bundle [" + s + "]"); - context.installBundle("file:" + s); - } - -} diff --git a/akka-osgi/deployer/src/main/scala/Activator.scala b/akka-osgi/deployer/src/main/scala/Activator.scala new file mode 100644 index 0000000000..d12b63ba16 --- /dev/null +++ b/akka-osgi/deployer/src/main/scala/Activator.scala @@ -0,0 +1,36 @@ + +package se.scalablesolutions.akka.osgi.deployer + +import org.osgi.framework.{BundleContext, BundleActivator} +import java.io.File + +class Activator extends BundleActivator { + + private val AKKA_DEPLOYER_DIR = "akka.deployer.dir" + private val AKKA_DEPLOYER_POLL = "akka.deployer.poll" + + private var watcher: DirWatcher = _ + + def start(context: BundleContext) { + var bundlesDir = context.getProperty(AKKA_DEPLOYER_DIR) + bundlesDir = if (bundlesDir == null) "akka" else bundlesDir + + // check dir exists + if (new File(bundlesDir).listFiles == null) { + System.out.println("DirInstaller WARNING: Directory '" + bundlesDir + "' does not exist!") + return + } + + var interval = context.getProperty(AKKA_DEPLOYER_POLL) + interval = if (interval == null) "2000" else interval + + watcher = new DirWatcher(context, bundlesDir, interval.toInt) + watcher.startWatching + } + + def stop(context: BundleContext) { + if (watcher != null) + watcher.stopWatching + } + +} diff --git a/akka-osgi/deployer/src/main/scala/DirWatcher.scala b/akka-osgi/deployer/src/main/scala/DirWatcher.scala new file mode 100644 index 0000000000..4d4292604c --- /dev/null +++ b/akka-osgi/deployer/src/main/scala/DirWatcher.scala @@ -0,0 +1,112 @@ + +package se.scalablesolutions.akka.osgi.deployer + +import org.osgi.util.tracker.ServiceTracker +import java.io.File +import org.osgi.service.packageadmin.PackageAdmin +import org.osgi.framework.{Bundle, BundleContext} + +class DirWatcher(context: BundleContext, bundlesDir: String, interval: Int) { + + private var running = false + + private final var timestamps = Map[String, Long]() + + private val packageAdminTracker = new ServiceTracker(context, classOf[PackageAdmin].getName, null) + packageAdminTracker.open + + def startWatching { + if (running) return + running = true + new Thread { + override def run { + try { + while (running) { + val found = getAllFiles(bundlesDir) + analyseNewState(found) + Thread.sleep(interval) + } + } + catch { + case e: InterruptedException => + } + } + }.start() + } + + def stopWatching { + running = false + } + + private def getAllFiles(dirName: String): List[File] = { + val content = new File(dirName).listFiles + val files = content.filter(_.isFile).toList + val childs = content.filter(_.isDirectory).toList.flatMap(d => getAllFiles(d.getCanonicalPath)) + files ::: childs + } + + private def analyseNewState(found: List[File]) { + println("FOUND:" + found) + + // new or updated + val changed = found.filter(f => timestamps.getOrElse(f.getCanonicalPath, -1L) < f.lastModified) + changed.foreach {f => + val name = f.getCanonicalPath + timestamps += (name -> f.lastModified) + if (name.endsWith(".jar")) installOrUpdateBundle(name) + } + println("CHANGED:" + changed) + + // removed + val removed = timestamps.filter(f => !found.map(_.getCanonicalPath).contains(f._1)) + removed.foreach {f => + context.getBundles.filter(b => b.getLocation.equals("file:" + f._1)).foreach(_.uninstall) + timestamps -= f._1 + } + println("REMOVED:" + removed) + + if (changed.size + removed.size > 0) + startAllAndRefresh() + + println("") + } + + private def startAllAndRefresh() { + context.getBundles.filter(b => b.getState != Bundle.ACTIVE && !isFragment(b)).foreach {b => + try { + b.start + } catch { + case e: Exception => { + System.out.println("Problems starting bundle: " + b) + e.printStackTrace + } + } + } + + val admin = this.packageAdminTracker.getService.asInstanceOf[PackageAdmin] + System.out.println("DirInstaller: Refreshing packages") + admin.refreshPackages(null) + } + + + private def isFragment(b: Bundle): Boolean = { + var admin: PackageAdmin = this.packageAdminTracker.getService.asInstanceOf[PackageAdmin] + return admin.getBundleType(b) == PackageAdmin.BUNDLE_TYPE_FRAGMENT + } + + + private def installOrUpdateBundle(s: String) { + for (b <- context.getBundles) { + if (b.getLocation.endsWith(s)) { + System.out.println("DirInstaller: Updating bundle [" + b.getSymbolicName + "]") + b.stop + b.update + return + } + } + System.out.println("DirInstaller: Installing bundle [" + s + "]") + context.installBundle("file:" + s) + } + + +} \ No newline at end of file From 7a03fba47b4debb3a9b3635c38b39bc714e8657e Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Tue, 25 May 2010 15:19:06 +0200 Subject: [PATCH 04/33] changed karaf url --- akka-osgi/karaf/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/akka-osgi/karaf/pom.xml b/akka-osgi/karaf/pom.xml index ffc9dc8697..aaf297a82c 100644 --- a/akka-osgi/karaf/pom.xml +++ b/akka-osgi/karaf/pom.xml @@ -15,8 +15,8 @@ pom - http://www.apache.org/dist/felix/apache-felix-karaf-1.2.0.tar.gz - apache-felix-karaf-1.2.0 + http://www.apache.org/dist/felix/apache-felix-karaf-1.4.0.tar.gz + apache-felix-karaf-1.4.0 From 75dc8474b76d1c8f141b293f5da6b6b2f0eba590 Mon Sep 17 00:00:00 2001 From: Andreas Kollegger Date: Sun, 6 Jun 2010 14:33:10 -0400 Subject: [PATCH 05/33] initial changes for OSGification: added bnd4sbt plugin, changed artifact naming to include _osgi --- project/build/AkkaProject.scala | 42 ++++++++++++++++++++------------ project/build/lib/bnd4sbt.jar | Bin 0 -> 8092 bytes project/plugins/Plugins.scala | 3 ++- 3 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 project/build/lib/bnd4sbt.jar diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 4ad4858d09..3aa43b196d 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -10,6 +10,8 @@ import java.util.jar.Attributes import java.util.jar.Attributes.Name._ import java.io.File +import com.weiglewilczek.bnd4sbt._ + class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------ @@ -25,6 +27,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { lazy val deployPath = info.projectPath / "deploy" lazy val distPath = info.projectPath / "dist" + val artifactQualifier = buildScalaVersion + "_osgi" + override def compileOptions = super.compileOptions ++ Seq("-deprecation", "-Xmigration", @@ -36,7 +40,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { override def javaCompileOptions = JavaCompileOption("-Xlint:unchecked") :: super.javaCompileOptions.toList - def distName = "%s_%s-%s.zip".format(name, buildScalaVersion, version) + def distName = "%s_%s-%s.zip".format(name, artifactQualifier, version) lazy val dist = zipTask(allArtifacts, "dist", distName) dependsOn (`package`) describedAs("Zips up the distribution.") @@ -109,20 +113,20 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { override def manifestClassPath = Some(allArtifacts.getFiles .filter(_.getName.endsWith(".jar")) .filter(!_.getName.contains("scala-library")) - .map("lib_managed/scala_%s/compile/".format(buildScalaVersion) + _.getName) + .map("lib_managed/scala_%s/compile/".format(artifactQualifier) + _.getName) .mkString(" ") + " scala-library.jar" + - " dist/akka-core_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-http_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-camel_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-amqp_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-persistence-common_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-persistence-redis_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-persistence-mongo_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-persistence-cassandra_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-kernel_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-spring_%s-%s.jar".format(buildScalaVersion, version) + - " dist/akka-jta_%s-%s.jar".format(buildScalaVersion, version) + " dist/akka-core_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-http_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-camel_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-amqp_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-persistence-common_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-persistence-redis_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-persistence-mongo_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-persistence-cassandra_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-kernel_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-spring_%s-%s.jar".format(artifactQualifier, version) + + " dist/akka-jta_%s-%s.jar".format(artifactQualifier, version) ) //Exclude slf4j1.5.11 from the classpath, it's conflicting... @@ -398,12 +402,14 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { } def akkaArtifacts = { - descendents(info.projectPath / "dist", "*" + buildScalaVersion + "-" + version + ".jar") + descendents(info.projectPath / "dist", "*" + artifactQualifier + "-" + version + ".jar") } // ------------------------------------------------------------ - class AkkaDefaultProject(info: ProjectInfo, val deployPath: Path) extends DefaultProject(info) with DeployProject + class AkkaDefaultProject(info: ProjectInfo, val deployPath: Path) extends DefaultProject(info) with DeployProject with OSGiProject + + trait DeployProject extends DefaultProject { // defines where the deployTask copies jars to @@ -424,4 +430,10 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { FileUtilities.copyFile(jar, toDir / jar.name, log) } else None } + + trait OSGiProject extends DefaultProject with BNDPlugin { + override def bndExportPackage = Set("*") + } + + } diff --git a/project/build/lib/bnd4sbt.jar b/project/build/lib/bnd4sbt.jar new file mode 100644 index 0000000000000000000000000000000000000000..5906b493003a67283602179ffdf24d15acdeb0b4 GIT binary patch literal 8092 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 Date: Sun, 6 Jun 2010 20:05:26 -0400 Subject: [PATCH 06/33] initial implementation of OSGiWrapperProject, applied to jgroups dependency to make it OSGi-friendly --- .../src/main/resources/features.xml | 7 +++ project/build/AkkaProject.scala | 51 +++++++++++++++++- project/plugins/.Plugins.scala.swp | Bin 12288 -> 0 bytes 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 akka-karaf/akka-features/src/main/resources/features.xml delete mode 100644 project/plugins/.Plugins.scala.swp diff --git a/akka-karaf/akka-features/src/main/resources/features.xml b/akka-karaf/akka-features/src/main/resources/features.xml new file mode 100644 index 0000000000..9176437f46 --- /dev/null +++ b/akka-karaf/akka-features/src/main/resources/features.xml @@ -0,0 +1,7 @@ + + + + mvn:se.scalablesolutions.akka.akka-wrap/jgroups-wrapper_2.8.0.RC3_osgi/2.9.0.GA + mvn:se.scalablesolutions.akka/akka-core_2.8.0.RC3_osgi/0.9 + + diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 47559ad865..2b96abb597 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -86,6 +86,9 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // examples lazy val akka_samples = project("akka-samples", "akka-samples", new AkkaSamplesParentProject(_)) + // OSGi wrappers + lazy val akka_wrappers = project("akka-wrap", "akka-wrap", new AkkaWrappersParentProject(_)) + // ------------------------------------------------------------ // Run Akka microkernel using 'sbt run' + use for packaging executable JAR override def mainClass = Some("se.scalablesolutions.akka.kernel.Main") @@ -169,9 +172,28 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { " -Dpackaging=jar -DgeneratePom=true" command ! log } - None + None } dependsOn(dist) describedAs("Run mvn install for artifacts in dist.") + lazy val publishLocalMvnWrapped = runMvnInstallWrapped + def runMvnInstallWrapped = task { + for (absPath <- wrappedArtifacts.getPaths) { + val artifactRE = """.*/([^/]+)-([^-]+).jar""".r + val artifactRE(artifactId, artifactVersion) = absPath + val command = "mvn install:install-file" + + " -Dfile=" + absPath + + " -DgroupId=se.scalablesolutions.akka.akka-wrap" + + " -DartifactId=" + artifactId + + " -Dversion=" + artifactVersion + + " -Dpackaging=jar -DgeneratePom=true" + command ! log + } + None + } dependsOn(`package`) describedAs("Run mvn install for wrapped artifacts in akka-wrap.") + + + + // ------------------------------------------------------------ // subprojects class AkkaCoreProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { @@ -362,6 +384,23 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { new AkkaSampleRemoteProject(_), akka_kernel) } + + // ================= OSGi Wrappers ================== + class JgroupsWrapperProject(info: ProjectInfo) extends OSGiWrapperProject(info) { + override def wrappedVersion = "2.9.0.GA" + override def bndImportPackage = Set("org.testng.*;resolution:=optional", + "org.bouncycastle.jce.provider;resolution:=optional", + "bsh;resolution:=optional", + "*") + + val jgroups = "jgroups" % "jgroups" % wrappedVersion % "compile" + } + + class AkkaWrappersParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val jgroups_wrapper = project("jgroups-wrapper", "jgroups-wrapper", + new JgroupsWrapperProject(_)) + } + // ------------------------------------------------------------ // helper functions def removeDupEntries(paths: PathFinder) = @@ -390,6 +429,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { def akkaArtifacts = descendents(info.projectPath / "dist", "*" + buildScalaVersion + "_osgi-" + version + ".jar") + def wrappedArtifacts = descendents(info.projectPath / "akka-wrap", "*" + buildScalaVersion + "_osgi-" + "*.jar") + // ------------------------------------------------------------ class AkkaDefaultProject(info: ProjectInfo, val deployPath: Path) extends DefaultProject(info) with DeployProject with OSGiProject @@ -419,5 +460,13 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { } + abstract class OSGiWrapperProject(info: ProjectInfo) extends DefaultProject(info) with BNDPlugin { + def wrappedVersion:String + override def version = OpaqueVersion(wrappedVersion) + override def artifactID = moduleID + "_osgi" + override def bndEmbedDependencies = true + override def bndExportPackage = Set("*") + } + } diff --git a/project/plugins/.Plugins.scala.swp b/project/plugins/.Plugins.scala.swp deleted file mode 100644 index 39709506fa92639a4034a394fdcff22edf268002..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmYc?2=nw+FxN9;U|?VnU|?{5&Fv#s_Kdkeijg5PJ3l8UH9bAG2qcLcyCs&CRO3s$Q}1xG_*Gz5@CptK}S*MgV9*vQa8SxHerSSS=( zc9cCD0;3@?8UmvsFd71*Aut*OqaiRF0;3@?0z#mqfRW)p0|NsS0|Nsm0|Nsi6psL} zk9u)51V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qOb8^VFffSoF);Y^LFWHK zV*sD|85lnCGcau6XJAO=XJC-!XJEL-$H36d$G{K>RV&BGz#zuQz`)7Jz`z00J7Pj? z)T5&zFd71*Aut*OqaiRF0;3@?8UmvsFd71*Auz;4pq9Z70u+oal3*MdZ3hxGHAzV{ zPqR!iGq$uyOR-EeO*A$)u`n<*F*Zz0GO;u_H8nM>Wl&HkOUzM7%1bdRPAXBbRZvRK z&($kW%}mcpEzitJu1d{TQczV;f+_&B4fITu6ciZr^&xsQQ&JOQ8q+gNGD?&5atl&( zO7ink^T6thlS*_!igY3C!NFn%dWO2eevSdb9{wRpaHEP+3-UomxqIjqB&MepD}V%) z6cS4mlrl<63as??;U?=rtxC?%)z3{VOU=_)g8HSnASE>z;+N85y^_@AjJ(X`#GIT; zJ&+98b`V1s Date: Mon, 7 Jun 2010 15:59:47 +0200 Subject: [PATCH 07/33] Removed Maven projects and added bnd4sbt --- akka-osgi/akka-dependencies-bundle/pom.xml | 56 -------- akka-osgi/akka-sample-osgi/pom.xml | 49 ------- .../src/main/scala/Activator.scala | 36 ----- .../src/main/scala/Actors.scala | 18 --- akka-osgi/deployer/pom.xml | 46 ------- .../deployer/src/main/scala/Activator.scala | 36 ----- .../deployer/src/main/scala/DirWatcher.scala | 112 --------------- akka-osgi/karaf/pom.xml | 127 ------------------ akka-osgi/karaf/src/main/assembly/runtime.xml | 37 ----- akka-osgi/pom.xml | 45 ------- project/build/AkkaProject.scala | 26 ++++ project/plugins/Plugins.scala | 4 +- 12 files changed, 29 insertions(+), 563 deletions(-) delete mode 100644 akka-osgi/akka-dependencies-bundle/pom.xml delete mode 100644 akka-osgi/akka-sample-osgi/pom.xml delete mode 100644 akka-osgi/akka-sample-osgi/src/main/scala/Activator.scala delete mode 100644 akka-osgi/akka-sample-osgi/src/main/scala/Actors.scala delete mode 100644 akka-osgi/deployer/pom.xml delete mode 100644 akka-osgi/deployer/src/main/scala/Activator.scala delete mode 100644 akka-osgi/deployer/src/main/scala/DirWatcher.scala delete mode 100644 akka-osgi/karaf/pom.xml delete mode 100644 akka-osgi/karaf/src/main/assembly/runtime.xml delete mode 100644 akka-osgi/pom.xml diff --git a/akka-osgi/akka-dependencies-bundle/pom.xml b/akka-osgi/akka-dependencies-bundle/pom.xml deleted file mode 100644 index 74d1792be4..0000000000 --- a/akka-osgi/akka-dependencies-bundle/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - 4.0.0 - - - se.scalablesolutions.akka - akka-osgi-parent - 0.7-SNAPSHOT - - - akka-dependencies-bundle - Akka Dependencies Bundle - - - - akka-kernel - se.scalablesolutions.akka - ${project.version} - - - - - - - org.apache.felix - maven-bundle-plugin - - - - *;resolution:=optional - - - - !test.*, - - - !scala.*, - !org.apache.commons.io.*, - !org.codehaus.jackson.*, - !org.codehaus.jettison.*, - !org.jboss.netty.*, - - - !se.scalablesolutions.akka.*, - - - *;-split-package:=merge-first - - - - - - - - diff --git a/akka-osgi/akka-sample-osgi/pom.xml b/akka-osgi/akka-sample-osgi/pom.xml deleted file mode 100644 index bd48ca2392..0000000000 --- a/akka-osgi/akka-sample-osgi/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - 4.0.0 - - akka-sample-osgi - Akka OSGi Sample Module - - jar - - - akka-osgi-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - - - akka-core - se.scalablesolutions.akka - ${project.version} - - - org.osgi - org.osgi.core - - - org.osgi - org.osgi.compendium - - - - - - - org.apache.felix - maven-bundle-plugin - - - se.scalablesolutions.akka.osgi.sample.Activator - - se.scalablesolutions.akka.osgi.sample - - - - - - - - diff --git a/akka-osgi/akka-sample-osgi/src/main/scala/Activator.scala b/akka-osgi/akka-sample-osgi/src/main/scala/Activator.scala deleted file mode 100644 index 7925a2705f..0000000000 --- a/akka-osgi/akka-sample-osgi/src/main/scala/Activator.scala +++ /dev/null @@ -1,36 +0,0 @@ - -package se.scalablesolutions.akka.osgi.sample - -import org.osgi.framework.{BundleContext, BundleActivator} -import se.scalablesolutions.akka.config.ScalaConfig._ -import se.scalablesolutions.akka.actor.{Supervisor, SupervisorFactory} - -class Activator extends BundleActivator { - - var supervisor: Supervisor = _ - - val ping = new Ping - - def start(context: BundleContext) { - println("Starting Akka OSGi sample") - - supervisor = SupervisorFactory( - SupervisorConfig( - RestartStrategy(OneForOne, 3, 100, List(classOf[Exception])), - Supervise(ping, LifeCycle(Permanent)) :: Nil)).newInstance - - supervisor.start - println("Supervisor: " + supervisor) - - println("Sending ping") - ping send CounterMessage(0) - - } - - def stop(context: BundleContext) { - println("Stopping Akka OSGi sample") - - supervisor.stop - } - -} \ No newline at end of file diff --git a/akka-osgi/akka-sample-osgi/src/main/scala/Actors.scala b/akka-osgi/akka-sample-osgi/src/main/scala/Actors.scala deleted file mode 100644 index 008ea98ef1..0000000000 --- a/akka-osgi/akka-sample-osgi/src/main/scala/Actors.scala +++ /dev/null @@ -1,18 +0,0 @@ - -package se.scalablesolutions.akka.osgi.sample - -import se.scalablesolutions.akka.actor.Actor - -case class CounterMessage(counter: Int) - -class Ping extends Actor { - def receive = { - case CounterMessage(i) => println("Got message " + i) - } -} - -class Pong extends Actor { - def receive = { - case _ => - } -} diff --git a/akka-osgi/deployer/pom.xml b/akka-osgi/deployer/pom.xml deleted file mode 100644 index db35d10dc1..0000000000 --- a/akka-osgi/deployer/pom.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - 4.0.0 - - - se.scalablesolutions.akka - akka-osgi-parent - 0.7-SNAPSHOT - - - akka-deployer - Akka Deployer - - - - org.scala-lang - scala-library - ${scala.version} - - - org.osgi - org.osgi.core - - - org.osgi - org.osgi.compendium - - - - - - - org.apache.felix - maven-bundle-plugin - - - se.scalablesolutions.akka.osgi.deployer.Activator - se.scalablesolutions.akka.osgi.deployer.* - - - - - - - diff --git a/akka-osgi/deployer/src/main/scala/Activator.scala b/akka-osgi/deployer/src/main/scala/Activator.scala deleted file mode 100644 index d12b63ba16..0000000000 --- a/akka-osgi/deployer/src/main/scala/Activator.scala +++ /dev/null @@ -1,36 +0,0 @@ - -package se.scalablesolutions.akka.osgi.deployer - -import org.osgi.framework.{BundleContext, BundleActivator} -import java.io.File - -class Activator extends BundleActivator { - - private val AKKA_DEPLOYER_DIR = "akka.deployer.dir" - private val AKKA_DEPLOYER_POLL = "akka.deployer.poll" - - private var watcher: DirWatcher = _ - - def start(context: BundleContext) { - var bundlesDir = context.getProperty(AKKA_DEPLOYER_DIR) - bundlesDir = if (bundlesDir == null) "akka" else bundlesDir - - // check dir exists - if (new File(bundlesDir).listFiles == null) { - System.out.println("DirInstaller WARNING: Directory '" + bundlesDir + "' does not exist!") - return - } - - var interval = context.getProperty(AKKA_DEPLOYER_POLL) - interval = if (interval == null) "2000" else interval - - watcher = new DirWatcher(context, bundlesDir, interval.toInt) - watcher.startWatching - } - - def stop(context: BundleContext) { - if (watcher != null) - watcher.stopWatching - } - -} diff --git a/akka-osgi/deployer/src/main/scala/DirWatcher.scala b/akka-osgi/deployer/src/main/scala/DirWatcher.scala deleted file mode 100644 index 4d4292604c..0000000000 --- a/akka-osgi/deployer/src/main/scala/DirWatcher.scala +++ /dev/null @@ -1,112 +0,0 @@ - -package se.scalablesolutions.akka.osgi.deployer - -import org.osgi.util.tracker.ServiceTracker -import java.io.File -import org.osgi.service.packageadmin.PackageAdmin -import org.osgi.framework.{Bundle, BundleContext} - -class DirWatcher(context: BundleContext, bundlesDir: String, interval: Int) { - - private var running = false - - private final var timestamps = Map[String, Long]() - - private val packageAdminTracker = new ServiceTracker(context, classOf[PackageAdmin].getName, null) - packageAdminTracker.open - - def startWatching { - if (running) return - running = true - new Thread { - override def run { - try { - while (running) { - val found = getAllFiles(bundlesDir) - analyseNewState(found) - Thread.sleep(interval) - } - } - catch { - case e: InterruptedException => - } - } - }.start() - } - - def stopWatching { - running = false - } - - private def getAllFiles(dirName: String): List[File] = { - val content = new File(dirName).listFiles - val files = content.filter(_.isFile).toList - val childs = content.filter(_.isDirectory).toList.flatMap(d => getAllFiles(d.getCanonicalPath)) - files ::: childs - } - - private def analyseNewState(found: List[File]) { - println("FOUND:" + found) - - // new or updated - val changed = found.filter(f => timestamps.getOrElse(f.getCanonicalPath, -1L) < f.lastModified) - changed.foreach {f => - val name = f.getCanonicalPath - timestamps += (name -> f.lastModified) - if (name.endsWith(".jar")) installOrUpdateBundle(name) - } - println("CHANGED:" + changed) - - // removed - val removed = timestamps.filter(f => !found.map(_.getCanonicalPath).contains(f._1)) - removed.foreach {f => - context.getBundles.filter(b => b.getLocation.equals("file:" + f._1)).foreach(_.uninstall) - timestamps -= f._1 - } - println("REMOVED:" + removed) - - if (changed.size + removed.size > 0) - startAllAndRefresh() - - println("") - } - - private def startAllAndRefresh() { - context.getBundles.filter(b => b.getState != Bundle.ACTIVE && !isFragment(b)).foreach {b => - try { - b.start - } catch { - case e: Exception => { - System.out.println("Problems starting bundle: " + b) - e.printStackTrace - } - } - } - - val admin = this.packageAdminTracker.getService.asInstanceOf[PackageAdmin] - System.out.println("DirInstaller: Refreshing packages") - admin.refreshPackages(null) - } - - - private def isFragment(b: Bundle): Boolean = { - var admin: PackageAdmin = this.packageAdminTracker.getService.asInstanceOf[PackageAdmin] - return admin.getBundleType(b) == PackageAdmin.BUNDLE_TYPE_FRAGMENT - } - - - private def installOrUpdateBundle(s: String) { - for (b <- context.getBundles) { - if (b.getLocation.endsWith(s)) { - System.out.println("DirInstaller: Updating bundle [" + b.getSymbolicName + "]") - b.stop - b.update - return - } - } - System.out.println("DirInstaller: Installing bundle [" + s + "]") - context.installBundle("file:" + s) - } - - -} \ No newline at end of file diff --git a/akka-osgi/karaf/pom.xml b/akka-osgi/karaf/pom.xml deleted file mode 100644 index aaf297a82c..0000000000 --- a/akka-osgi/karaf/pom.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - 4.0.0 - - - akka-osgi-parent - se.scalablesolutions.akka - 0.7-SNAPSHOT - - - akka-karaf - Akka OSGi Karaf Distribution - pom - - - http://www.apache.org/dist/felix/apache-felix-karaf-1.4.0.tar.gz - apache-felix-karaf-1.4.0 - - - - - - akka-dependencies-bundle - se.scalablesolutions.akka - ${project.version} - - - - - scala-library - org.scala-lang-osgi - 2.7.7 - - - commons-io - commons-io - 1.4 - - - org.codehaus.jackson - jackson-core-asl - 1.2.1 - - - org.codehaus.jackson - jackson-mapper-asl - 1.2.1 - - - org.jboss.netty - netty - 3.2.0.ALPHA3 - - - - - akka-core - se.scalablesolutions.akka - ${project.version} - - - akka-deployer - se.scalablesolutions.akka - ${project.version} - - - akka-sample-osgi - se.scalablesolutions.akka - ${project.version} - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - generate-resources - generate-resources - - - - - - - - - run - - - - - - maven-assembly-plugin - - - src/main/assembly/runtime.xml - - - - - make-distribution-dir - package - - directory-single - - - - make-distribution-zip - package - - single - - - - - - - - diff --git a/akka-osgi/karaf/src/main/assembly/runtime.xml b/akka-osgi/karaf/src/main/assembly/runtime.xml deleted file mode 100644 index 2fe4ef12cd..0000000000 --- a/akka-osgi/karaf/src/main/assembly/runtime.xml +++ /dev/null @@ -1,37 +0,0 @@ - - runtime - - zip - - false - - - src/main/resources/runtime - - - - target/generated/runtime - - - - - - - se.scalablesolutions.akka:akka-deployer - - ${karaf.root.dir}/akka - false - false - false - - - - se.scalablesolutions.akka:akka-deployer - - ${karaf.root.dir}/deploy - false - false - false - - - diff --git a/akka-osgi/pom.xml b/akka-osgi/pom.xml deleted file mode 100644 index 9748335c9b..0000000000 --- a/akka-osgi/pom.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - 4.0.0 - - - se.scalablesolutions.akka - akka - 0.7-SNAPSHOT - - - akka-osgi-parent - Akka OSGi Parent - - pom - - - 4.2.0 - - - - deployer - akka-dependencies-bundle - akka-sample-osgi - karaf - - - - - - org.osgi - org.osgi.core - ${osgi.version} - provided - - - org.osgi - org.osgi.compendium - ${osgi.version} - provided - - - - - diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index a26396c8e8..d3491f6a3f 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -10,6 +10,8 @@ import java.util.jar.Attributes import java.util.jar.Attributes.Name._ import java.io.File +import com.weiglewilczek.bnd4sbt.BNDPlugin + class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------ @@ -65,6 +67,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { lazy val akka_jta = project("akka-jta", "akka-jta", new AkkaJTAProject(_), akka_core) lazy val akka_kernel = project("akka-kernel", "akka-kernel", new AkkaKernelProject(_), akka_core, akka_http, akka_spring, akka_camel, akka_persistence, akka_amqp) + lazy val akka_osgi = project("akka-osgi", "akka-osgi", new AkkaOSGiParentProject(_)) // functional tests in java lazy val akka_fun_test = project("akka-fun-test-java", "akka-fun-test-java", new AkkaFunTestProject(_), akka_kernel) @@ -273,6 +276,29 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val jta_spec = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" } + // ================= OSGi Packaging ================== + class AkkaOSGiBundleProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { + override def bndClasspath = compileClasspath + override def bndPrivatePackage = Set("") + override def bndExportPackage = Set("se.scalablesolutions.akka.*;version=0.9") + } + + class AkkaOSGiAssemblyProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { + // FIXME: Find out how to replace mvn-assembly within SBT + + //override def packageAction = task { + //FileUtilities.copy(info.dependencies.map(_.outputPath), "XXX", true, true, log) + //None + //} dependsOn(compile) describedAs("Creates the OSGi distribution.") + } + + class AkkaOSGiParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val akka_osgi_bundle = project("akka-osgi-bundle", "akka-osgi-bundle", + new AkkaOSGiBundleProject(_), akka_kernel) + lazy val akka_osgi_assembly = project("akka-osgi-assembly", "akka-osgi-assembly", + new AkkaOSGiAssemblyProject(_), akka_osgi_bundle) + } + // ================= TEST ================== class AkkaFunTestProject(info: ProjectInfo) extends DefaultProject(info) { val jackson_core_asl = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" diff --git a/project/plugins/Plugins.scala b/project/plugins/Plugins.scala index f36c65ed13..895df56970 100644 --- a/project/plugins/Plugins.scala +++ b/project/plugins/Plugins.scala @@ -5,4 +5,6 @@ class Plugins(info: ProjectInfo) extends PluginDefinition(info) { val spdeSbt = "us.technically.spde" % "spde-sbt-plugin" % "0.4.1" // val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/" // val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT" -} \ No newline at end of file + + lazy val bnd4sbt = "com.weiglewilczek" % "bnd4sbt" % "0.4" +} From b36785172961b2516efae1ac3350e83baec57b30 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Mon, 7 Jun 2010 16:28:19 +0200 Subject: [PATCH 08/33] Added dependencies-bundle. --- project/build/AkkaProject.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index c5dbec2f30..945cd2491f 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -302,6 +302,13 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { override def bndExportPackage = Set("se.scalablesolutions.akka.*;version=0.9") } + class AkkaOSGiDependenciesBundleProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { + override def bndClasspath = compileClasspath + override def bndPrivatePackage = Set("") + override def bndImportPackage = Set("*;resolution:=optional") + override def bndExportPackage = Set("!se.scalablesolutions.akka.*", "*") + } + class AkkaOSGiAssemblyProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { // FIXME: Find out how to replace mvn-assembly within SBT @@ -314,6 +321,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { class AkkaOSGiParentProject(info: ProjectInfo) extends ParentProject(info) { lazy val akka_osgi_bundle = project("akka-osgi-bundle", "akka-osgi-bundle", new AkkaOSGiBundleProject(_), akka_kernel) + lazy val akka_osgi_dependencies_bundle = project("akka-osgi-dependencies-bundle", "akka-osgi-dependencies-bundle", + new AkkaOSGiDependenciesBundleProject(_), akka_kernel) lazy val akka_osgi_assembly = project("akka-osgi-assembly", "akka-osgi-assembly", new AkkaOSGiAssemblyProject(_), akka_osgi_bundle) } From 556cbc3405c97bbba13b5a6987124c53d868d1ee Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Mon, 7 Jun 2010 16:46:49 +0200 Subject: [PATCH 09/33] Removed some dependencies since they will be provided by their own bundles --- project/build/AkkaProject.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 945cd2491f..3b8bdf44da 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -306,7 +306,18 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { override def bndClasspath = compileClasspath override def bndPrivatePackage = Set("") override def bndImportPackage = Set("*;resolution:=optional") - override def bndExportPackage = Set("!se.scalablesolutions.akka.*", "*") + override def bndExportPackage = Set( + // Provided by Akka bundle + "!se.scalablesolutions.akka.*", + + // Provided by other bundles + "!org.apache.commons.io.*", + "!org.codehaus.jackson.*", + "!org.codehaus.jettison.*", + "!org.jboss.netty.*", + + // Export the rest + "*") } class AkkaOSGiAssemblyProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { From 89f581ae52a1b8e2ef38081436a9f9153f229e93 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Mon, 7 Jun 2010 17:36:11 +0200 Subject: [PATCH 10/33] Work in progress! Trying to find an alternative to mvn assembly --- project/build/AkkaProject.scala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 3b8bdf44da..706e0573ac 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -323,19 +323,24 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { class AkkaOSGiAssemblyProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { // FIXME: Find out how to replace mvn-assembly within SBT - //override def packageAction = task { - //FileUtilities.copy(info.dependencies.map(_.outputPath), "XXX", true, true, log) - //None - //} dependsOn(compile) describedAs("Creates the OSGi distribution.") + override def packageAction = task { + + println("----------------------------") + println("2" + unmanagedClasspath) + + + //FileUtilities.copy(info.dependencies.map(_.outputPath), "bundles", true, true, log) + None + } //dependsOn(compile) describedAs("Creates the OSGi distribution.") } class AkkaOSGiParentProject(info: ProjectInfo) extends ParentProject(info) { lazy val akka_osgi_bundle = project("akka-osgi-bundle", "akka-osgi-bundle", new AkkaOSGiBundleProject(_), akka_kernel) lazy val akka_osgi_dependencies_bundle = project("akka-osgi-dependencies-bundle", "akka-osgi-dependencies-bundle", - new AkkaOSGiDependenciesBundleProject(_), akka_kernel) + new AkkaOSGiDependenciesBundleProject(_), akka_osgi_bundle) lazy val akka_osgi_assembly = project("akka-osgi-assembly", "akka-osgi-assembly", - new AkkaOSGiAssemblyProject(_), akka_osgi_bundle) + new AkkaOSGiAssemblyProject(_), akka_osgi_bundle, akka_osgi_dependencies_bundle) } // ================= TEST ================== From d0fe535a67eb402e98fab23e1557942cd1f78393 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Tue, 8 Jun 2010 10:36:41 +0200 Subject: [PATCH 11/33] Use more idiomatic way to add the assembly task --- project/build/AkkaProject.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 706e0573ac..98441a44e1 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -323,15 +323,17 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { class AkkaOSGiAssemblyProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { // FIXME: Find out how to replace mvn-assembly within SBT - override def packageAction = task { + lazy val assemblyAction = task { println("----------------------------") println("2" + unmanagedClasspath) - //FileUtilities.copy(info.dependencies.map(_.outputPath), "bundles", true, true, log) None - } //dependsOn(compile) describedAs("Creates the OSGi distribution.") + } + + override def packageAction = super.packageAction dependsOn(assemblyAction) + } class AkkaOSGiParentProject(info: ProjectInfo) extends ParentProject(info) { From 2c5fd110e5fdc01d51de455ccda18374eb9f003a Mon Sep 17 00:00:00 2001 From: Andreas Kollegger Date: Tue, 8 Jun 2010 22:57:55 -0400 Subject: [PATCH 12/33] pulled wrappers into AkkaWrapperProject trait file; sjson, objenesis, dispatch-json, netty ok; multiverse is next --- .../src/main/resources/features.xml | 15 ++++ project/build/AkkaProject.scala | 52 +----------- project/build/AkkaWrappersProject.scala | 81 +++++++++++++++++++ 3 files changed, 97 insertions(+), 51 deletions(-) create mode 100644 project/build/AkkaWrappersProject.scala diff --git a/akka-karaf/akka-features/src/main/resources/features.xml b/akka-karaf/akka-features/src/main/resources/features.xml index 9176437f46..7506820024 100644 --- a/akka-karaf/akka-features/src/main/resources/features.xml +++ b/akka-karaf/akka-features/src/main/resources/features.xml @@ -1,7 +1,22 @@ + + + mvn:com.weiglewilczek.scala-lang-osgi/scala-library/2.8.0.RC2 + mvn:org.eclipse.scalamodules/scalamodules-core/2.0-M2 + + + + + mvn:se.scalablesolutions.akka.akka-wrap/dispatch-json_2.8.0.RC3_osgi/0.7.4 + mvn:org.objenesis/objenesis/1.2 + mvn:sjson.json/sjson/0.6-SNAPSHOT + + + sjson mvn:se.scalablesolutions.akka.akka-wrap/jgroups-wrapper_2.8.0.RC3_osgi/2.9.0.GA + mvn:org.jboss.netty/netty/3.2.0.CR1 mvn:se.scalablesolutions.akka/akka-core_2.8.0.RC3_osgi/0.9 diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 2b96abb597..90e9a3124e 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -12,7 +12,7 @@ import java.io.File import com.weiglewilczek.bnd4sbt._ -class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { +class AkkaParent(info: ProjectInfo) extends DefaultProject(info) with AkkaWrappersProject { // ------------------------------------------------------------ // project versions @@ -86,9 +86,6 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // examples lazy val akka_samples = project("akka-samples", "akka-samples", new AkkaSamplesParentProject(_)) - // OSGi wrappers - lazy val akka_wrappers = project("akka-wrap", "akka-wrap", new AkkaWrappersParentProject(_)) - // ------------------------------------------------------------ // Run Akka microkernel using 'sbt run' + use for packaging executable JAR override def mainClass = Some("se.scalablesolutions.akka.kernel.Main") @@ -175,25 +172,6 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { None } dependsOn(dist) describedAs("Run mvn install for artifacts in dist.") - lazy val publishLocalMvnWrapped = runMvnInstallWrapped - def runMvnInstallWrapped = task { - for (absPath <- wrappedArtifacts.getPaths) { - val artifactRE = """.*/([^/]+)-([^-]+).jar""".r - val artifactRE(artifactId, artifactVersion) = absPath - val command = "mvn install:install-file" + - " -Dfile=" + absPath + - " -DgroupId=se.scalablesolutions.akka.akka-wrap" + - " -DartifactId=" + artifactId + - " -Dversion=" + artifactVersion + - " -Dpackaging=jar -DgeneratePom=true" - command ! log - } - None - } dependsOn(`package`) describedAs("Run mvn install for wrapped artifacts in akka-wrap.") - - - - // ------------------------------------------------------------ // subprojects class AkkaCoreProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { @@ -384,23 +362,6 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { new AkkaSampleRemoteProject(_), akka_kernel) } - - // ================= OSGi Wrappers ================== - class JgroupsWrapperProject(info: ProjectInfo) extends OSGiWrapperProject(info) { - override def wrappedVersion = "2.9.0.GA" - override def bndImportPackage = Set("org.testng.*;resolution:=optional", - "org.bouncycastle.jce.provider;resolution:=optional", - "bsh;resolution:=optional", - "*") - - val jgroups = "jgroups" % "jgroups" % wrappedVersion % "compile" - } - - class AkkaWrappersParentProject(info: ProjectInfo) extends ParentProject(info) { - lazy val jgroups_wrapper = project("jgroups-wrapper", "jgroups-wrapper", - new JgroupsWrapperProject(_)) - } - // ------------------------------------------------------------ // helper functions def removeDupEntries(paths: PathFinder) = @@ -429,8 +390,6 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { def akkaArtifacts = descendents(info.projectPath / "dist", "*" + buildScalaVersion + "_osgi-" + version + ".jar") - def wrappedArtifacts = descendents(info.projectPath / "akka-wrap", "*" + buildScalaVersion + "_osgi-" + "*.jar") - // ------------------------------------------------------------ class AkkaDefaultProject(info: ProjectInfo, val deployPath: Path) extends DefaultProject(info) with DeployProject with OSGiProject @@ -460,13 +419,4 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { } - abstract class OSGiWrapperProject(info: ProjectInfo) extends DefaultProject(info) with BNDPlugin { - def wrappedVersion:String - override def version = OpaqueVersion(wrappedVersion) - override def artifactID = moduleID + "_osgi" - override def bndEmbedDependencies = true - override def bndExportPackage = Set("*") - } - - } diff --git a/project/build/AkkaWrappersProject.scala b/project/build/AkkaWrappersProject.scala new file mode 100644 index 0000000000..5826ee4d55 --- /dev/null +++ b/project/build/AkkaWrappersProject.scala @@ -0,0 +1,81 @@ + /*---------------------------------------------------------------------------\ +| Copyright (C) 2009-2010 Scalable Solutions AB | +\---------------------------------------------------------------------------*/ + +import sbt._ +import sbt.CompileOrder._ +import spde._ + +import java.util.jar.Attributes +import java.util.jar.Attributes.Name._ +import java.io.File + +import com.weiglewilczek.bnd4sbt._ + +trait AkkaWrappersProject extends DefaultProject { + + // ------------------------------------------------------------ + // project versions + val JERSEY_VERSION:String + val ATMO_VERSION:String + val CASSANDRA_VERSION:String + val LIFT_VERSION:String + val SCALATEST_VERSION:String + val MULTIVERSE_VERSION:String + + // OSGi wrappers + lazy val akka_wrappers = project("akka-wrap", "akka-wrap", new AkkaWrappersParentProject(_)) + + import Process._ + lazy val publishLocalMvnWrapped = runMvnInstallWrapped + def runMvnInstallWrapped = task { + for (absPath <- wrappedArtifacts.getPaths) { + val artifactRE = """.*/([^/]+)-([^-]+).jar""".r + val artifactRE(artifactId, artifactVersion) = absPath + val command = "mvn install:install-file" + + " -Dfile=" + absPath + + " -DgroupId=se.scalablesolutions.akka.akka-wrap" + + " -DartifactId=" + artifactId + + " -Dversion=" + artifactVersion + + " -Dpackaging=jar -DgeneratePom=true" + command ! log + } + None + } dependsOn(`package`) describedAs("Run mvn install for wrapped artifacts in akka-wrap.") + + // ================= OSGi Wrappers ================== + class JgroupsWrapperProject(info: ProjectInfo) extends OSGiWrapperProject(info) { + override def wrappedVersion = "2.9.0.GA" + override def bndImportPackage = Set("org.testng.*;resolution:=optional", + "org.bouncycastle.jce.provider;resolution:=optional", + "bsh;resolution:=optional", + "*") + + val jgroups = "jgroups" % "jgroups" % wrappedVersion % "compile" + } + + class DispatchJsonWrapperProject(info: ProjectInfo) extends OSGiWrapperProject(info) { + override def wrappedVersion = "0.7.4" + + val dispatch_json = "net.databinder" % "dispatch-json_2.8.0.RC3" % wrappedVersion % "compile" + } + + class AkkaWrappersParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val jgroups_wrapper = project("jgroups-wrapper", "jgroups-wrapper", + new JgroupsWrapperProject(_)) + lazy val dispath_json = project("dispatch-json", "dispatch-json", + new DispatchJsonWrapperProject(_)) + } + + def wrappedArtifacts = descendents(info.projectPath / "akka-wrap", "*" + buildScalaVersion + "_osgi-" + "*.jar") + + abstract class OSGiWrapperProject(info: ProjectInfo) extends DefaultProject(info) with BNDPlugin { + def wrappedVersion:String + override def version = OpaqueVersion(wrappedVersion) + override def artifactID = moduleID + "_osgi" + override def bndEmbedDependencies = true + override def bndExportPackage = Set("*") + } + + +} From c325a74f5c41e54d1ed622a5447a7154867bd942 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Wed, 16 Jun 2010 00:50:52 +0200 Subject: [PATCH 13/33] Basic OSGi stuff working. Need to exclude transitive dependencies from the bundle list. --- project/build/AkkaProject.scala | 39 +++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 98441a44e1..6f5002d543 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -311,29 +311,46 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { "!se.scalablesolutions.akka.*", // Provided by other bundles + "!com.google.inject.*", + "!javax.transaction.*", + "!javax.ws.rs.*", + "!net.liftweb.common.*", + "!net.liftweb.util.*", + "!org.apache.camel.*", "!org.apache.commons.io.*", + "!org.apache.commons.pool.*", "!org.codehaus.jackson.*", - "!org.codehaus.jettison.*", "!org.jboss.netty.*", + "!org.springframework.*", // Export the rest "*") } - class AkkaOSGiAssemblyProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { - // FIXME: Find out how to replace mvn-assembly within SBT + class AkkaOSGiAssemblyProject(info: ProjectInfo) extends DefaultProject(info) { - lazy val assemblyAction = task { - - println("----------------------------") - println("2" + unmanagedClasspath) + // FIXME: Transitive dependencies should not be included, we should list every bundle individually - //FileUtilities.copy(info.dependencies.map(_.outputPath), "bundles", true, true, log) + val guicey = "org.guiceyfruit" % "guice-all" % "2.0" % "compile" + val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" + val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" + val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" + val lift_common = "net.liftweb" % "lift-common" % LIFT_VERSION % "compile" + val lift_util = "net.liftweb" % "lift-util" % LIFT_VERSION % "compile" + val camel_core = "org.apache.camel" % "camel-core" % "2.3.0" % "compile" + val commons_pool = "commons-pool" % "commons-pool" % "1.5.4" % "compile" + val jackson = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" + val netty = "org.jboss.netty" % "netty" % "3.2.0.CR1" % "compile" + val spring_beans = "org.springframework" % "spring-beans" % "3.0.1.RELEASE" % "compile" + val spring_context = "org.springframework" % "spring-context" % "3.0.1.RELEASE" % "compile" + + override def packageAction = task { + val libs: Seq[Path] = managedClasspath(config("compile")).get.toSeq + val prjs: Seq[Path] = info.dependencies.toSeq.asInstanceOf[Seq[DefaultProject]].map(_.jarPath) + val all = libs ++ prjs + FileUtilities.copyFlat(all, outputPath / "bundles", log) None } - - override def packageAction = super.packageAction dependsOn(assemblyAction) - } class AkkaOSGiParentProject(info: ProjectInfo) extends ParentProject(info) { From 7a6465cd82bbdcdc47704a1c4a4671b5b0e2b7a9 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Wed, 16 Jun 2010 11:55:25 +0200 Subject: [PATCH 14/33] Updated bnd4sbt plugin --- project/plugins/Plugins.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins/Plugins.scala b/project/plugins/Plugins.scala index 895df56970..a133f55757 100644 --- a/project/plugins/Plugins.scala +++ b/project/plugins/Plugins.scala @@ -6,5 +6,5 @@ class Plugins(info: ProjectInfo) extends PluginDefinition(info) { // val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/" // val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT" - lazy val bnd4sbt = "com.weiglewilczek" % "bnd4sbt" % "0.4" + lazy val bnd4sbt = "com.weiglewilczek.bnd4sbt" % "bnd4sbt" % "1.0.0.RC1" } From f0b440453d5909e7fe4a1ee07f8a960b14f01b9e Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Wed, 16 Jun 2010 11:55:57 +0200 Subject: [PATCH 15/33] Exclude transitive dependencies Ongoing work on finding the bundle list --- project/build/AkkaProject.scala | 50 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 3eba7ef4a5..3ddc872cc6 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -67,6 +67,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", javaNetRepo) // val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", sonatypeSnapshotRepo) val liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsSnapshots) + val scalaBundleConfig = ModuleConfiguration("com.weiglewilczek.scala-lang-osgi", ScalaToolsReleases) // ------------------------------------------------------------ // project defintions @@ -308,42 +309,51 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { override def bndPrivatePackage = Set("") override def bndImportPackage = Set("*;resolution:=optional") override def bndExportPackage = Set( - // Provided by Akka bundle - "!se.scalablesolutions.akka.*", + "org.aopalliance.*;version=1.0.0", // Provided by other bundles + "!se.scalablesolutions.akka.*", "!com.google.inject.*", "!javax.transaction.*", "!javax.ws.rs.*", - "!net.liftweb.common.*", - "!net.liftweb.util.*", - "!org.apache.camel.*", "!org.apache.commons.io.*", "!org.apache.commons.pool.*", "!org.codehaus.jackson.*", "!org.jboss.netty.*", "!org.springframework.*", + "!org.apache.camel.*", + "!org.fusesource.commons.management.*", - // Export the rest - "*") + "*;version=0.0.0") } class AkkaOSGiAssemblyProject(info: ProjectInfo) extends DefaultProject(info) { - // FIXME: Transitive dependencies should not be included, we should list every bundle individually + // Scala bundle + val scala_bundle = "com.weiglewilczek.scala-lang-osgi" % "scala-library_2.8.0.RC3" % "1.0" % "compile" intransitive() - val guicey = "org.guiceyfruit" % "guice-all" % "2.0" % "compile" - val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" - val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" - val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" - val lift_common = "net.liftweb" % "lift-common" % LIFT_VERSION % "compile" - val lift_util = "net.liftweb" % "lift-util" % LIFT_VERSION % "compile" - val camel_core = "org.apache.camel" % "camel-core" % "2.3.0" % "compile" - val commons_pool = "commons-pool" % "commons-pool" % "1.5.4" % "compile" - val jackson = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" - val netty = "org.jboss.netty" % "netty" % "3.2.0.CR1" % "compile" - val spring_beans = "org.springframework" % "spring-beans" % "3.0.1.RELEASE" % "compile" - val spring_context = "org.springframework" % "spring-context" % "3.0.1.RELEASE" % "compile" + // Camel bundles + val camel_core = "org.apache.camel" % "camel-core" % "2.3.0" % "compile" intransitive() + val fusesource_commonman = "org.fusesource.commonman" % "commons-management" % "1.0" intransitive() + + // Spring bundles + val spring_beans = "org.springframework" % "spring-beans" % "3.0.1.RELEASE" % "compile" intransitive() + val spring_context = "org.springframework" % "spring-context" % "3.0.1.RELEASE" % "compile" intransitive() + val spring_jms = "org.springframework" % "spring-jms" % "3.0.1.RELEASE" % "compile" intransitive() + val spring_aop = "org.springframework" % "spring-aop" % "3.0.1.RELEASE" % "compile" intransitive() + val spring_asm = "org.springframework" % "spring-asm" % "3.0.1.RELEASE" % "compile" intransitive() + val spring_core = "org.springframework" % "spring-core" % "3.0.1.RELEASE" % "compile" intransitive() + val spring_expression = "org.springframework" % "spring-expression" % "3.0.1.RELEASE" % "compile" intransitive() + val spring_tx = "org.springframework" % "spring-tx" % "3.0.1.RELEASE" % "compile" intransitive() + + val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" intransitive() + val commons_pool = "commons-pool" % "commons-pool" % "1.5.4" % "compile" intransitive() + val jackson_core = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" intransitive() + val jackson = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" intransitive() + val netty = "org.jboss.netty" % "netty" % "3.2.0.CR1" % "compile" intransitive() + val guicey = "org.guiceyfruit" % "guice-all" % "2.0" % "compile" intransitive() + val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" intransitive() + val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" intransitive() override def packageAction = task { val libs: Seq[Path] = managedClasspath(config("compile")).get.toSeq From bfbdc7ae4bf0d5e836684df16d9c89b3d52cf2f6 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Thu, 17 Jun 2010 14:19:33 +0200 Subject: [PATCH 16/33] All bundles resolve! --- project/build/AkkaProject.scala | 42 +++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 3ddc872cc6..1c7170a078 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -300,22 +300,25 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ================= OSGi Packaging ================== class AkkaOSGiBundleProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { override def bndClasspath = compileClasspath - override def bndPrivatePackage = Set("") - override def bndExportPackage = Set("se.scalablesolutions.akka.*;version=0.9") + override def bndPrivatePackage = Seq("") + override def bndExportPackage = Seq("se.scalablesolutions.akka.*;version=0.9") } class AkkaOSGiDependenciesBundleProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { override def bndClasspath = compileClasspath - override def bndPrivatePackage = Set("") - override def bndImportPackage = Set("*;resolution:=optional") - override def bndExportPackage = Set( + override def bndPrivatePackage = Seq("") + override def bndImportPackage = Seq("*;resolution:=optional") + override def bndExportPackage = Seq( "org.aopalliance.*;version=1.0.0", // Provided by other bundles "!se.scalablesolutions.akka.*", + "!net.liftweb.*", "!com.google.inject.*", "!javax.transaction.*", "!javax.ws.rs.*", + "!javax.jms.*", + "!javax.transaction,*", "!org.apache.commons.io.*", "!org.apache.commons.pool.*", "!org.codehaus.jackson.*", @@ -332,27 +335,40 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // Scala bundle val scala_bundle = "com.weiglewilczek.scala-lang-osgi" % "scala-library_2.8.0.RC3" % "1.0" % "compile" intransitive() + // Lift bundles + val lift_actor = "net.liftweb" % "lift-actor" % LIFT_VERSION % "compile" intransitive() + val lift_common = "net.liftweb" % "lift-common" % LIFT_VERSION % "compile" intransitive() + val lift_json = "net.liftweb" % "lift-json" % LIFT_VERSION % "compile" intransitive() + val lift_util = "net.liftweb" % "lift-util" % LIFT_VERSION % "compile" intransitive() + //val lift_webkit = "net.liftweb" % "lift-webkit" % LIFT_VERSION % "compile" intransitive() + //val lift_testkit = "net.liftweb" % "lift-testkit" % LIFT_VERSION % "compile" intransitive() + // Camel bundles val camel_core = "org.apache.camel" % "camel-core" % "2.3.0" % "compile" intransitive() val fusesource_commonman = "org.fusesource.commonman" % "commons-management" % "1.0" intransitive() // Spring bundles - val spring_beans = "org.springframework" % "spring-beans" % "3.0.1.RELEASE" % "compile" intransitive() - val spring_context = "org.springframework" % "spring-context" % "3.0.1.RELEASE" % "compile" intransitive() - val spring_jms = "org.springframework" % "spring-jms" % "3.0.1.RELEASE" % "compile" intransitive() - val spring_aop = "org.springframework" % "spring-aop" % "3.0.1.RELEASE" % "compile" intransitive() - val spring_asm = "org.springframework" % "spring-asm" % "3.0.1.RELEASE" % "compile" intransitive() - val spring_core = "org.springframework" % "spring-core" % "3.0.1.RELEASE" % "compile" intransitive() - val spring_expression = "org.springframework" % "spring-expression" % "3.0.1.RELEASE" % "compile" intransitive() - val spring_tx = "org.springframework" % "spring-tx" % "3.0.1.RELEASE" % "compile" intransitive() + val spring_beans = "org.springframework" % "spring-beans" % "3.0.2.RELEASE" % "compile" intransitive() + val spring_context = "org.springframework" % "spring-context" % "3.0.2.RELEASE" % "compile" intransitive() + val spring_jms = "org.springframework" % "spring-jms" % "3.0.2.RELEASE" % "compile" intransitive() + val spring_aop = "org.springframework" % "spring-aop" % "3.0.2.RELEASE" % "compile" intransitive() + val spring_asm = "org.springframework" % "spring-asm" % "3.0.2.RELEASE" % "compile" intransitive() + val spring_core = "org.springframework" % "spring-core" % "3.0.2.RELEASE" % "compile" intransitive() + val spring_expression = "org.springframework" % "spring-expression" % "3.0.2.RELEASE" % "compile" intransitive() + val spring_tx = "org.springframework" % "spring-tx" % "3.0.2.RELEASE" % "compile" intransitive() val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" intransitive() val commons_pool = "commons-pool" % "commons-pool" % "1.5.4" % "compile" intransitive() + val commons_codec = "commons-codec" % "commons-codec" % "1.4" % "compile" intransitive() + val commons_fileupload = "commons-fileupload" % "commons-fileupload" % "1.2.1" % "compile" intransitive() val jackson_core = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" intransitive() val jackson = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" intransitive() val netty = "org.jboss.netty" % "netty" % "3.2.0.CR1" % "compile" intransitive() val guicey = "org.guiceyfruit" % "guice-all" % "2.0" % "compile" intransitive() + val joda = "joda-time" % "joda-time" % "1.6" intransitive() + val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" intransitive() + val jms_1_1 = "org.apache.geronimo.specs" % "geronimo-jms_1.1_spec" % "1.1.1" % "compile" intransitive() val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" intransitive() override def packageAction = task { From 157956eeebdc60fcbc3d42679ef73acb055814bb Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Thu, 17 Jun 2010 15:10:39 +0200 Subject: [PATCH 17/33] Started work on OSGi sample --- .../src/main/scala/Activator.scala | 26 +++++++++++++++++++ .../src/main/scala/Actor.scala | 12 +++++++++ project/build/AkkaProject.scala | 12 +++++++++ 3 files changed, 50 insertions(+) create mode 100644 akka-samples/akka-sample-osgi/src/main/scala/Activator.scala create mode 100644 akka-samples/akka-sample-osgi/src/main/scala/Actor.scala diff --git a/akka-samples/akka-sample-osgi/src/main/scala/Activator.scala b/akka-samples/akka-sample-osgi/src/main/scala/Activator.scala new file mode 100644 index 0000000000..81e2d25d97 --- /dev/null +++ b/akka-samples/akka-sample-osgi/src/main/scala/Activator.scala @@ -0,0 +1,26 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package sample.osgi + +import org.osgi.framework.{BundleActivator, BundleContext} + + +class Activator extends BundleActivator { + + def start(context: BundleContext) { + println("Start") + val osgiActor = new OSGiActor + osgiActor ! "Hello" + + + + } + + def stop(context: BundleContext) { + println("stop") + } + +} + diff --git a/akka-samples/akka-sample-osgi/src/main/scala/Actor.scala b/akka-samples/akka-sample-osgi/src/main/scala/Actor.scala new file mode 100644 index 0000000000..90bd521d7b --- /dev/null +++ b/akka-samples/akka-sample-osgi/src/main/scala/Actor.scala @@ -0,0 +1,12 @@ +package sample.osgi + +import se.scalablesolutions.akka.actor.Actor + + +class OSGiActor extends Actor { + def receive = { + case msg: String => + println("Got message: " + msg) + } +} + diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 1c7170a078..3742089c55 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -436,6 +436,16 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val commons_codec = "commons-codec" % "commons-codec" % "1.3" % "compile" } + class AkkaSampleOSGiProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { + override def bndClasspath = compileClasspath + + val osgi_core = "org.osgi" % "org.osgi.core" % "4.2.0" + + override def bndBundleActivator = Some("sample.osgi.Activator") + override def bndPrivatePackage = Nil + override def bndExportPackage = Seq("sample.osgi.*;version=0.9") + } + class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) { lazy val akka_sample_ants = project("akka-sample-ants", "akka-sample-ants", new AkkaSampleAntsProject(_), akka_core) @@ -455,6 +465,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { new AkkaSampleSecurityProject(_), akka_kernel) lazy val akka_sample_remote = project("akka-sample-remote", "akka-sample-remote", new AkkaSampleRemoteProject(_), akka_kernel) + lazy val akka_sample_osgi = project("akka-sample-osgi", "akka-sample-osgi", + new AkkaSampleOSGiProject(_), akka_core) } // ------------------------------------------------------------ From 3c3d9a4440b7d28ebf288c9f33cb92e354dc96df Mon Sep 17 00:00:00 2001 From: Andreas Kollegger Date: Thu, 17 Jun 2010 12:12:42 -0400 Subject: [PATCH 18/33] synced with jboner/master; decoupled AkkaWrapperProject; bumped bnd4sbt to 1.0.0.RC2 --- project/build/AkkaProject.scala | 9 +++++---- project/build/AkkaWrappersProject.scala | 13 ++++++++++--- project/plugins/Plugins.scala | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 0745dcb08a..ca0da80ee3 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -12,7 +12,7 @@ import java.io.File import com.weiglewilczek.bnd4sbt._ -class AkkaParent(info: ProjectInfo) extends DefaultProject(info) with AkkaWrappersProject { +class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------ // project versions @@ -22,6 +22,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) with AkkaWrappe val LIFT_VERSION = "2.0-scala280-SNAPSHOT" val SCALATEST_VERSION = "1.2-for-scala-2.8.0.RC3-SNAPSHOT" val MULTIVERSE_VERSION = "0.5.2" + val COMMONS_CODEC_VERSION = "1.4" // ------------------------------------------------------------ lazy val deployPath = info.projectPath / "deploy" @@ -243,7 +244,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) with AkkaWrappe class AkkaRedisProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { val redis = "com.redis" % "redisclient" % "2.8.0.RC3-1.4-SNAPSHOT" % "compile" - val commons_codec = "commons-codec" % "commons-codec" % "1.4" % "compile" + val commons_codec = "commons-codec" % "commons-codec" % COMMONS_CODEC_VERSION % "compile" override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil } @@ -339,7 +340,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) with AkkaWrappe class AkkaSampleSecurityProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) { val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1.1" % "compile" val jsr250 = "javax.annotation" % "jsr250-api" % "1.0" % "compile" - val commons_codec = "commons-codec" % "commons-codec" % "1.3" % "compile" + val commons_codec = "commons-codec" % "commons-codec" % COMMONS_CODEC_VERSION % "compile" } class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) { @@ -416,7 +417,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) with AkkaWrappe trait OSGiProject extends DefaultProject with BNDPlugin { override def artifactID = moduleID + "_osgi" - override def bndExportPackage = Set("*") + override def bndExportPackage = Seq("se.scalablesolutions.akka.*;version=0.9") } diff --git a/project/build/AkkaWrappersProject.scala b/project/build/AkkaWrappersProject.scala index 5826ee4d55..f92e91d191 100644 --- a/project/build/AkkaWrappersProject.scala +++ b/project/build/AkkaWrappersProject.scala @@ -46,7 +46,7 @@ trait AkkaWrappersProject extends DefaultProject { // ================= OSGi Wrappers ================== class JgroupsWrapperProject(info: ProjectInfo) extends OSGiWrapperProject(info) { override def wrappedVersion = "2.9.0.GA" - override def bndImportPackage = Set("org.testng.*;resolution:=optional", + override def bndImportPackage = Seq("org.testng.*;resolution:=optional", "org.bouncycastle.jce.provider;resolution:=optional", "bsh;resolution:=optional", "*") @@ -60,11 +60,19 @@ trait AkkaWrappersProject extends DefaultProject { val dispatch_json = "net.databinder" % "dispatch-json_2.8.0.RC3" % wrappedVersion % "compile" } + class MultiverseWrapperProject(info: ProjectInfo) extends OSGiWrapperProject(info) { + override def wrappedVersion = "0.5.2" + + val multiverse = "org.multiverse" % "multiverse-alpha" % wrappedVersion % "compile" + } + class AkkaWrappersParentProject(info: ProjectInfo) extends ParentProject(info) { lazy val jgroups_wrapper = project("jgroups-wrapper", "jgroups-wrapper", new JgroupsWrapperProject(_)) lazy val dispath_json = project("dispatch-json", "dispatch-json", new DispatchJsonWrapperProject(_)) + lazy val multiverse_wrapper = project("multiverse-wrapper", "multiverse-wrapper", + new MultiverseWrapperProject(_)) } def wrappedArtifacts = descendents(info.projectPath / "akka-wrap", "*" + buildScalaVersion + "_osgi-" + "*.jar") @@ -73,8 +81,7 @@ trait AkkaWrappersProject extends DefaultProject { def wrappedVersion:String override def version = OpaqueVersion(wrappedVersion) override def artifactID = moduleID + "_osgi" - override def bndEmbedDependencies = true - override def bndExportPackage = Set("*") + override def bndExportPackage = Seq("*") } diff --git a/project/plugins/Plugins.scala b/project/plugins/Plugins.scala index 997b1e8e67..ac9ff64e25 100644 --- a/project/plugins/Plugins.scala +++ b/project/plugins/Plugins.scala @@ -5,5 +5,5 @@ class Plugins(info: ProjectInfo) extends PluginDefinition(info) { val spdeSbt = "us.technically.spde" % "spde-sbt-plugin" % "0.4.1" // val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/" // val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT" - val bnd4sbt = "com.weiglewilczek" % "bnd4sbt" % "0.4" + val bnd4sbt = "com.weiglewilczek.bnd4sbt" % "bnd4sbt" % "1.0.0.RC2" } From 54b48a3d6d03b76f7e9872d5f70fb8fc23fd14bc Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Mon, 21 Jun 2010 18:40:15 +0200 Subject: [PATCH 19/33] OSGi work: Switched to bnd4sbt 1.0.0.RC3, using projectVersion for exported packages now and fixed a merge bug. --- project/build/AkkaProject.scala | 10 ++-------- project/plugins/Plugins.scala | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index d4f563f1d5..31bd9e9245 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -21,7 +21,6 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val CASSANDRA_VERSION = "0.6.1" val LIFT_VERSION = "2.0-scala280-SNAPSHOT" val SCALATEST_VERSION = "1.2-for-scala-2.8.0.RC3-SNAPSHOT" - val MULTIVERSE_VERSION = "0.5.2" val COMMONS_CODEC_VERSION = "1.4" val MULTIVERSE_VERSION = "0.6-SNAPSHOT" @@ -69,12 +68,9 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", javaNetRepo) // val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", sonatypeSnapshotRepo) val liftModuleConfig = ModuleConfiguration("net.liftweb", ScalaToolsSnapshots) -<<<<<<< HEAD - val scalaBundleConfig = ModuleConfiguration("com.weiglewilczek.scala-lang-osgi", ScalaToolsReleases) -======= +// val scalaBundleConfig = ModuleConfiguration("com.weiglewilczek.scala-lang-osgi", ScalaToolsReleases) def codehausSnapshotRepo = "Codehaus Snapshots" at "http://snapshots.repository.codehaus.org" val multiverseModuleConfig = ModuleConfiguration("org.multiverse", codehausSnapshotRepo) ->>>>>>> master // ------------------------------------------------------------ // project defintions @@ -528,8 +524,6 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { trait OSGiProject extends DefaultProject with BNDPlugin { override def artifactID = moduleID + "_osgi" - override def bndExportPackage = Seq("se.scalablesolutions.akka.*;version=0.9") - + override def bndExportPackage = Seq("se.scalablesolutions.akka.*;version=%s".format(projectVersion.value)) } - } diff --git a/project/plugins/Plugins.scala b/project/plugins/Plugins.scala index bee2aec2f2..1efcad620c 100644 --- a/project/plugins/Plugins.scala +++ b/project/plugins/Plugins.scala @@ -6,5 +6,5 @@ class Plugins(info: ProjectInfo) extends PluginDefinition(info) { // val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/" // val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT" - val bnd4sbt = "com.weiglewilczek.bnd4sbt" % "bnd4sbt" % "1.0.0.RC2" + val bnd4sbt = "com.weiglewilczek.bnd4sbt" % "bnd4sbt" % "1.0.0.RC3" } From dbb12da4f7f1528f7141545aa41125b8a3849a55 Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Mon, 21 Jun 2010 22:02:15 +0200 Subject: [PATCH 20/33] OSGi work: Fixed packageAction for AkkaOSGiAssemblyProject (publish-local working now) and reverted to default artifactID (removed superfluous suffix '_osgi'). --- project/build/AkkaProject.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 31bd9e9245..600f075a5f 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -370,12 +370,12 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { val jms_1_1 = "org.apache.geronimo.specs" % "geronimo-jms_1.1_spec" % "1.1.1" % "compile" intransitive() val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" intransitive() - override def packageAction = task { + override def packageAction = { val libs: Seq[Path] = managedClasspath(config("compile")).get.toSeq val prjs: Seq[Path] = info.dependencies.toSeq.asInstanceOf[Seq[DefaultProject]].map(_.jarPath) val all = libs ++ prjs FileUtilities.copyFlat(all, outputPath / "bundles", log) - None + super.packageAction } } @@ -523,7 +523,6 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) { } trait OSGiProject extends DefaultProject with BNDPlugin { - override def artifactID = moduleID + "_osgi" override def bndExportPackage = Seq("se.scalablesolutions.akka.*;version=%s".format(projectVersion.value)) } } From 38fc975c0a0a3112314fee67e0b4947256594921 Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Mon, 21 Jun 2010 23:29:09 +0200 Subject: [PATCH 21/33] OSGi work: Fixed plugin configuration => Added missing repo and module config for BND. --- project/plugins/Plugins.scala | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/project/plugins/Plugins.scala b/project/plugins/Plugins.scala index 1efcad620c..3abc87270f 100644 --- a/project/plugins/Plugins.scala +++ b/project/plugins/Plugins.scala @@ -1,10 +1,15 @@ import sbt._ class Plugins(info: ProjectInfo) extends PluginDefinition(info) { - val databinderRepo = "Databinder Repository" at "http://databinder.net/repo" - val spdeSbt = "us.technically.spde" % "spde-sbt-plugin" % "0.4.1" // val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/" // val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT" - val bnd4sbt = "com.weiglewilczek.bnd4sbt" % "bnd4sbt" % "1.0.0.RC3" + // Repositories: Using module configs to speed up resolution => Repos must be defs! + def aquteRepo = "aQute Maven Repository" at "http://www.aqute.biz/repo" + lazy val aquteModuleConfig = ModuleConfiguration("biz.aQute", aquteRepo) + def databinderRepo = "Databinder Repository" at "http://databinder.net/repo" + lazy val spdeModuleConfig = ModuleConfiguration("us.technically.spde", databinderRepo) + + val bnd4sbt = "com.weiglewilczek.bnd4sbt" % "bnd4sbt" % "1.0.0.RC3" + val spdeSbt = "us.technically.spde" % "spde-sbt-plugin" % "0.4.1" } From 083226517be318ccded7d728be32a9e032f71fc0 Mon Sep 17 00:00:00 2001 From: Roman Roelofsen Date: Mon, 28 Jun 2010 12:21:56 +0200 Subject: [PATCH 22/33] Removed pom.xml, not needed anymore --- pom.xml | 566 -------------------------------------------------------- 1 file changed, 566 deletions(-) delete mode 100644 pom.xml diff --git a/pom.xml b/pom.xml deleted file mode 100644 index ea7edf729e..0000000000 --- a/pom.xml +++ /dev/null @@ -1,566 +0,0 @@ - - - 4.0.0 - - Akka Project - akka - se.scalablesolutions.akka - 0.7-SNAPSHOT - 2009 - http://akkasource.org - pom - - - Akka implements a unique hybrid of: - * Actors , which gives you: - * Simple and high-level abstractions for concurrency and parallelism. - * Asynchronous, non-blocking and highly performant event-driven programming model. - * Very lightweight event-driven processes (create ~6.5 million actors on 4 G RAM). - * Supervision hierarchies with let-it-crash semantics. For writing highly fault-tolerant systems that never stop, systems that self-heal. - * Software Transactional Memory (STM). (Distributed transactions coming soon). - * Transactors: combine actors and STM into transactional actors. Allows you to compose atomic message flows with automatic rollback and retry. - * Remoting: highly performant distributed actors with remote supervision and error management. - * Cluster membership management. - - Akka also has a set of add-on modules: - * Persistence: A set of pluggable back-end storage modules that works in sync with the STM. - * Cassandra distributed and highly scalable database. - * MongoDB document database. - * Redis data structures database (upcoming) - * REST (JAX-RS): Expose actors as REST services. - * Comet: Expose actors as Comet services. - * Security: Digest and Kerberos based security. - * Microkernel: Run Akka as a stand-alone kernel. - - - - 2.7.7 - UTF-8 - 1.5 - ${maven.compiler.source} - ${project.build.sourceEncoding} - ${project.build.sourceEncoding} - 0.5.2 - 1.1.5 - 1.9.18-i - - - - akka-util-java - akka-util - akka-cluster - akka-core - akka-persistence - akka-rest - akka-comet - akka-amqp - akka-security - akka-patterns - akka-kernel - akka-osgi - akka-fun-test-java - akka-samples - - - - Scalable Solutions AB - http://scalablesolutions.se - - - - - The Apache License, ASL Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 - - - - - - jboner - Jonas Bonér - +1 - jonas [REMOVE] AT jonasboner DOT com - - Founder - Hacker - Despot - - - - viktorklang - Viktor Klang - +1 - viktor.klang [REMOVE] AT gmail DOT com - - Apostle - - - - - - scm:git:git://github.com/jboner/akka.git - scm:git:git@github.com:jboner/akka.git - http://github.com/jboner/akka - - - - assembla - http://assembla.com/spaces/akka/ - - - - hudson - http://hudson.scala-tools.org/job/akka/ - - - - - - - - - User and Developer Discussion List - http://groups.google.com/group/akka-user - akka-user@googlegroups.com - akka-user+subscribe@googlegroups.com - akka-user+unsubscribe@googlegroups.com - - - - - - project.embedded.module - Project Embedded Repository - file://${env.AKKA_HOME}/embedded-repo - - - repo1.maven - Maven Main Repository - http://repo1.maven.org/maven2 - - - scala-tools-snapshots - Scala-Tools Maven2 Snapshot Repository - http://scala-tools.org/repo-snapshots - - - scala-tools - Scala-Tools Maven2 Repository - http://scala-tools.org/repo-releases - - - lag - Configgy's' Repository - http://www.lag.net/repo - - - multiverse-releases - http://multiverse.googlecode.com/svn/maven-repository/releases - - false - - - - multiverse-snaphosts - http://multiverse.googlecode.com/svn/maven-repository/snapshots - - - maven2-repository.dev.java.net - Java.net Repository for Maven - http://download.java.net/maven/2 - - - java.net - Java.net Legacy Repository for Maven - http://download.java.net/maven/1 - legacy - - - guiceyfruit.release - GuiceyFruit Release Repository - http://guiceyfruit.googlecode.com/svn/repo/releases/ - - false - - - true - - - - guiceyfruit.snapshot - GuiceyFruit Snapshot Repository - http://guiceyfruit.googlecode.com/svn/repo/snapshots/ - - true - - - false - - - - guice-maven - guice maven - http://guice-maven.googlecode.com/svn/trunk - - - google-maven-repository - Google Maven Repository - http://google-maven-repository.googlecode.com/svn/repository/ - - - repository.codehaus.org - Codehaus Maven Repository - http://repository.codehaus.org - - true - - - - repository.jboss.org - JBoss Repository for Maven - http://repository.jboss.org/maven2 - - false - - - - nexus.griddynamics.net - Grid Dynamics Maven Repository - https://nexus.griddynamics.net/nexus/content/groups/public - - false - - - - databinder.net/repo/ - dbDispatch Repository for Maven - http://databinder.net/repo - - false - - - - - - - onejar-maven-plugin.googlecode.com - http://onejar-maven-plugin.googlecode.com/svn/mavenrepo - - - scala-tools.org - Scala-Tools Maven2 Repository - http://scala-tools.org/repo-releases - - - - - src/main/scala - src/test/scala - - - org.apache.maven.plugins - maven-enforcer-plugin - 1.0-beta-1 - - - enforce-akka-home - - enforce - - - - - env.AKKA_HOME - "You must have set AKKA_HOME!" - - - - ${env.AKKA_HOME}/embedded-repo - - - - true - - - - enforce-java - - enforce - - - - - 1.6.0 - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.4.2 - - - **/*Test.java - - - - **/InMemNestedStateTest.java - - - - akka.home - ${basedir}/.. - - - org.multiverse.api.exceptions.WriteConflictException.reuse - true - - - - - - org.mortbay.jetty - maven-jetty-plugin - - / - 5 - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.0.2 - - 1.5 - 1.5 - - - - org.scala-tools - maven-scala-plugin - 2.10.1 - - - - compile - testCompile - - - - - - -Xmx1024m - - - ${scala.version} - - - - true - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-changes-plugin - 2.0 - - - org.apache.maven.plugins - maven-jar-plugin - 2.2 - - - - ${project.version} - - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 1.0-beta-1 - - - org.apache.felix - maven-bundle-plugin - 2.0.0 - true - - - J2SE-1.5 - <_versionpolicy>[$(@),$(version;=+;$(@))) - - - - - create-bundle - package - - bundle - - - - bundle-install - install - - install - - - - - - - - - - - org.codehaus.mojo - taglist-maven-plugin - 2.3 - - - FIXME - TODO - XXX - @fixme - @todo - @deprecated - - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.1.2 - - - - cim - dependencies - dependency-convergence - - index - issue-tracking - license - mailing-list - - plugins - project-team - scm - summary - - - - - - org.scala-tools - maven-scala-plugin - 2.12.2 - - ${project.build.sourceEncoding} - - 1.2-SNAPSHOT - ${scala.version} - - -Xmx1024m - -DpackageLinkDefs=file://${project.build.directory}/packageLinkDefs.properties - - - - - - - org.apache.maven.plugins - maven-changes-plugin - 2.1 - - - - changes-report - - - - - ${basedir}/changes.xml - - - - maven-surefire-report-plugin - - - - - report-only - - - - - - - - - release - - - scala-tools.org - http://nexus.scala-tools.org/content/repositories/releases - - - scala-tools.org - file://${user.home}/.m2/mvnsites/akka - - - - - hudson - - - hudson.scala-tools.org - file:///home/scala-tools.org/www/repo-snapshots - - - hudson.scala-tools.org - file:///home/scala-tools.org/www/repo-snapshots - false - - - hudson.scala-tools.org - file:///home/scala-tools.org/www/mvnsites-snapshots/akka - - - - - From 240ae68c7907a4f25b0f26e599c6b918edc8dbd9 Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Tue, 20 Jul 2010 12:54:33 +0200 Subject: [PATCH 23/33] Adding become to Actor --- akka-core/src/main/scala/actor/Actor.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/akka-core/src/main/scala/actor/Actor.scala b/akka-core/src/main/scala/actor/Actor.scala index 76adf9c729..244fd8fbc7 100644 --- a/akka-core/src/main/scala/actor/Actor.scala +++ b/akka-core/src/main/scala/actor/Actor.scala @@ -414,6 +414,14 @@ trait Actor extends Logging { * Is the actor able to handle the message passed in as arguments? */ def isDefinedAt(message: Any): Boolean = base.isDefinedAt(message) + + /** One of the fundamental methods of the ActorsModel + * Actor assumes a new behavior + */ + def become(behavior: Option[Receive]) { + self.hotswap = behavior + self.checkReceiveTimeout // FIXME : how to reschedule receivetimeout on hotswap? + } // ========================================= // ==== INTERNAL IMPLEMENTATION DETAILS ==== @@ -427,7 +435,7 @@ trait Actor extends Logging { } private val lifeCycles: Receive = { - case HotSwap(code) => self.hotswap = code; self.checkReceiveTimeout // FIXME : how to reschedule receivetimeout on hotswap? + case HotSwap(code) => become(code) case Exit(dead, reason) => self.handleTrapExit(dead, reason) case Link(child) => self.link(child) case Unlink(child) => self.unlink(child) From bd7bd160a0c4a0398526b4bbf129b38909fe5ca7 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Tue, 20 Jul 2010 18:48:34 +0200 Subject: [PATCH 24/33] Minor changes in akka-sample-camel --- .../src/main/scala/Actors.scala | 8 +++- .../src/main/scala/Boot.scala | 38 +++++++++---------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala index 275dc6b094..f3b50d9c6e 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Actors.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Actors.scala @@ -117,14 +117,18 @@ class HttpConsumer(producer: ActorRef) extends Actor with Consumer { def endpointUri = "jetty:http://0.0.0.0:8875/" protected def receive = { - // only keep Exchange.HTTP_PATH message header (which needed by bridge endpoint) - case msg: Message => producer forward msg.setHeaders(msg.headers(Set(Exchange.HTTP_PATH))) + case msg => producer forward msg } } class HttpProducer(transformer: ActorRef) extends Actor with Producer { def endpointUri = "jetty://http://akkasource.org/?bridgeEndpoint=true" + override protected def receiveBeforeProduce = { + // only keep Exchange.HTTP_PATH message header (which needed by bridge endpoint) + case msg: Message => msg.setHeaders(msg.headers(Set(Exchange.HTTP_PATH))) + } + override protected def receiveAfterProduce = { // do not reply but forward result to transformer case msg => transformer forward msg diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index 3894c24cca..b8a483b55f 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -16,16 +16,6 @@ import se.scalablesolutions.akka.config.ScalaConfig._ */ class Boot { - // ----------------------------------------------------------------------- - // Create CamelContext with Spring-based registry and custom route builder - // ----------------------------------------------------------------------- - - val context = new ClassPathXmlApplicationContext("/context-boot.xml", getClass) - val registry = new ApplicationContextRegistry(context) - - CamelContextManager.init(new DefaultCamelContext(registry)) - CamelContextManager.context.addRoutes(new CustomRouteBuilder) - // ----------------------------------------------------------------------- // Basic example // ----------------------------------------------------------------------- @@ -41,9 +31,17 @@ class Boot { // Supervise(actorOf[Consumer2], LifeCycle(Permanent)) :: Nil)) // ----------------------------------------------------------------------- - // Tranformer example + // Custom Camel route example // ----------------------------------------------------------------------- + // Create CamelContext and a Spring-based registry + val context = new ClassPathXmlApplicationContext("/context-boot.xml", getClass) + val registry = new ApplicationContextRegistry(context) + + // Use a custom Camel context and a custom touter builder + CamelContextManager.init(new DefaultCamelContext(registry)) + CamelContextManager.context.addRoutes(new CustomRouteBuilder) + val producer = actorOf[Producer1] val mediator = actorOf(new Transformer(producer)) val consumer = actorOf(new Consumer3(mediator)) @@ -52,12 +50,20 @@ class Boot { mediator.start consumer.start + // ----------------------------------------------------------------------- + // Non-blocking consumer-producer example (Akka homepage transformation) + // ----------------------------------------------------------------------- + + val httpTransformer = actorOf(new HttpTransformer).start + val httpProducer = actorOf(new HttpProducer(httpTransformer)).start + val httpConsumer = actorOf(new HttpConsumer(httpProducer)).start + // ----------------------------------------------------------------------- // Publish subscribe examples // ----------------------------------------------------------------------- // - // Cometd example commented out because camel-cometd is broken in Camel 2.3 + // Cometd example commented out because camel-cometd is broken since Camel 2.3 // //val cometdUri = "cometd://localhost:8111/test/abc?baseResource=file:target" @@ -79,14 +85,6 @@ class Boot { actorOf[Consumer4].start // POSTing "stop" to http://0.0.0.0:8877/camel/stop stops and unpublishes this actor actorOf[Consumer5].start // POSTing any msg to http://0.0.0.0:8877/camel/start starts and published Consumer4 again. - // ----------------------------------------------------------------------- - // Non-blocking consumer-producer example (Akka homepage transformation) - // ----------------------------------------------------------------------- - - val nbResponder = actorOf(new HttpTransformer).start - val nbProducer = actorOf(new HttpProducer(nbResponder)).start - val nbConsumer = actorOf(new HttpConsumer(nbProducer)).start - // ----------------------------------------------------------------------- // Active object example // ----------------------------------------------------------------------- From 18929cd31684039c8e1ce5a02bb3bcdf64f493f2 Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Tue, 20 Jul 2010 12:01:02 +0200 Subject: [PATCH 25/33] closes #31: Some fixes to the O-S-G-i settings in SBT project file; also deleted superfluous bnd4sbt.jar in project/build/lib directory. --- project/build/AkkaProject.scala | 151 +++++++++++++++++++------------- project/build/lib/bnd4sbt.jar | Bin 8092 -> 0 bytes 2 files changed, 91 insertions(+), 60 deletions(-) delete mode 100644 project/build/lib/bnd4sbt.jar diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index d1e125f07d..b24f477d75 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -16,6 +16,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // Compile settings // ------------------------------------------------------------------------------------------------------------------- + override def compileOptions = super.compileOptions ++ Seq("-deprecation", "-Xmigration", @@ -29,6 +30,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // Deploy/dist settings // ------------------------------------------------------------------------------------------------------------------- + lazy val deployPath = info.projectPath / "deploy" lazy val distPath = info.projectPath / "dist" def distName = "%s_%s-%s.zip".format(name, buildScalaVersion, version) @@ -37,6 +39,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // All repositories *must* go here! See ModuleConigurations below. // ------------------------------------------------------------------------------------------------------------------- + object Repositories { lazy val AkkaRepo = MavenRepository("Akka Repository", "http://scalablesolutions.se/akka/repository") lazy val CodehausSnapshotRepo = MavenRepository("Codehaus Snapshots", "http://snapshots.repository.codehaus.org") @@ -54,6 +57,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // must be resolved from a ModuleConfiguration. This will result in a significant acceleration of the update action. // Therefore, if repositories are defined, this must happen as def, not as val. // ------------------------------------------------------------------------------------------------------------------- + import Repositories._ lazy val atmosphereModuleConfig = ModuleConfiguration("org.atmosphere", SonatypeSnapshotRepo) lazy val grizzlyModuleConfig = ModuleConfiguration("com.sun.grizzly", JavaNetRepo) @@ -74,6 +78,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // Versions // ------------------------------------------------------------------------------------------------------------------- + lazy val ATMO_VERSION = "0.6" lazy val CAMEL_VERSION = "2.4.0" lazy val CASSANDRA_VERSION = "0.6.1" @@ -90,6 +95,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // Dependencies // ------------------------------------------------------------------------------------------------------------------- + object Dependencies { // Compile @@ -149,16 +155,16 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { lazy val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" - lazy val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" intransitive() + lazy val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" intransitive - lazy val lift = "net.liftweb" % "lift-webkit" % LIFT_VERSION % "compile" - lazy val lift_util = "net.liftweb" % "lift-util" % LIFT_VERSION % "compile" + lazy val lift_util = "net.liftweb" % "lift-util" % LIFT_VERSION % "compile" + lazy val lift_webkit = "net.liftweb" % "lift-webkit" % LIFT_VERSION % "compile" lazy val log4j = "log4j" % "log4j" % "1.2.15" % "compile" lazy val mongo = "org.mongodb" % "mongo-java-driver" % "1.4" % "compile" - lazy val multiverse = "org.multiverse" % "multiverse-alpha" % MULTIVERSE_VERSION % "compile" intransitive() + lazy val multiverse = "org.multiverse" % "multiverse-alpha" % MULTIVERSE_VERSION % "compile" intransitive lazy val netty = "org.jboss.netty" % "netty" % "3.2.1.Final" % "compile" @@ -203,6 +209,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // Subprojects // ------------------------------------------------------------------------------------------------------------------- + lazy val akka_core = project("akka-core", "akka-core", new AkkaCoreProject(_)) lazy val akka_amqp = project("akka-amqp", "akka-amqp", new AkkaAMQPProject(_), akka_core) lazy val akka_http = project("akka-http", "akka-http", new AkkaHttpProject(_), akka_core, akka_camel) @@ -212,10 +219,13 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { lazy val akka_jta = project("akka-jta", "akka-jta", new AkkaJTAProject(_), akka_core) lazy val akka_kernel = project("akka-kernel", "akka-kernel", new AkkaKernelProject(_), akka_core, akka_http, akka_spring, akka_camel, akka_persistence, akka_amqp) + lazy val akka_osgi = project("akka-osgi", "akka-osgi", new AkkaOSGiParentProject(_)) lazy val akka_samples = project("akka-samples", "akka-samples", new AkkaSamplesParentProject(_)) - // ------------------------------------------------------------ - // Run Akka microkernel using 'sbt run' + use for packaging executable JAR + // ------------------------------------------------------------------------------------------------------------------- + // Miscellaneous + // ------------------------------------------------------------------------------------------------------------------- + override def mainClass = Some("se.scalablesolutions.akka.kernel.Main") override def packageOptions = @@ -303,6 +313,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-core subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaCoreProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { val aopalliance = Dependencies.aopalliance val commons_codec = Dependencies.commons_codec @@ -333,6 +344,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-amqp subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaAMQPProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { val commons_io = Dependencies.commons_io val rabbit = Dependencies.rabbit @@ -346,6 +358,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-http subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaHttpProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { val annotation = Dependencies.annotation val atmo = Dependencies.atmo @@ -374,6 +387,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-camel subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaCamelProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { val camel_core = Dependencies.camel_core } @@ -381,6 +395,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-persistence subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaPersistenceParentProject(info: ProjectInfo) extends ParentProject(info) { lazy val akka_persistence_common = project("akka-persistence-common", "akka-persistence-common", new AkkaPersistenceCommonProject(_), akka_core) @@ -403,6 +418,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-persistence-redis subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaRedisProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { val commons_codec = Dependencies.commons_codec val redis = Dependencies.redis @@ -413,6 +429,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-persistence-mongo subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaMongoProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { val mongo = Dependencies.mongo @@ -422,6 +439,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-persistence-cassandra subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaCassandraProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) { val cassandra = Dependencies.cassandra val log4j = Dependencies.log4j @@ -440,11 +458,13 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-kernel subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaKernelProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) // ------------------------------------------------------------------------------------------------------------------- // akka-spring subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaSpringProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { val spring_beans = Dependencies.spring_beans val spring_context = Dependencies.spring_context @@ -458,6 +478,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { // ------------------------------------------------------------------------------------------------------------------- // akka-jta subproject // ------------------------------------------------------------------------------------------------------------------- + class AkkaJTAProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with CodeFellowPlugin { val atomikos_transactions = Dependencies.atomikos_transactions val atomikos_transactions_api = Dependencies.atomikos_transactions_api @@ -466,7 +487,19 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { //val atomikos_transactions_util = "com.atomikos" % "transactions-util" % "3.2.3" % "compile" } - // ================= OSGi Packaging ================== + // ------------------------------------------------------------------------------------------------------------------- + // OSGi stuff + // ------------------------------------------------------------------------------------------------------------------- + + class AkkaOSGiParentProject(info: ProjectInfo) extends ParentProject(info) { + lazy val akka_osgi_dependencies_bundle = project("akka-osgi-dependencies-bundle", "akka-osgi-dependencies-bundle", + new AkkaOSGiDependenciesBundleProject(_), akka_kernel, akka_jta) // akka_kernel does not depend on akka_jta (why?) therefore we list akka_jta here + lazy val akka_osgi_assembly = project("akka-osgi-assembly", "akka-osgi-assembly", + new AkkaOSGiAssemblyProject(_), akka_osgi_dependencies_bundle, akka_core, akka_amqp, akka_http, + akka_camel, akka_spring, akka_jta, akka_persistence.akka_persistence_common, + akka_persistence.akka_persistence_redis, akka_persistence.akka_persistence_mongo, + akka_persistence.akka_persistence_cassandra) + } class AkkaOSGiDependenciesBundleProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { override def bndClasspath = compileClasspath @@ -497,73 +530,69 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { class AkkaOSGiAssemblyProject(info: ProjectInfo) extends DefaultProject(info) { // Scala bundle - val scala_bundle = "com.weiglewilczek.scala-lang-osgi" % "scala-library_2.8.0.RC3" % "1.0" % "compile" intransitive() + val scala_bundle = "com.weiglewilczek.scala-lang-osgi" % "scala-library" % buildScalaVersion % "compile" intransitive // Lift bundles - val lift_actor = "net.liftweb" % "lift-actor" % LIFT_VERSION % "compile" intransitive() - val lift_common = "net.liftweb" % "lift-common" % LIFT_VERSION % "compile" intransitive() - val lift_json = "net.liftweb" % "lift-json" % LIFT_VERSION % "compile" intransitive() - val lift_util = "net.liftweb" % "lift-util" % LIFT_VERSION % "compile" intransitive() - //val lift_webkit = "net.liftweb" % "lift-webkit" % LIFT_VERSION % "compile" intransitive() - //val lift_testkit = "net.liftweb" % "lift-testkit" % LIFT_VERSION % "compile" intransitive() +// val lift_util = Dependencies.lift_util.intransitive +// val lift_actor = "net.liftweb" % "lift-actor" % LIFT_VERSION % "compile" intransitive +// val lift_common = "net.liftweb" % "lift-common" % LIFT_VERSION % "compile" intransitive +// val lift_json = "net.liftweb" % "lift-json" % LIFT_VERSION % "compile" intransitive // Camel bundles - val camel_core = "org.apache.camel" % "camel-core" % "2.3.0" % "compile" intransitive() - val fusesource_commonman = "org.fusesource.commonman" % "commons-management" % "1.0" intransitive() + val camel_core = Dependencies.camel_core.intransitive + val fusesource_commonman = "org.fusesource.commonman" % "commons-management" % "1.0" intransitive // Spring bundles - val spring_beans = "org.springframework" % "spring-beans" % "3.0.2.RELEASE" % "compile" intransitive() - val spring_context = "org.springframework" % "spring-context" % "3.0.2.RELEASE" % "compile" intransitive() - val spring_jms = "org.springframework" % "spring-jms" % "3.0.2.RELEASE" % "compile" intransitive() - val spring_aop = "org.springframework" % "spring-aop" % "3.0.2.RELEASE" % "compile" intransitive() - val spring_asm = "org.springframework" % "spring-asm" % "3.0.2.RELEASE" % "compile" intransitive() - val spring_core = "org.springframework" % "spring-core" % "3.0.2.RELEASE" % "compile" intransitive() - val spring_expression = "org.springframework" % "spring-expression" % "3.0.2.RELEASE" % "compile" intransitive() - val spring_tx = "org.springframework" % "spring-tx" % "3.0.2.RELEASE" % "compile" intransitive() + val spring_beans = Dependencies.spring_beans.intransitive + val spring_context = Dependencies.spring_context.intransitive + val spring_aop = "org.springframework" % "spring-aop" % SPRING_VERSION % "compile" intransitive + val spring_asm = "org.springframework" % "spring-asm" % SPRING_VERSION % "compile" intransitive + val spring_core = "org.springframework" % "spring-core" % SPRING_VERSION % "compile" intransitive + val spring_expression = "org.springframework" % "spring-expression" % SPRING_VERSION % "compile" intransitive + val spring_jms = "org.springframework" % "spring-jms" % SPRING_VERSION % "compile" intransitive + val spring_tx = "org.springframework" % "spring-tx" % SPRING_VERSION % "compile" intransitive - val commons_io = "commons-io" % "commons-io" % "1.4" % "compile" intransitive() - val commons_pool = "commons-pool" % "commons-pool" % "1.5.4" % "compile" intransitive() - val commons_codec = "commons-codec" % "commons-codec" % "1.4" % "compile" intransitive() - val commons_fileupload = "commons-fileupload" % "commons-fileupload" % "1.2.1" % "compile" intransitive() - val jackson_core = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile" intransitive() - val jackson = "org.codehaus.jackson" % "jackson-mapper-asl" % "1.2.1" % "compile" intransitive() - val netty = "org.jboss.netty" % "netty" % "3.2.0.CR1" % "compile" intransitive() - val guicey = "org.guiceyfruit" % "guice-all" % "2.0" % "compile" intransitive() - val joda = "joda-time" % "joda-time" % "1.6" intransitive() + val commons_codec = Dependencies.commons_codec.intransitive + val commons_io = Dependencies.commons_io.intransitive + val commons_pool = Dependencies.commons_pool.intransitive + val guicey = Dependencies.guicey.intransitive + val jackson = Dependencies.jackson.intransitive + val jackson_core = Dependencies.jackson_core.intransitive + val jsr311 = Dependencies.jsr311.intransitive + val jta_1_1 = Dependencies.jta_1_1.intransitive + val netty = Dependencies.netty.intransitive + val commons_fileupload = "commons-fileupload" % "commons-fileupload" % "1.2.1" % "compile" intransitive + val jms_1_1 = "org.apache.geronimo.specs" % "geronimo-jms_1.1_spec" % "1.1.1" % "compile" intransitive + val joda = "joda-time" % "joda-time" % "1.6" intransitive - val jta_1_1 = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile" intransitive() - val jms_1_1 = "org.apache.geronimo.specs" % "geronimo-jms_1.1_spec" % "1.1.1" % "compile" intransitive() - val jsr311 = "javax.ws.rs" % "jsr311-api" % "1.1" % "compile" intransitive() + override def packageAction = + task { + val libs: Seq[Path] = managedClasspath(config("compile")).get.toSeq + val prjs: Seq[Path] = info.dependencies.toSeq.asInstanceOf[Seq[DefaultProject]] map { _.jarPath } + val all = libs ++ prjs + val destination = outputPath / "bundles" + FileUtilities.copyFlat(all, destination, log) + log info "Copied %s bundles to %s".format(all.size, destination) + None + } - override def packageAction = { - val libs: Seq[Path] = managedClasspath(config("compile")).get.toSeq - val prjs: Seq[Path] = info.dependencies.toSeq.asInstanceOf[Seq[DefaultProject]].map(_.jarPath) - val all = libs ++ prjs - FileUtilities.copyFlat(all, outputPath / "bundles", log) - super.packageAction - } + override def artifacts = Set.empty } - class AkkaOSGiParentProject(info: ProjectInfo) extends ParentProject(info) { - lazy val akka_osgi_dependencies_bundle = project("akka-osgi-dependencies-bundle", "akka-osgi-dependencies-bundle", - new AkkaOSGiDependenciesBundleProject(_), akka_kernel, - akka_jta // akka_kernel does not depend on akka_jta (why?) therefore we list akka_jta here - ) - lazy val akka_osgi_assembly = project("akka-osgi-assembly", "akka-osgi-assembly", - new AkkaOSGiAssemblyProject(_), akka_osgi_dependencies_bundle, akka_core, akka_amqp, akka_http, - akka_camel, akka_spring, akka_jta, akka_persistence.akka_persistence_common, - akka_persistence.akka_persistence_redis, akka_persistence.akka_persistence_mongo, - akka_persistence.akka_persistence_cassandra) - } + // ------------------------------------------------------------------------------------------------------------------- + // Test + // ------------------------------------------------------------------------------------------------------------------- - // ================= TEST ================== class AkkaActiveObjectTestProject(info: ProjectInfo) extends DefaultProject(info) { // testing val junit = "junit" % "junit" % "4.5" % "test" val jmock = "org.jmock" % "jmock" % "2.4.0" % "test" } - // ================= EXAMPLES ================== + // ------------------------------------------------------------------------------------------------------------------- + // Examples + // ------------------------------------------------------------------------------------------------------------------- + class AkkaSampleAntsProject(info: ProjectInfo) extends DefaultSpdeProject(info) with CodeFellowPlugin { // val scalaToolsSnapshots = ScalaToolsSnapshots override def spdeSourcePath = mainSourcePath / "spde" @@ -574,8 +603,8 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { class AkkaSampleLiftProject(info: ProjectInfo) extends DefaultWebProject(info) with DeployProject with CodeFellowPlugin { val commons_logging = Dependencies.commons_logging - val lift = Dependencies.lift val lift_util = Dependencies.lift_util + val lift_webkit = Dependencies.lift_webkit val servlet = Dependencies.servlet // testing @@ -649,8 +678,10 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { new AkkaSampleOSGiProject(_), akka_core) } - // ------------------------------------------------------------ - // helper functions + // ------------------------------------------------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------------------------------------------------- + def removeDupEntries(paths: PathFinder) = Path.lazyPathFinder { val mapped = paths.get map { p => (p.relativePath, p) } @@ -675,7 +706,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { ) } - def akkaArtifacts = descendents(info.projectPath / "dist", "*" + buildScalaVersion + "_osgi-" + version + ".jar") + def akkaArtifacts = descendents(info.projectPath / "dist", "*" + buildScalaVersion + "-" + version + ".jar") // ------------------------------------------------------------ class AkkaDefaultProject(info: ProjectInfo, val deployPath: Path) extends DefaultProject(info) with DeployProject with OSGiProject diff --git a/project/build/lib/bnd4sbt.jar b/project/build/lib/bnd4sbt.jar deleted file mode 100644 index 5906b493003a67283602179ffdf24d15acdeb0b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8092 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 Date: Wed, 21 Jul 2010 08:33:20 +0200 Subject: [PATCH 26/33] Remove misleading term 'non-blocking' from comments. --- akka-samples/akka-sample-camel/src/main/scala/Boot.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index b8a483b55f..b3b428c01a 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -51,7 +51,7 @@ class Boot { consumer.start // ----------------------------------------------------------------------- - // Non-blocking consumer-producer example (Akka homepage transformation) + // Asynchronous consumer-producer example (Akka homepage transformation) // ----------------------------------------------------------------------- val httpTransformer = actorOf(new HttpTransformer).start From fc178be417a7ce5795a284c6affb4cbf636fab8b Mon Sep 17 00:00:00 2001 From: Debasish Ghosh Date: Wed, 21 Jul 2010 12:53:50 +0530 Subject: [PATCH 27/33] fix for idle client closing issues by redis server #338 and #340 --- .../src/main/scala/RedisStorageBackend.scala | 8 +++++++- .../2.8.0-1.4/redisclient-2.8.0-1.4.jar | Bin 175418 -> 177997 bytes 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala index 33b1c04a73..eef60784a0 100644 --- a/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala +++ b/akka-persistence/akka-persistence-redis/src/main/scala/RedisStorageBackend.scala @@ -72,7 +72,7 @@ private [akka] object RedisStorageBackend extends // need an explicit definition in akka-conf val nodes = config.getList("akka.storage.redis.cluster") - val db = + def connect() = nodes match { case Seq() => // no cluster defined @@ -89,6 +89,8 @@ private [akka] object RedisStorageBackend extends } } + var db = connect() + /** * Map storage in Redis. *

@@ -411,6 +413,10 @@ private [akka] object RedisStorageBackend extends try { body } catch { + case e: RedisConnectionException => { + db = connect() + body + } case e: java.lang.NullPointerException => throw new StorageException("Could not connect to Redis server") case e => diff --git a/embedded-repo/com/redis/redisclient/2.8.0-1.4/redisclient-2.8.0-1.4.jar b/embedded-repo/com/redis/redisclient/2.8.0-1.4/redisclient-2.8.0-1.4.jar index a5c824b19ea6113c9671cd7a49978886599a359b..b811e6ab926b5b6fe094d5a4989abbb61b3aec5e 100644 GIT binary patch delta 38960 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 z z8U4*wnsPd8zrNW!xxT9P+pCRNrw9JnbLa5hFOf6eujpTH5ZwBZrF!Fv3yj4#pGj5~ep>?>o8cUEEl-ius1arH!X$mRde_wE1`Ob;Fw7PxdzWRZE=z^u>cM z>)77eewAk&#Wd#4U$xo()&f#33FDt*_g^lkI9DGe*1ogX zFZq?iIVY(l>V=bgH$_PDCUI}M)-ijA*~v%COihIYg-_17cJ`g}bDuPxa)A;mfBA@PB2dA64L#W`{sVhlGOoP+qbW5w^A}|i286!Tcuk1 zi$}<^p!~^NflC)IstbJ*kz27v>7xAGrS*NM5?7Q)%4jb+mpDCZV{r9sBhkp@rKOur zZ{1mR+-2q7qDAaFzpH1P=jlgfPmj8?;BxZTOJO@#>Q=4LeUn>aRUW-^Q)26-{XZw% zwhs%u8~5*4|lz!#hE%Ed2zMaTF=SFaSng7{qp1Bt+ zb8hBttgn)`@wJnWSaMM7=4;K&6_aYUD!gUOJh$~*oK~3~Q(C$qJZr_8l}p2BUMsJj zbGWoRHS^7cVzE=Z(`p`D#mj%%`pt3fWz*MFvuz(}%+Y6ES)F#+fU zoIch(`+AAW>^H?<%Vvf#+&Y`SRBUDTf`moO%a*I0bX}*o;mkA(oh#AT8>)WK`BwEx zCuo^jf62UWc`Mh4dP~jyR6k?+p5vD{c&#f`Q@(mzeQ)gU^w4zKYX-M-8dct3emaGH zsqeYhW}$hqcdaYd)qnVLt!U-hVD{=eJLI&gY>vnH`(m@7FQ|~zV18qcJk4u zQ$@F|EAuJZb*w3QtKZHQl|oB;cl~WQ_bN;G{=G7>eQT=s@^GywYi}Qa^=4gYYyFm$ z*A|uPD6guCm*>Zh#Lv$|(joO7Y6M3%2Iedg)p!t+10 z&YfJFy-TgB_xUm<)3TE{tsi{45^!?s`g3=qt{U!Lm;Siz+NpPw)puFMZMt&r$v20C z`z{{*wYe`j`tiMqjmFLHyXS0LU0lll#^tQ`@$SCe>EGW4{)+A|aamq9=R&d5v~w17 za{q1YtJtupuD<&8tanR4Rje;OQLMqAYjxN9#v{E+Yo<(_ZW_ACR%P~GW_K&0gN4hB zCEjcb$>FamyUexn%JpYKOGATH6KeA2-nky}^45YVuG~**p@+VvK20r4pX@%3>+Rth z+qZ?+R$cSld7v8D4L9j?=SmbzBOI(PlNo8&2U zRa)A%Ik$3~RPy3tZ#^;N*Im8!cIhWyE!}#=Z9V%YE$_b@E=o_|*O<6Y(yYoj?B<#B zZ#iEoZy3M2+7R9tvT%Rvjt$lygsg3S&tIyn>bvKVzv%qZ^UJqSVhTDGrIPc`JEFPe zzh<@nwExGIdH(mS3;dHm&-W@v=4!t`vo6m)lFEl>5fAE zUDvJ;(N7(}J>TcwBDQtbtFV9Bk$&p%*L3n4C2*>rR@K5uVs2j z-*n>mzfIJ=q5ihpEzA2%MW#2;g}zdWWvkNvmsT%yTcEb_jY#wQ>zgM_rzaf9m#KMIK+K)U)YjnTFByYfCH^=wE;-B0#XH-5E79DbUe@N0SJvT2>K{)9R z*IcFET+PEHBi(eT*x3&!r$e{RzQIcVew#rNTWQYpJAY zte;>IKCiZWf`<8Yr?d+iMnUH$%{cf~*56My$v%azYwp7`vg zK?Reg@41x6CiCr=KVM=w*SmPvygLV;=bw2r=lS#ZbE0{k-hK<=y@`2-W@ogI2JN$o*Nk_L(>8hV`hD~^#`h}u0#5g)?)QIZ zCiG$UW`}*HKPTj$W~w~@N%8(f)=#mg+iS|jPW+z6|8t(sg#UYMPW(Q4@W;NI=8u1q z=6wG7DCUIFm3|%j74|b&KOH{ZUSAWk=I!xMSHC@eBK*uHICisx-Q48*)XFnD%1|Ed0miJvn+U32~ZGgoc*d+mOH-R;W%Hk(AF1X1no z`u*pxHq;wDD-C|dmNK(L@2;@m`+-}j-*q?eUF$v*q0Z+yS#qIV z;S5RXBNo!1jutBOnLajo-a6SV!u!{<%!_k5V@l->@6L3XJ)vZEM~UU0RB5(sQ<-z)(IxIaOI{jS&srKb=Y-+L!<#Ql$C+jwbrCyp z;FF^1Le^z2Vl%{|9;B3U&uN-)!uz63?Ywixj3bR=YJN)UHBMNxs=8|Cgv`0;JCuv{ z?sJ*&pBD^UApI$i`+?hHrn@ZWvt3q4H9UX*s_*y@n*+KXlXp%Ptbe|3Pr)U&-LW&e z<5W($mMg5ZWQb2v@=e|!(($)Q@SAqalJ!Cp_gQsk$IY%k9+tD?S*g>z_2Py5gZHew zHzQlAcCuhX`N6v`CA{qU=RXFr-gjYSn}+pdyY@|6t}}) zCv{r%%s)%^h`$r}{P{2Dw1?NJdiGY&FU*Tner=fWrCI3`KWFnJX@=v}21`epFrhW1EGI&-YiE%dIZ?l}h-f-rwiEZFA+5 zFR_Vte>nE_FTa$NBQt6L5q5i(rsY#MeVigxS2N-7PW$7_r+g6pp?R{#>t>N$-I@9I zL8orc3lckY-o@!gkdw{1mI|A6#X83QBAg#B#1BpL?NItLb>pdQj=G0P!|+d>&I$RTCYzHim7k3I`}Da!ssGsQ zIbqf34@(qJ$Zz6cs`uQT`cdvjdj`MFRI__Ym+I>~J?8tl-%$$UVhZwdZZnKso1Yw|spG%J>iDM= z1*Wx%dW~YQUC$L>j1oS*iuKln?BpNJdw)AUJ)BCK7uTK@&-l%HE7EKCNBw^{b~>y- znlHZMrN*gy7gj8v%`@k=V0+~{{&L&97h-DXnVH(VlrOgZ{c$t9{qGN($kUV}~ z@sG^W^PGQt9=IPqAM%;~g{A_#e$}OezLCCV6OXxmyg1z~SAL>v&yN?6*RKzFEN<1= z_F;d^(e`r&-3!DI|L8oDIpthY%7VfmKjE<7{~1A}`yc=EvQ>*SFlhQCkM0Y1yx*QH z!nBA9wEA_rOf;kHc71UsMeq_l@JhSM4|3$U_ewD}gH?d;sM@Y4%XAmKfnqzKB9jj* zXx+`kJn8A6mAKoR)tCYx+bpiBGnsLMOx@0J$Rq$Z5Ttv0-g`#g>CzjRc(#X`FlDeX zi*&r-e$ayH6WILi+pL+Yz-EKC$5>96-^e7gy}_Qz1iam4`*vrhbKtdt;FYY~7rQYP zfVDv;CQKlNog37);!zA=DlJy`4Zq&%SW*(}Z&+<~zSXo3s1*yuW{+zbGrAZ~i(&D67yr_>0!|s9Bk7>P5sQZX~w_Ud=fnwmw^T zXGGs|(ZeQjmTrI6F1=|pPw;~FLUtzx#tt6a!pj?X9u?3jtuu3;xhqNgBaicvE$ebL zHm#G$E;_o+B)NCfU9Zh6^9^~_zipnSIbC-5%NrpFrylFMomTH@{A1GP6@}T6D__Pq z3H;T2ANlI9#op+h4hj)@fXG@7X9ApX4^B}Z|bBJ zj)23W!L^4uQ$D1q#xVLyrGzd_oX9m#glBRmU*zenrLU6JDs5C0TQ+#4*H4gav#~!h zE%`=N^J&ZC$=AbAh%;q){hPYSYG?Yfk;a~X$hy{u!quGYQO3-@+@;)>u&_1I!*AoAewhSwDe zB89RK-#&5KRaUp^<3+EQFJf-ucjC(*2>;r)_xJ|wdh6K5Tl&9cSbddb-re!;(V=RV z`4?^<5m%9zmmjRkVdar6uC&FCL*_(q%0v$CD;|8|nno`IT_?JndfchhtGVHV@uS&! zpU>6@_C~8Ny<~5=sXJS~@b_WHqWip%dy+5IL zWBX0{m0$SOf`Y9jJl-bO{TBIfgR$bw7DfSnO#h_;4E~ri^(0FrIovxlEEUK-As{Sk*D2E zMPNo$50fXDv8;zF5zOG~WpV{Gf_j;vz`EA=GR1>M1pAmA!6Ko3O#Wbz<$X*cV8*{b zCPzNuj`ubv;TPpj_w8r021|DLGf6YeG@qV0iAihnKXcyc_xqW6z%uVZGSjRkU$j!2 zww{RxRJ=`hn7|~>s5Cuh0+X(SNXL5{zypK$i(hfL*Z* zr0n+eYZI9iz+V12kx81VIbm{Fg1kaNekz+l9}z+i@AFLM&e(=K3t z9PYR>iItgwp^$}v0o5PVQYOz$k(z#g64W2aw|wcNnD=lx*JLI;u)Dn{Gf6X@%>kLO zHTho-&-8hdnRpaH&Ut~f(^CaSdsse5xx(~UlbM9TzWfg|Bw!&(RBN*4Lc!@XCo%DG zf(-ei))X{t`un*|qD*E>AoBc6_@?ik0*$#-Q<$Wg43|F`iwp%93beIU80^W3GU`9WnHU)Km>C$9QB*b_2RT=4@@-|#>H5={guu$r zxnxhCD#XChp@NYtGYNrBNXhxf_lSvsVLgUd;~q`t|H~*oJ##vfggnTy zY=f9-222bL|Ct#WY)~xgpT1x^lQ%e)-%V$dX3~5ynem|P^anjmoRj}O=b!E{0~+4` zGnk~AGTwsxt2LQ_2^(nlt0dSR>}LKVf0-E=BH0-jj8P0v`~Xs}J#8kF7&u`|&Sa8i z+W%$x0!~Jm>03532`Pdc*LP-TSQ9e?gBCjjg9(cMJ&cUgKTKefp1xovlNvZYPJ&EW zz&icnY9_7e0<)Ne!6tZI`InN)%)n5=#=xMBV!}sG#_0<=8Fi)?%!2x(eioB7^Lk#! z>G8abQjpXDN`ohPdip?t>BEYiuT%x6UtGgvI9+5mlQ28Ty^&5cr|)0MB+i^I!Z=-8 zgi&pJ-fU<}t)0yz%_J;7eb#Iyd9aW7fCQ`+rU!tc@4q}F+w`c3Og!=+8xNd0I!BL- zfk9r2fx!~R(XG?v=P-GI<0NYilQgrGG2`@8MvO9G*Mj47Y1KrndrS-rN*MXOaQgW< zOzL30Kj$z>Gftf@I+sZa9ClW7nWUL)EWu$1+W9+O&XSQGoD0EDT>z2}v6;SiE|Vr$ z{Y#L5u02>iXb-RySk!kOlQffx6Uawmpaf|IE)H|P*3J=TVqmz$%)p?E;+dnC#Q%Np%Kh%Nr}nS{ZC^I~6@*#ssA1{YQa1}hZv_DpY_53NVe z&S#Ql-Vz3mVVUU-VT^3k&rf6G0sBT~0h2W2_vsc3nAE`zJW{z+Xl&(1tj1aPi~eQU1DJh=D=Ej)6f6#V(f&P~d8SPF%z!&8*$YIK8ohQ9=~iTcDKsOXX&$#`M6+OrlJ7lR)(@0`OpoqrCaBE0sVwS-BUNoonmEGKF(dEvee$28L%W3=9@1Ar`U(KB!sFB+cx&k8!%3B9r9w4SN~cru%PY;!y-O zNs#x8iK95-_+iHBvWFQZrvF7XN+t=gPYOVyvX`d!u4K{%`(*b@CTV7-Yv5v0e)^A< z&`9T7#U#zN>IOu2!!{;P&~7tG`Y}qVe{+qIfnh!~1A`2TqeE{`pSX%i3vAIokO8H4 zr{7-1qyrYD{ZD)WC5&wR4A>4HE-{5DR+! zvh*3q04H5g-bLQJf*N|xFOlDBKUMZoPC4v-ab zEtAen=U>ev#`O3HDE+yBqYb(HFO3oc1;4?Ch73PihMb4kou3BzIMw36%U)!AVBA_#K4fq%D`ZX5`rD(Ow%t~Fquv7+5q+4%neM^Otn_i zw{3trYVWQr7AY16h9jKlO`~77Ow%2^nRKTY*fR-$vm`jy-xN&o?`L9Q@MUIT5J9o6 z&S`r5MkXb&s(FFy-*_@HF#KU+U_eby{;ty(Ze&sgmxd=dGD$Pvbq7VH*7N{RCf@0C zo1n>9cN3E|bE!8pP_Otf34j9!tgQhgm*@l1rU@E9kpl;V+5g9nJeU|5%vjKUQsK`u z{a^r-EjSp!$>e)*WbjiC1_m{L1_nVCPiO^A&)>`>54LCGW+rJSzv$`fHbZ+R7eNA{ zans*zX3_%3vGf)uX{JYs(=E0@RYY%Ll4f3%0!>+qw=ju=)$IidUruM594H_RI!$HT zRwiL^JiiDz`S&L)1H&Xv^lGp(iwRWRi%#dB$Rq?#wQ*aSq?t|?Ot0PwO~wniGD$OY z6*EmvRFj*2cPq3DaC_tNP%}_-orQrx62))NE2i^rV^Ze?Wk%y$M#(zU8y7N(GN)97 zQ;F8}>orXL( Date: Wed, 21 Jul 2010 11:22:42 +0200 Subject: [PATCH 28/33] closes #342: Added parens to ActorRegistry.shutdownAll. --- akka-core/src/main/scala/actor/ActorRegistry.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/akka-core/src/main/scala/actor/ActorRegistry.scala b/akka-core/src/main/scala/actor/ActorRegistry.scala index 88113a30a0..57b27f08b0 100644 --- a/akka-core/src/main/scala/actor/ActorRegistry.scala +++ b/akka-core/src/main/scala/actor/ActorRegistry.scala @@ -159,7 +159,7 @@ object ActorRegistry extends ListenerManagement { /** * Shuts down and unregisters all actors in the system. */ - def shutdownAll = { + def shutdownAll() { log.info("Shutting down all actors in the system...") foreach(_.stop) actorsByUUID.clear From 7e56c35a5e43986f6df1addab879a28bd7e50a4d Mon Sep 17 00:00:00 2001 From: Heiko Seeberger Date: Wed, 21 Jul 2010 11:17:43 +0200 Subject: [PATCH 29/33] closes #341: Fixed O-S-G-i example. --- .../src/main/scala/Activator.scala | 24 -------------- .../src/main/scala/Actor.scala | 12 ------- .../src/main/scala/osgiExample.scala | 33 +++++++++++++++++++ project/build/AkkaProject.scala | 12 +++---- 4 files changed, 38 insertions(+), 43 deletions(-) delete mode 100644 akka-samples/akka-sample-osgi/src/main/scala/Activator.scala delete mode 100644 akka-samples/akka-sample-osgi/src/main/scala/Actor.scala create mode 100644 akka-samples/akka-sample-osgi/src/main/scala/osgiExample.scala diff --git a/akka-samples/akka-sample-osgi/src/main/scala/Activator.scala b/akka-samples/akka-sample-osgi/src/main/scala/Activator.scala deleted file mode 100644 index 04c7f165b9..0000000000 --- a/akka-samples/akka-sample-osgi/src/main/scala/Activator.scala +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (C) 2009-2010 Scalable Solutions AB - */ - -package sample.osgi - -import org.osgi.framework.{BundleActivator, BundleContext} - - -class Activator extends BundleActivator { - - def start(context: BundleContext) { - println("Start") - val osgiActor = new OSGiActor - //osgiActor ! "Hello" - Unit - } - - def stop(context: BundleContext) { - println("stop") - } - -} - diff --git a/akka-samples/akka-sample-osgi/src/main/scala/Actor.scala b/akka-samples/akka-sample-osgi/src/main/scala/Actor.scala deleted file mode 100644 index 90bd521d7b..0000000000 --- a/akka-samples/akka-sample-osgi/src/main/scala/Actor.scala +++ /dev/null @@ -1,12 +0,0 @@ -package sample.osgi - -import se.scalablesolutions.akka.actor.Actor - - -class OSGiActor extends Actor { - def receive = { - case msg: String => - println("Got message: " + msg) - } -} - diff --git a/akka-samples/akka-sample-osgi/src/main/scala/osgiExample.scala b/akka-samples/akka-sample-osgi/src/main/scala/osgiExample.scala new file mode 100644 index 0000000000..0cef797c47 --- /dev/null +++ b/akka-samples/akka-sample-osgi/src/main/scala/osgiExample.scala @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2009-2010 Scalable Solutions AB + */ + +package se.scalablesolutions.akka +package sample.osgi + +import actor.{ Actor, ActorRegistry } +import actor.Actor._ + +import org.osgi.framework.{ BundleActivator, BundleContext } + +class Activator extends BundleActivator { + + def start(context: BundleContext) { + println("Starting the OSGi example ...") + val echo = actorOf[EchoActor].start + val answer = (echo !! "OSGi example") + println(answer getOrElse "No answer!") + } + + def stop(context: BundleContext) { + ActorRegistry.shutdownAll() + println("Stopped the OSGi example.") + } +} + +class EchoActor extends Actor { + + override def receive = { + case x => self reply x + } +} diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index b24f477d75..6c770d4e54 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -170,6 +170,8 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { lazy val protobuf = "com.google.protobuf" % "protobuf-java" % "2.3.0" % "compile" + lazy val osgi_core = "org.osgi" % "org.osgi.core" % "4.2.0" + lazy val rabbit = "com.rabbitmq" % "amqp-client" % "1.8.1" % "compile" lazy val redis = "com.redis" % "redisclient" % "2.8.0-1.4" % "compile" @@ -646,13 +648,9 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { } class AkkaSampleOSGiProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) with BNDPlugin { - override def bndClasspath = compileClasspath - - val osgi_core = "org.osgi" % "org.osgi.core" % "4.2.0" - - override def bndBundleActivator = Some("sample.osgi.Activator") - override def bndPrivatePackage = Nil - override def bndExportPackage = Seq("sample.osgi.*;version=0.9") + val osgi_core = Dependencies.osgi_core + override lazy val bndBundleActivator = Some("se.scalablesolutions.akka.sample.osgi.Activator") + override lazy val bndExportPackage = Nil // Necessary because of mixing-in AkkaDefaultProject which exports all ...akka.* packages! } class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) { From c5f30d8948eacc37f62c4637402cc011cdbca828 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Wed, 21 Jul 2010 14:32:07 +0200 Subject: [PATCH 30/33] Closes #333 Allow applications to wait for endpoints being activated --- akka-camel/src/main/scala/CamelService.scala | 19 +++++++++- .../src/main/scala/ConsumerPublisher.scala | 38 +++++++++---------- .../test/scala/CamelServiceFeatureTest.scala | 20 +++++----- .../src/test/scala/RemoteConsumerTest.scala | 4 +- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/akka-camel/src/main/scala/CamelService.scala b/akka-camel/src/main/scala/CamelService.scala index 65a6a44fe5..11f2907c4d 100644 --- a/akka-camel/src/main/scala/CamelService.scala +++ b/akka-camel/src/main/scala/CamelService.scala @@ -1,9 +1,10 @@ /** * Copyright (C) 2009-2010 Scalable Solutions AB */ - package se.scalablesolutions.akka.camel +import java.util.concurrent.CountDownLatch + import se.scalablesolutions.akka.actor.Actor._ import se.scalablesolutions.akka.actor.{AspectInitRegistry, ActorRegistry} import se.scalablesolutions.akka.util.{Bootable, Logging} @@ -77,6 +78,22 @@ trait CamelService extends Bootable with Logging { * @see onUnload */ def unload = onUnload + + /** + * Sets an expectation of the number of upcoming endpoint activations and returns + * a {@link CountDownLatch} that can be used to wait for the activations to occur. + * Endpoint activations that occurred in the past are not considered. + */ + def expectEndpointActivationCount(count: Int): CountDownLatch = + (consumerPublisher !! SetExpectedRegistrationCount(count)).as[CountDownLatch].get + + /** + * Sets an expectation of the number of upcoming endpoint de-activations and returns + * a {@link CountDownLatch} that can be used to wait for the de-activations to occur. + * Endpoint de-activations that occurred in the past are not considered. + */ + def expectEndpointDeactivationCount(count: Int): CountDownLatch = + (consumerPublisher !! SetExpectedUnregistrationCount(count)).as[CountDownLatch].get } /** diff --git a/akka-camel/src/main/scala/ConsumerPublisher.scala b/akka-camel/src/main/scala/ConsumerPublisher.scala index 298c70c2b7..e50c625639 100644 --- a/akka-camel/src/main/scala/ConsumerPublisher.scala +++ b/akka-camel/src/main/scala/ConsumerPublisher.scala @@ -65,50 +65,50 @@ private[camel] object ConsumerPublisher extends Logging { * Actor that publishes consumer actors and active object methods at Camel endpoints. * The Camel context used for publishing is CamelContextManager.context. This actor * accepts messages of type - * se.scalablesolutions.akka.camel.service.ConsumerRegistered, - * se.scalablesolutions.akka.camel.service.ConsumerMethodRegistered and - * se.scalablesolutions.akka.camel.service.ConsumerUnregistered. + * se.scalablesolutions.akka.camel.ConsumerRegistered, + * se.scalablesolutions.akka.camel.ConsumerUnregistered. + * se.scalablesolutions.akka.camel.ConsumerMethodRegistered and + * se.scalablesolutions.akka.camel.ConsumerMethodUnregistered. * * @author Martin Krasser */ private[camel] class ConsumerPublisher extends Actor { import ConsumerPublisher._ - @volatile private var latch = new CountDownLatch(0) + @volatile private var registrationLatch = new CountDownLatch(0) + @volatile private var unregistrationLatch = new CountDownLatch(0) - /** - * Adds a route to the actor identified by a Publish message to the global CamelContext. - */ protected def receive = { case r: ConsumerRegistered => { handleConsumerRegistered(r) - latch.countDown // needed for testing only. + registrationLatch.countDown } case u: ConsumerUnregistered => { handleConsumerUnregistered(u) - latch.countDown // needed for testing only. + unregistrationLatch.countDown } case mr: ConsumerMethodRegistered => { handleConsumerMethodRegistered(mr) - latch.countDown // needed for testing only. + registrationLatch.countDown } case mu: ConsumerMethodUnregistered => { handleConsumerMethodUnregistered(mu) - latch.countDown // needed for testing only. + unregistrationLatch.countDown } - case SetExpectedMessageCount(num) => { - // needed for testing only. - latch = new CountDownLatch(num) - self.reply(latch) + case SetExpectedRegistrationCount(num) => { + registrationLatch = new CountDownLatch(num) + self.reply(registrationLatch) + } + case SetExpectedUnregistrationCount(num) => { + unregistrationLatch = new CountDownLatch(num) + self.reply(unregistrationLatch) } case _ => { /* ignore */} } } -/** - * Command message used For testing-purposes only. - */ -private[camel] case class SetExpectedMessageCount(num: Int) +private[camel] case class SetExpectedRegistrationCount(num: Int) +private[camel] case class SetExpectedUnregistrationCount(num: Int) /** * Defines an abstract route to a target which is either an actor or an active object method.. diff --git a/akka-camel/src/test/scala/CamelServiceFeatureTest.scala b/akka-camel/src/test/scala/CamelServiceFeatureTest.scala index df42237a61..09d386c383 100644 --- a/akka-camel/src/test/scala/CamelServiceFeatureTest.scala +++ b/akka-camel/src/test/scala/CamelServiceFeatureTest.scala @@ -27,7 +27,7 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi // count expectations in the next step (needed for testing only). service.consumerPublisher.start // set expectations on publish count - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + val latch = service.expectEndpointActivationCount(1) // start the CamelService service.load // await publication of first test consumer @@ -44,7 +44,7 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi scenario("access non-blocking consumer actors via Camel direct-endpoints") { given("two consumer actors registered before and after CamelService startup") - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + val latch = service.expectEndpointActivationCount(1) actorOf(new TestConsumer("direct:publish-test-2")).start assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -60,7 +60,7 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi scenario("access blocking, non-responding consumer actor via a Camel direct-endpoint") { given("a consumer actor registered after CamelService startup") - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + val latch = service.expectEndpointActivationCount(1) actorOf(new TestBlocker("direct:publish-test-3")).start assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -84,13 +84,13 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi given("a consumer actor registered after CamelService startup") assert(CamelContextManager.context.hasEndpoint(endpointUri) eq null) - var latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + var latch = service.expectEndpointActivationCount(1) val consumer = actorOf(new TestConsumer(endpointUri)).start assert(latch.await(5000, TimeUnit.MILLISECONDS)) assert(CamelContextManager.context.hasEndpoint(endpointUri) ne null) when("the actor is stopped") - latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + latch = service.expectEndpointDeactivationCount(1) consumer.stop assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -121,7 +121,7 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi scenario("access active object methods via Camel direct-endpoints") { given("an active object registered after CamelService startup") - var latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get + var latch = service.expectEndpointActivationCount(3) val obj = ActiveObject.newInstance(classOf[PojoBase]) assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -136,7 +136,7 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi assert(response3 === "m4base: x y") // cleanup to avoid conflicts with next test (i.e. avoid multiple consumers on direct-endpoints) - latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get + latch = service.expectEndpointDeactivationCount(3) ActiveObject.stop(obj) assert(latch.await(5000, TimeUnit.MILLISECONDS)) } @@ -144,15 +144,15 @@ class CamelServiceFeatureTest extends FeatureSpec with BeforeAndAfterAll with Gi feature("Unpublish active object method from the global CamelContext") { - scenario("access to unregistered active object methof via Camel direct-endpoint fails") { + scenario("access to unregistered active object method via Camel direct-endpoint fails") { given("an active object registered after CamelService startup") - var latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get + var latch = service.expectEndpointActivationCount(3) val obj = ActiveObject.newInstance(classOf[PojoBase]) assert(latch.await(5000, TimeUnit.MILLISECONDS)) when("the active object is stopped") - latch = (service.consumerPublisher !! SetExpectedMessageCount(3)).as[CountDownLatch].get + latch = service.expectEndpointDeactivationCount(3) ActiveObject.stop(obj) assert(latch.await(5000, TimeUnit.MILLISECONDS)) diff --git a/akka-camel/src/test/scala/RemoteConsumerTest.scala b/akka-camel/src/test/scala/RemoteConsumerTest.scala index 7e3b666590..25c6d6b975 100644 --- a/akka-camel/src/test/scala/RemoteConsumerTest.scala +++ b/akka-camel/src/test/scala/RemoteConsumerTest.scala @@ -45,7 +45,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = actorOf[RemoteConsumer].start when("remote consumer publication is triggered") - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + var latch = service.expectEndpointActivationCount(1) consumer !! "init" assert(latch.await(5000, TimeUnit.MILLISECONDS)) @@ -61,7 +61,7 @@ class RemoteConsumerTest extends FeatureSpec with BeforeAndAfterAll with GivenWh val consumer = ActiveObject.newRemoteInstance(classOf[PojoRemote], host, port) when("remote consumer publication is triggered") - val latch = (service.consumerPublisher !! SetExpectedMessageCount(1)).as[CountDownLatch].get + var latch = service.expectEndpointActivationCount(1) consumer.foo("init") assert(latch.await(5000, TimeUnit.MILLISECONDS)) From 764555dfa870728c2ff55e2bff82b531f84c967f Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Wed, 21 Jul 2010 15:37:37 +0200 Subject: [PATCH 31/33] Added example how to use JMS endpoints in standalone applications. --- .../{context-boot.xml => context-jms.xml} | 0 .../src/main/scala/Boot.scala | 2 +- .../main/scala/StandaloneApplication.scala | 45 ++++++++++++++++++- 3 files changed, 44 insertions(+), 3 deletions(-) rename akka-samples/akka-sample-camel/src/main/resources/{context-boot.xml => context-jms.xml} (100%) diff --git a/akka-samples/akka-sample-camel/src/main/resources/context-boot.xml b/akka-samples/akka-sample-camel/src/main/resources/context-jms.xml similarity index 100% rename from akka-samples/akka-sample-camel/src/main/resources/context-boot.xml rename to akka-samples/akka-sample-camel/src/main/resources/context-jms.xml diff --git a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala index b3b428c01a..bcb884d186 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/Boot.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/Boot.scala @@ -35,7 +35,7 @@ class Boot { // ----------------------------------------------------------------------- // Create CamelContext and a Spring-based registry - val context = new ClassPathXmlApplicationContext("/context-boot.xml", getClass) + val context = new ClassPathXmlApplicationContext("/context-jms.xml", getClass) val registry = new ApplicationContextRegistry(context) // Use a custom Camel context and a custom touter builder diff --git a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala index 0a7304ba0e..941da479b3 100644 --- a/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala +++ b/akka-samples/akka-sample-camel/src/main/scala/StandaloneApplication.scala @@ -5,8 +5,9 @@ import org.apache.camel.builder.RouteBuilder import org.apache.camel.spring.spi.ApplicationContextRegistry import org.springframework.context.support.ClassPathXmlApplicationContext -import se.scalablesolutions.akka.camel.{CamelService, CamelContextManager} -import se.scalablesolutions.akka.actor.{ActorRegistry, ActiveObject} +import se.scalablesolutions.akka.actor.{Actor, ActorRegistry, ActiveObject} +import se.scalablesolutions.akka.camel._ +import se.scalablesolutions.akka.util.Logging /** * @author Martin Krasser @@ -81,3 +82,43 @@ class StandaloneSpringApplicationRoute extends RouteBuilder { from("direct:test3").to("active-object:pojo3?method=foo") } } + +object StandaloneJmsApplication { + def main(args: Array[String]) = { + val context = new ClassPathXmlApplicationContext("/context-jms.xml") + val registry = new ApplicationContextRegistry(context) + + // Init CamelContextManager with custom CamelContext + CamelContextManager.init(new DefaultCamelContext(registry)) + + // Create new instance of CamelService and start it + val service = CamelService.newInstance.load + // Expect two consumer endpoints to be activated + val completion = service.expectEndpointActivationCount(2) + + val jmsUri = "jms:topic:test" + // Wire publisher and consumer using a JMS topic + val jmsSubscriber1 = Actor.actorOf(new Subscriber("jms-subscriber-1", jmsUri)).start + val jmsSubscriber2 = Actor.actorOf(new Subscriber("jms-subscriber-2", jmsUri)).start + val jmsPublisher = Actor.actorOf(new Publisher("jms-publisher", jmsUri)).start + + // wait for the consumer (subscriber) endpoint being activated + completion.await + + // Send 10 messages to via publisher actor + for(i <- 1 to 10) { + jmsPublisher ! ("Akka rocks (%d)" format i) + } + + // Send 10 messages to JMS topic directly + for(i <- 1 to 10) { + CamelContextManager.template.sendBody(jmsUri, "Camel rocks (%d)" format i) + } + + // Graceful shutdown of all endpoints/routes + service.unload + + // Shutdown example actors + ActorRegistry.shutdownAll + } +} From 747b6014ece9a2cb298c51bcfd18240b23820c51 Mon Sep 17 00:00:00 2001 From: Martin Krasser Date: Wed, 21 Jul 2010 20:06:46 +0200 Subject: [PATCH 32/33] HTTP Producer/Consumer concurrency test (ignored by default) --- .../src/test/scala/HttpConcurrencyTest.scala | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTest.scala diff --git a/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTest.scala b/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTest.scala new file mode 100644 index 0000000000..8372304afd --- /dev/null +++ b/akka-samples/akka-sample-camel/src/test/scala/HttpConcurrencyTest.scala @@ -0,0 +1,104 @@ +package sample.camel + +import collection.mutable.Set + +import java.util.concurrent.CountDownLatch + +import org.junit._ + +import se.scalablesolutions.akka.actor.Actor._ +import se.scalablesolutions.akka.actor.{ActorRegistry, ActorRef, Actor} +import se.scalablesolutions.akka.camel.{CamelService, Message, Producer, Consumer} +import se.scalablesolutions.akka.routing.CyclicIterator +import se.scalablesolutions.akka.routing.Routing._ +import org.scalatest.junit.JUnitSuite + +/** + * @author Martin Krasser + */ +@Ignore +class HttpConcurrencyTest extends JUnitSuite { + import HttpConcurrencyTest._ + + @Test def shouldProcessMessagesConcurrently = { + val num = 50 + val latch1 = new CountDownLatch(num) + val latch2 = new CountDownLatch(num) + val latch3 = new CountDownLatch(num) + val client1 = actorOf(new HttpClientActor("client1", latch1)).start + val client2 = actorOf(new HttpClientActor("client2", latch2)).start + val client3 = actorOf(new HttpClientActor("client3", latch3)).start + for (i <- 1 to num) { + client1 ! Message("client1", Map(Message.MessageExchangeId -> i)) + client2 ! Message("client2", Map(Message.MessageExchangeId -> i)) + client3 ! Message("client3", Map(Message.MessageExchangeId -> i)) + } + latch1.await + latch2.await + latch3.await + assert(num == (client1 !! "getCorrelationIdCount").as[Int].get) + assert(num == (client2 !! "getCorrelationIdCount").as[Int].get) + assert(num == (client3 !! "getCorrelationIdCount").as[Int].get) + } +} + +object HttpConcurrencyTest { + var service: CamelService = _ + + @BeforeClass + def beforeClass = { + service = CamelService.newInstance.load + + val workers = for (i <- 1 to 8) yield actorOf[HttpServerWorker].start + val balancer = loadBalancerActor(new CyclicIterator(workers.toList)) + + val completion = service.expectEndpointActivationCount(1) + val server = actorOf(new HttpServerActor(balancer)).start + completion.await + } + + @AfterClass + def afterClass = { + service.unload + ActorRegistry.shutdownAll + } + + class HttpClientActor(label: String, latch: CountDownLatch) extends Actor with Producer { + def endpointUri = "jetty:http://0.0.0.0:8855/echo" + var correlationIds = Set[Any]() + + override protected def receive = { + case "getCorrelationIdCount" => self.reply(correlationIds.size) + case msg => super.receive(msg) + } + + override protected def receiveAfterProduce = { + case msg: Message => { + val corr = msg.headers(Message.MessageExchangeId) + val body = msg.bodyAs[String] + correlationIds += corr + assert(label == body) + latch.countDown + print(".") + } + } + } + + class HttpServerActor(balancer: ActorRef) extends Actor with Consumer { + def endpointUri = "jetty:http://0.0.0.0:8855/echo" + + def receive = { + case msg => balancer forward msg + } + } + + class HttpServerWorker extends Actor { + protected def receive = { + case msg => { + // slow processing + Thread.sleep(100) + self.reply(msg) + } + } + } +} \ No newline at end of file From 358963f3066acb55515eb31ebf2e727f03705810 Mon Sep 17 00:00:00 2001 From: Debasish Ghosh Date: Thu, 22 Jul 2010 16:57:19 +0530 Subject: [PATCH 33/33] MongoDB based persistent Maps now use Mongo updates. Also upgraded mongo-java driver to 2.0 --- .../src/main/scala/MongoStorageBackend.scala | 6 ++---- project/build/AkkaProject.scala | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/akka-persistence/akka-persistence-mongo/src/main/scala/MongoStorageBackend.scala b/akka-persistence/akka-persistence-mongo/src/main/scala/MongoStorageBackend.scala index d5581b373b..950165567d 100644 --- a/akka-persistence/akka-persistence-mongo/src/main/scala/MongoStorageBackend.scala +++ b/akka-persistence/akka-persistence-mongo/src/main/scala/MongoStorageBackend.scala @@ -78,10 +78,8 @@ private[akka] object MongoStorageBackend extends val o = dbo.get(VALUE).asInstanceOf[Map[AnyRef, AnyRef]] o.putAll(m) - // remove existing reference - removeMapStorageFor(name) - // and insert - coll.insert(new BasicDBObject().append(KEY, name).append(VALUE, o)) + val newdbo = new BasicDBObject().append(KEY, name).append(VALUE, o) + coll.update(new BasicDBObject().append(KEY, name), newdbo, true, false) } } } diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala index 6c770d4e54..4507193de9 100644 --- a/project/build/AkkaProject.scala +++ b/project/build/AkkaProject.scala @@ -162,7 +162,7 @@ class AkkaParentProject(info: ProjectInfo) extends DefaultProject(info) { lazy val log4j = "log4j" % "log4j" % "1.2.15" % "compile" - lazy val mongo = "org.mongodb" % "mongo-java-driver" % "1.4" % "compile" + lazy val mongo = "org.mongodb" % "mongo-java-driver" % "2.0" % "compile" lazy val multiverse = "org.multiverse" % "multiverse-alpha" % MULTIVERSE_VERSION % "compile" intransitive