diff --git a/.gitignore b/.gitignore index 50ecf13..2f54d56 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ webgen.cache *.i?? .idea *.swp +atlassian-ide-plugin.xml diff --git a/features/pom.xml b/features/pom.xml index ffb719e..bee661f 100644 --- a/features/pom.xml +++ b/features/pom.xml @@ -1,122 +1,120 @@ - + - + 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. + --> - - parent - org.fusesource.slang - 1.0.0-SNAPSHOT - - - 4.0.0 + + parent + org.fusesource.slang + 1.0.0-SNAPSHOT + - features - Slang :: Features + 4.0.0 - - - org.osgi - org.osgi.core - provided - - - org.apache.aries.blueprint - org.apache.aries.blueprint - provided - - - org.ops4j.pax.logging - pax-logging-api - provided - - - org.apache.felix - org.apache.felix.fileinstall - provided - - + features + Slang :: Features - - - - ${pom.basedir}/src/main/resources - true - - **/* - - - - - - org.apache.karaf.tooling - features-maven-plugin - ${karaf.version} - - - validate - process-resources - - validate - - - target/classes/features.xml - - - - - - - org.slf4j - slf4j-simple - 1.6.1 - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.7 - - - attach-artifacts - package - - attach-artifact - - - - - target/classes/features.xml - xml - features - - - - - - - - + + + org.osgi + org.osgi.core + provided + + + org.apache.aries.blueprint + org.apache.aries.blueprint + provided + + + org.ops4j.pax.logging + pax-logging-api + provided + + + org.apache.felix + org.apache.felix.fileinstall + provided + + + + + + + ${pom.basedir}/src/main/resources + true + + **/* + + + + + + org.apache.karaf.tooling + features-maven-plugin + ${karaf.version} + + + validate + process-resources + + validate + + + target/classes/features.xml + + + + + + + org.slf4j + slf4j-simple + 1.6.1 + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + attach-artifacts + package + + attach-artifact + + + + + target/classes/features.xml + xml + features + + + + + + + + diff --git a/pom.xml b/pom.xml index 2fd182b..c4d3e31 100644 --- a/pom.xml +++ b/pom.xml @@ -1,142 +1,173 @@ - - - 4.0.0 - - org.fusesource.slang - parent - pom - 1.0.0-SNAPSHOT - Slang - 2010 - - - 0.3.1 - [$(version;==;$(@)),$(version;+;$(@))) - 3.1.10 - 1.4.0 - 2.3.6 - 4.8.1 - 2.2.5 - 4.2.0 - 1.2.0 - 1.6.3 - 1.2.0 - 2.9.1 - - UTF-8 - - - - scala - features - - - - install - - - org.apache.felix - maven-bundle-plugin - ${felix.plugin.version} - true - - - org.apache.maven.plugins - maven-resources-plugin - 2.5 - - - - - - - - org.apache.felix - org.osgi.core - ${felix.osgi.version} - provided - - - org.ops4j.pax.logging - pax-logging-api - ${pax.logging.version} - provided - - - org.apache.felix - org.apache.felix.fileinstall - ${felix.fileinstall.version} - provided - - - org.ops4j.pax.swissbox - pax-swissbox-bnd - 1.2.0 - - - org.osgi - org.osgi.core - ${osgi.version} - - - org.apache.aries.blueprint - org.apache.aries.blueprint - ${aries.blueprint.version} - - - org.apache.felix - javax.servlet - - - - - - - junit - junit - ${junit.version} - test - - - org.ops4j.pax.exam - pax-exam - ${pax.exam.version} - test - - - org.ops4j.pax.exam - pax-exam-container-default - ${pax.exam.version} - test - - - org.ops4j.pax.exam - pax-exam-junit - ${pax.exam.version} - test - - - + + + 4.0.0 + + org.fusesource.slang + parent + pom + 1.0.0-SNAPSHOT + Slang + 2010 + + + 0.3.1 + [$(version;==;$(@)),$(version;+;$(@))) + 3.1.10 + 1.4.0 + 2.3.6 + 4.8.1 + 2.2.5 + 4.2.0 + 1.2.0 + 1.6.3 + 1.2.0 + 2.9.1 + + UTF-8 + + + + scala + features + + + + scm:git:git@wiki.crossing-tech.com:slang.git + scm:git:git@wiki.crossing-tech.com:slang.git + + + + install + + + org.apache.felix + maven-bundle-plugin + ${felix.plugin.version} + true + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + + + + maven-release-plugin + 2.2.2 + + true + + + + + + + + + + org.apache.felix + org.osgi.core + ${felix.osgi.version} + provided + + + org.ops4j.pax.logging + pax-logging-api + ${pax.logging.version} + provided + + + org.apache.felix + org.apache.felix.fileinstall + ${felix.fileinstall.version} + provided + + + org.ops4j.pax.swissbox + pax-swissbox-bnd + 1.2.0 + + + org.osgi + org.osgi.core + ${osgi.version} + + + org.apache.aries.blueprint + org.apache.aries.blueprint + ${aries.blueprint.version} + + + org.apache.felix + javax.servlet + + + + + + + junit + junit + ${junit.version} + test + + + org.ops4j.pax.exam + pax-exam + ${pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-container-default + ${pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-junit + ${pax.exam.version} + test + + + + + + + + nexusXtech + Nexus + https://wiki.crossing-tech.com/nexus/content/repositories/releases + + + + nexusXtech + Nexus + https://wiki.crossing-tech.com/nexus/content/repositories/snapshots + + diff --git a/scala/common/pom.xml b/scala/common/pom.xml index 87f8bcb..e617b62 100644 --- a/scala/common/pom.xml +++ b/scala/common/pom.xml @@ -1,69 +1,67 @@ - + - + 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. + --> - - scala - org.fusesource.slang - 1.0.0-SNAPSHOT - + + scala + org.fusesource.slang + 1.0.0-SNAPSHOT + - 4.0.0 + 4.0.0 - org.fusesource.slang.scala - org.fusesource.slang.scala.common - bundle - - Slang :: Scala :: Common + org.fusesource.slang.scala + org.fusesource.slang.scala.common + bundle - - - - org.apache.felix - org.osgi.core - - + Slang :: Scala :: Common - - - - org.apache.felix - maven-bundle-plugin - - - ${project.artifactId} - ${project.artifactId}*;version=${project.version} - - !${project.artifactId}*, - * - - <_versionpolicy>${bnd.version.policy} - - - - - - + + + + org.apache.felix + org.osgi.core + + - \ No newline at end of file + + + + org.apache.felix + maven-bundle-plugin + + + ${project.artifactId} + ${project.artifactId}*;version=${project.version} + + !${project.artifactId}*, + * + + <_versionpolicy>${bnd.version.policy} + + + + + + + + diff --git a/scala/deployer/pom.xml b/scala/deployer/pom.xml index f038416..6dec21f 100644 --- a/scala/deployer/pom.xml +++ b/scala/deployer/pom.xml @@ -1,120 +1,119 @@ - + - + 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. + --> - 4.0.0 + 4.0.0 - - scala - org.fusesource.slang - 1.0.0-SNAPSHOT - + + scala + org.fusesource.slang + 1.0.0-SNAPSHOT + - org.fusesource.slang.scala - org.fusesource.slang.scala.deployer - bundle + org.fusesource.slang.scala + org.fusesource.slang.scala.deployer + bundle - Slang :: Scala :: Deployer + Slang :: Scala :: Deployer - - - org.apache.felix - org.osgi.core - provided - - - org.scala-lang - scala-library - - - org.ops4j.pax.logging - pax-logging-api - - - org.apache.felix - org.apache.felix.fileinstall - - - org.ops4j.pax.swissbox - pax-swissbox-bnd - true - - - junit - junit - - + + + org.apache.felix + org.osgi.core + provided + + + org.scala-lang + scala-library + + + org.ops4j.pax.logging + pax-logging-api + + + org.apache.felix + org.apache.felix.fileinstall + + + org.ops4j.pax.swissbox + pax-swissbox-bnd + true + + + junit + junit + + - - - - org.apache.maven.plugins - maven-eclipse-plugin - 2.9 - - true - - ch.epfl.lamp.sdt.core.scalabuilder - - - ch.epfl.lamp.sdt.core.scalanature - - - org.eclipse.jdt.launching.JRE_CONTAINER - ch.epfl.lamp.sdt.launching.SCALA_CONTAINER - - - - - org.apache.felix - maven-bundle-plugin - - - - ${project.artifactId};blueprint.graceperiod:=false - ${project.artifactId}*;version=${project.version} - - !${project.artifactId}*, - org.apache.commons.logging;version="[1.1, 1.2)", - * - - <_versionpolicy>${bnd.version.policy} - pax-swissbox-bnd,bndlib,ops4j-base-lang - true - - - - - + + + + org.apache.maven.plugins + maven-eclipse-plugin + 2.9 + + true + + ch.epfl.lamp.sdt.core.scalabuilder + + + ch.epfl.lamp.sdt.core.scalanature + + + org.eclipse.jdt.launching.JRE_CONTAINER + ch.epfl.lamp.sdt.launching.SCALA_CONTAINER + + + + + org.apache.felix + maven-bundle-plugin + + + + ${project.artifactId};blueprint.graceperiod:=false + ${project.artifactId}*;version=${project.version} + + !${project.artifactId}*, + org.apache.commons.logging;version="[1.1, 1.2)", + * + + <_versionpolicy>${bnd.version.policy} + pax-swissbox-bnd,bndlib,ops4j-base-lang + true + + + + + + + + + + org.scala-tools + maven-scala-plugin + 2.15.2 + + + - - - - org.scala-tools - maven-scala-plugin - 2.15.2 - - - - diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaDeploymentListener.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaDeploymentListener.scala index ed8aabb..279ca9f 100644 --- a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaDeploymentListener.scala +++ b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaDeploymentListener.scala @@ -2,6 +2,9 @@ * Copyright (C) FuseSource, Inc. * http://fusesource.com * + * Copyright (C) Crossing-Tech SA, 2012. + * Contact: + * * 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 @@ -29,18 +32,9 @@ class ScalaDeploymentListener extends ArtifactUrlTransformer { val LOG = LogFactory.getLog(classOf[ScalaDeploymentListener]) - def canHandle(artifact: File) = { - artifact.isFile() && artifact.getName().endsWith(".scala") - } + def canHandle(artifact: File) = artifact.isFile && artifact.getName.endsWith(".scala") - def transform(artifact: URL) : URL = { - try { - new URL("scala", null, artifact.toString()); - } catch { - case e: Exception => { - LOG.error("Unable to build scala bundle", e); - return null; - } - } + def transform(artifact: URL) : URL = try new URL("scala", null, artifact.toString) catch { + case e: Exception => LOG.error("Unable to build scala bundle", e); null } -} \ No newline at end of file +} diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaSource.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaSource.scala new file mode 100644 index 0000000..d0a5dde --- /dev/null +++ b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaSource.scala @@ -0,0 +1,166 @@ +/** + * Copyright (C) Crossing-Tech SA, 2012. + * Contact: + * + * Copyright (C) FuseSource, Inc. + * http://fusesource.com + * + * 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 org.fusesource.slang.scala.deployer + +import java.io._ +import java.net.URL +import org.apache.commons.logging.LogFactory +import org.osgi.framework.BundleContext +import tools.nsc.io.AbstractFile +import tools.nsc.io.PlainFile +import compiler._ +import archiver.ScalaArchiver + +class ScalaSource(val url: URL, val libraries: List[AbstractFile]) extends AbstractFile { + + final val LOG = LogFactory.getLog(classOf[ScalaSource]) + + require(url != null, "Invalid URL for ScalaSource constructor.") + require((libraries != null) && libraries.forall(_ != null), "Invalid libraries for ScalaSource constructor.") + + def this(url: URL, context: BundleContext) = this (url, { + require(context != null, "No BundleContext available to search for OSGI bundles.") + val framework = context.getProperty("karaf.framework") + val jar = new File(context.getProperty("karaf.base"), context.getProperty("karaf.framework." + framework)) + AbstractFile.getDirectory(jar) :: Bundles.create(context.getBundles) + }) + + /**********************************************************************/ + + /* It should be noted that the Scala compiler has the following piece of + code to read so-called AbstractFiles. See SourceReader.scala, lines + 48 to 59 in the compiler source code. + + def read(file: AbstractFile): Array[Char] = { + try file match { + case p: PlainFile => read(p.file) + case z: ZipArchive#Entry => read(Channels.newChannel(z.input)) + case _ => read(ByteBuffer.wrap(file.toByteArray)) + } + catch { + case e: Exception => reportEncodingError("" + file) ; Array() + } + } + + The big problem about this pattern-matching is that the principle of + a common interface using the "toByteArray" method is invalidated. In + our specific case, this means that it useless to override the input(), + toCharArray() or toByteArray() methods to intercept and rewrite on the + fly what is read from the file. Such a situation is not satisfactory. + + We therefore chose not to inherit from the PlainFile, but to prefer + composition over inheritance. Which is the reason why we have a plainFile + field: fooling this pattern-matching. */ + + private val plainFile: PlainFile = new PlainFile(new File(url.toURI)) + + override def name: String = plainFile.name + override def path: String = plainFile.path + override def absolute: AbstractFile = plainFile.absolute + override def container: AbstractFile = plainFile.container + override def file: File = plainFile.file + override def create() {plainFile.create()} + override def delete() {plainFile.delete()} + override def isDirectory: Boolean = plainFile.isDirectory + override def lastModified: Long = plainFile.lastModified + override def input: InputStream = plainFile.input + override def output: OutputStream = plainFile.output + override def iterator: Iterator[AbstractFile] = plainFile.iterator + override def lookupName(name: String, directory: Boolean): AbstractFile = + plainFile.lookupName(name, directory) + override def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = + plainFile.lookupNameUnchecked(name, directory) + + override def toString() = + /* This call needs to be delegated to ensure that the referenced file is + named properly. The implementation in PlainFile removes the file: URI + prefix that is fed to a ScalaSource instance at construct-time. */ + plainFile.toString() + + /* The two following items need special care, since this is the IO interface + between ScalaSource files and the embedded compiler. + */ + + override def toByteArray: Array[Byte] = + /* NOTE: If the sizeOption is not properly overriden, the AbstractFile + default implementation of sizeOption will blow up. Hence the 'catch'. + + These composition vs. inheritance bugs are hard to find. In case this + happens, continue delegating calls to plainFile. */ + try super.toByteArray catch { + case e: java.util.NoSuchElementException => + val exc = new RuntimeException( + "Likely an implementation error: sizeOption in ScalaSource is not properly overriden.", e) + report(exc) + throw exc + } + + override def sizeOption = plainFile.sizeOption + + + /**********************************************************************/ + + private var exception: Option[Exception] = None + + private def report(e: Exception) { + exception match { + case None => exception = Some(e) + case Some(_) => {} + } + } + + def compile(): AbstractFile = { + LOG.debug("Compiling " + this + " using embedded Scala compiler.") + try { + exception = None + (new ScalaCompiler(libraries)).compile(this) + } catch { + case e: ScalaCompileFailure => + exception match { + case None => + // TODO: We here pass the same libs as the AbstractFile. Would + // be nicer to trim down to just what is the needed for the + // error-reporting bundle. But, oh well... + (new ScalaCompiler(libraries)).compile(new ScrewedSource(this)) + case Some(exc) => throw exc + } + } + } + + private def archive(dir: AbstractFile) = { + LOG.debug("Archiving compiled " + this + " into an OSGi bundle.") + (new ScalaArchiver(libraries)).archive(dir, this) + } + + def transform() = { + LOG.info("Transforming " + this + " into an OSGi bundle.") + // try { manifest() } catch {case e : Throwable => e.printStackTrace(); throw e} // TODO: Not robust enough yet. + archive(compile()) + } + + def manifest() { + + val source = io.Source.fromInputStream(input).getLines().mkString("\n") + + import parser.ScriptParser._ + val c: ParseResult[List[parser.Item]] = parse(source) + /* TODO: Extract manifest from this abstract file. */ + } +} diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaTransformer.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaTransformer.scala deleted file mode 100644 index 4cbef91..0000000 --- a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaTransformer.scala +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (C) FuseSource, Inc. - * http://fusesource.com - * - * 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 org.fusesource.slang.scala.deployer - -import archiver.ScalaArchiver -import compiler.{Bundles, ScalaCompiler} -import org.apache.commons.logging.LogFactory -import tools.nsc.io.{PlainFile, AbstractFile} -import java.net.URL -import org.osgi.framework.{BundleContext, Bundle} -import java.io.{InputStream, File, OutputStream} - -/** - * - */ -class ScalaTransformer(val bundles: List[AbstractFile]) { - - final val LOG = LogFactory.getLog(classOf[ScalaTransformer]) - - val compiler = new ScalaCompiler(bundles) - - val archiver = new ScalaArchiver(bundles) - - def transform(url: URL, stream: OutputStream) : Unit = { - val result = transform(url) - val bytes = new Array[Byte](1024) - var read = result.read(bytes) - while (read > 0) { - stream.write(bytes, 0, read) - read = result.read(bytes) - } - result.close - } - - def transform(url: URL) : InputStream = { - LOG.info("Transforming " + url + " into an OSGi bundle") - archiver.archive(compile(url), url) - } - - def compile(url: URL) = compiler.compile(files(url)) - - def files(url: URL) : List[AbstractFile] = { - if ("file" == url.getProtocol) { - List(new PlainFile(new File(url.toURI))) - } else { - List(AbstractFile.getURL(url)) - } - } -} - -object ScalaTransformer { - - def create(context: BundleContext) = { - val bundles : List[AbstractFile] = if (context == null) { - List() - } else { - val framework = context.getProperty("karaf.framework") - val jar = new File(context.getProperty("karaf.base"), context.getProperty("karaf.framework." + framework)) - AbstractFile.getDirectory(jar) :: Bundles.create(context.getBundles) - } - new ScalaTransformer(bundles) - } - - def create(libraries: List[AbstractFile]) = new ScalaTransformer(libraries) - -} \ No newline at end of file diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaURLHandler.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaURLHandler.scala index 5afab69..0854751 100644 --- a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaURLHandler.scala +++ b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScalaURLHandler.scala @@ -19,33 +19,24 @@ */ package org.fusesource.slang.scala.deployer -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.IOException -import java.io.InputStream -import java.net.MalformedURLException -import java.net.URL -import java.net.URLConnection -import org.apache.commons.logging.Log -import org.apache.commons.logging.LogFactory -import org.osgi.service.url.AbstractURLStreamHandlerService import reflect.BeanProperty +import java.io._ +import java.net._ +import org.apache.commons.logging._ +import org.osgi.service.url.AbstractURLStreamHandlerService import org.osgi.framework.BundleContext -import org.ops4j.pax.swissbox.bnd.BndUtils.createBundle -import java.util.Properties -import org.ops4j.pax.swissbox.bnd.OverwriteMode.MERGE /** * A URL handler that will transform a Scala source file into an OSGi bundle * on the fly. Needs to be registered in the OSGi registry. */ class ScalaURLHandler extends AbstractURLStreamHandlerService { - - private var LOG: Log = LogFactory.getLog(classOf[ScalaURLHandler]) - private var PREFIX: String = "scala:" - private var SYNTAX: String = PREFIX + "" - @BeanProperty var bundleContext : BundleContext = null + private val LOG: Log = LogFactory.getLog(classOf[ScalaURLHandler]) + private val PREFIX: String = "scala:" + private val SYNTAX: String = PREFIX + "" + + @BeanProperty var bundleContext: BundleContext = null /** * Open the connection for the given URL. @@ -55,9 +46,8 @@ class ScalaURLHandler extends AbstractURLStreamHandlerService { * @throws IOException if an error occurs or if the URL is malformed. */ def openConnection(url: URL): URLConnection = { - if (url.getPath == null || url.getPath.trim.length == 0) { + if (url.getPath == null || url.getPath.trim.length == 0) throw new MalformedURLException("Path can not be null or empty. Syntax: " + SYNTAX) - } LOG.debug("Scala source URL is: [" + url.getPath + "]") new Connection(url) } @@ -66,24 +56,20 @@ class ScalaURLHandler extends AbstractURLStreamHandlerService { override def getInputStream: InputStream = { try { - val url = if (source.toExternalForm.startsWith(PREFIX)) { - new URL(source.toExternalForm.substring(PREFIX.length)) - } else { - source - } + val url = + if (source.toExternalForm.startsWith(PREFIX)) + new URL(source.toExternalForm.substring(PREFIX.length)) + else source - ScalaTransformer.create(bundleContext).transform(url) - - - } - catch { - case e: Exception => { + new ScalaSource(url, bundleContext).transform() + } catch { + case e: Exception => LOG.error("Error creating bundle from Scala source code", e) - throw new IOException("Error opening spring xml url").initCause(e).asInstanceOf[IOException] - } + throw e } } - def connect {} + def connect() {} } + } diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScrewedSource.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScrewedSource.scala new file mode 100644 index 0000000..a6e4bb8 --- /dev/null +++ b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/ScrewedSource.scala @@ -0,0 +1,78 @@ +/** + * Copyright (C) Crossing-Tech SA, 2012. + * Contact: + * + * 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 org.fusesource.slang.scala.deployer + +import java.io._ +import org.apache.commons.logging.LogFactory +import tools.nsc.io.AbstractFile + +class ScrewedSource(var f: AbstractFile) extends AbstractFile { + + final val LOG = LogFactory.getLog(classOf[ScrewedSource]) + + LOG.debug("ScrewedSource for " + f.path) + + def fail(msg: String): Nothing = { + val s = "ScrewedSource: " + msg + " unimplemented." + LOG.debug(s) + throw new Exception(s) + } + + override def lookupNameUnchecked(name: String, directory: Boolean) = fail("lookupNameUnchecked") + override def lookupName(name: String, directory: Boolean) = fail("lookupName") + override def iterator = fail("iterator") + override def output = fail("output") + + /* sizeOption needs to be overriden to provide the length of the input stream. + It is NOT declared abstract in AbstractFile, which leads to weird IO errors + if this override is not properly made. + */ + override def sizeOption = Some(code.length) + + def code = """ + + import org.osgi.framework.{BundleContext, BundleActivator} + + package org.fusesource.slang.scala.deployer.failure { + + class MyActivator extends BundleActivator { + + def start (context : BundleContext) { throw new Exception ("Failed to compile Scala code") } + + def stop (context : BundleContext) { } + + } + + } + + """.getBytes("UTF-8") + // TODO: Check if global.settings.encoding.value is not better. + // see line 77 of AbstractFile.scala in the compiler's source code. + + override def input = new ByteArrayInputStream(code) + override def lastModified = fail("lastModified") + override def isDirectory = fail("isDirectory") + override def delete() {fail("delete")} + override def create() {fail("create")} + override def file = fail("file") + override def container = fail("container") + override def absolute = fail("absolute") + + override def path = f.path + override def name = f.name + +} diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/archiver/ScalaArchiver.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/archiver/ScalaArchiver.scala index ca18698..f042335 100644 --- a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/archiver/ScalaArchiver.scala +++ b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/archiver/ScalaArchiver.scala @@ -2,11 +2,14 @@ * Copyright (C) FuseSource, Inc. * http://fusesource.com * + * Copyright (C) Crossing-Tech SA, 2012. + * Contact: + * * 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 + * 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, @@ -16,17 +19,16 @@ */ package org.fusesource.slang.scala.deployer.archiver +import java.io._ +import java.net.URL +import java.util.Properties +import java.util.jar._ +import org.apache.commons.logging.LogFactory import tools.nsc.io.AbstractFile import tools.nsc.interpreter.AbstractFileClassLoader import org.osgi.framework.BundleActivator -import java.util.jar.JarFile.MANIFEST_NAME -import java.util.jar.{Attributes, Manifest, JarEntry, JarOutputStream} -import java.io.{ByteArrayOutputStream, ByteArrayInputStream, InputStream, OutputStream} import org.ops4j.pax.swissbox.bnd.BndUtils.createBundle - -import java.util.Properties -import org.apache.commons.logging.LogFactory -import java.net.URL +import org.fusesource.slang.scala.deployer.ScalaSource /** * Helper class that stores the contents of a Scala compile {@link AbstractFile} @@ -34,53 +36,53 @@ import java.net.URL */ class ScalaArchiver(bundles: List[AbstractFile]) { - val LOG = LogFactory.getLog(classOf[ScalaArchiver]) + private val LOG = LogFactory.getLog(classOf[ScalaArchiver]) - val classloaders = bundles.map(new AbstractFileClassLoader(_, getClass.getClassLoader)) + // classloaders contains a classloader supposed to be able to load classes + // from a given bundles. In fact, one classloader for each bundle. + private val classloaders = bundles.map(new AbstractFileClassLoader(_, getClass.getClassLoader)) - def archive(dir: AbstractFile, url: URL) : InputStream = { - val classloader = createClassLoader(dir) + def archive(dir: AbstractFile, source: ScalaSource): InputStream = { + + val classloader: AbstractFileClassLoader = createClassLoader(dir) val props = new Properties val bytes = new ByteArrayOutputStream val jar = new JarOutputStream(bytes) - entries(dir) { (name: String, file: AbstractFile) => - archiveFile(file, jar, name) - try { - val theType = classloader.loadClass(name.replaceAll(".class", "").replaceAll("/", ".")) - if (classOf[BundleActivator].isAssignableFrom(theType)) { - LOG.debug("Discovered bundle activator " + theType.getName) - props.put("Bundle-Activator", theType.getName) - } - } catch { - case e: Exception => e.printStackTrace - } - + entries(dir) { + (name: String, file: AbstractFile) => + archiveFile(file, jar, name) + try { + val renaming = name.replaceAll(".class", "").replaceAll("/", ".") + // TODO: Name processing is a bit crude... should be cleaned up. + val theType = classloader.loadClass(renaming) + if (classOf[BundleActivator].isAssignableFrom(theType)) { + LOG.debug("Discovered bundle activator " + theType.getName) + props.put("Bundle-Activator", theType.getName) + } + } // catch {case e: Exception => e.printStackTrace()} } - jar.close - bytes.close + jar.close() + bytes.close() - createBundle(new ByteArrayInputStream(bytes.toByteArray), - props, bsn(url)) + createBundle(new ByteArrayInputStream(bytes.toByteArray), props, bsn(source.url)) } - - def entries(dir: AbstractFile)(action: (String, AbstractFile) => Unit) : Unit = + + private def entries(dir: AbstractFile)(action: (String, AbstractFile) => Unit) { entries(dir, "")(action) + } - def entries(dir: AbstractFile, path:String)(action: (String, AbstractFile) => Unit) : Unit = { - dir.foreach { (file: AbstractFile) => - val name = if (path.length == 0) { file.name } else { path + "/" + file.name } - if (file.isDirectory) { - entries(file, name)(action) - } else { - action(name, file) - } - } + private def entries(dir: AbstractFile, path: String)(action: (String, AbstractFile) => Unit) { + dir.foreach { + (file: AbstractFile) => + val name = if (path.length == 0) file.name else path + "/" + file.name + if (file.isDirectory) entries(file, name) { action } else action(name, file) + } } - def archiveFile(file: AbstractFile, jar: JarOutputStream, name: String) = { + private def archiveFile(file: AbstractFile, jar: JarOutputStream, name: String) = { val entry = new JarEntry(name) jar.putNextEntry(entry) val bytes = new Array[Byte](1024) @@ -90,10 +92,10 @@ class ScalaArchiver(bundles: List[AbstractFile]) { jar.write(bytes, 0, read) read = is.read(bytes) } - jar.closeEntry + jar.closeEntry() } - def bsn(url: URL) = { + private def bsn(url: URL) = { var result = url.getPath if (result.endsWith(".scala")) { result = result.substring(0, result.length - 6) @@ -101,32 +103,36 @@ class ScalaArchiver(bundles: List[AbstractFile]) { if (result.startsWith("/")) { result = result.substring(1) } - result.replaceAll("/", ".") + result.replaceAll("/", ".") } - def createClassLoader(dir: AbstractFile) = new AbstractFileClassLoader(dir, getClass.getClassLoader) { - override def findClass(name: String) : Class[_] = { - try { - // let's try the bundle we're generating first - super.findClass(name) - } catch { + private def createClassLoader(dir: AbstractFile) = new AbstractFileClassLoader(dir, getClass.getClassLoader) { + + // Set to true to trace classloader activity. + //override protected def trace = true + + override def findClass(name: String): Class[_] = try { + // let's try the bundle we're generating first + super.findClass(name) + } catch { + case e: ClassNotFoundException => // and then fall back to the rest of the bundles - case e: ClassNotFoundException => findClassInBundles(name) - } + findClassInBundles(name) } - def findClassInBundles(name: String) : Class[_] = { + def findClassInBundles(name: String): Class[_] = classloaders.map(cl => findClass(name, cl)).find(cls => cls.isDefined) match { case Some(cls) => cls.get case None => throw new ClassNotFoundException(name) } - } def findClass(name: String, loader: AbstractFileClassLoader) = - try { - Some(loader.findClass(name)) - } catch { - case e: ClassNotFoundException => None + try Some(loader.findClass(name)) catch { + case e: /* ClassNotFoundException */ Exception => + /* TODO: Original code was catching the ClassNotFoundException, + and an inadequate exception was falling through. So I widened + the range of caught exceptions, but this is only a workaround. */ + None } } } diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/Bundles.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/Bundles.scala index 8cb0946..356116c 100644 --- a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/Bundles.scala +++ b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/Bundles.scala @@ -9,7 +9,7 @@ * 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 + * 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, @@ -19,12 +19,12 @@ */ package org.fusesource.slang.scala.deployer.compiler -import org.osgi.framework.Bundle -import java.io.{InputStream, IOException, File} -import scala.tools.nsc.io.{PlainFile, AbstractFile} -import java.net.{URISyntaxException, URL} +import annotation.tailrec +import java.io._ +import java.net._ import org.apache.commons.logging.LogFactory -import java.lang.String +import org.osgi.framework.Bundle +import tools.nsc.io.{AbstractFile, PlainFile} /** * Helper methods to transform OSGi bundles into {@link AbstractFile} implementations @@ -37,28 +37,19 @@ object Bundles { abstract class BundleEntry(url: URL, parent: DirEntry) extends AbstractFile { require(url != null, "url must not be null") - val bundle : Bundle + val bundle: Bundle lazy val (path: String, name: String) = getPathAndName(url) - lazy val fullName: String = (path::name::Nil).filter(!_.isEmpty).mkString("/") + lazy val fullName: String = (path :: name :: Nil).filter(!_.isEmpty).mkString("/") - /** - * @return null - */ def file: File = null - /** - * @return last modification time or 0 if not known - */ - def lastModified: Long = - try { url.openConnection.getLastModified } - catch { case _ => 0 } + def lastModified: Long = try url.openConnection.getLastModified catch { + case _ => 0 + } @throws(classOf[IOException]) - def container: AbstractFile = - valueOrElse(parent) { - throw new IOException("No container") - } + def container: AbstractFile = Option(parent).getOrElse(throw new IOException("No container")) @throws(classOf[IOException]) def input: InputStream = url.openStream() @@ -73,186 +64,141 @@ object Bundles { private def getPathAndName(url: URL): (String, String) = { val u = url.getPath var k = u.length - while( (k > 0) && (u(k - 1) == '/') ) + while ((k > 0) && (u(k - 1) == '/')) k = k - 1 var j = k - while( (j > 0) && (u(j - 1) != '/') ) + while ((j > 0) && (u(j - 1) != '/')) j = j - 1 (u.substring(if (j > 0) 1 else 0, if (j > 1) j - 1 else j), u.substring(j, k)) } - override def toString = fullName + override def toString() = fullName } - class DirEntry(val bundle: Bundle, url: URL, parent: DirEntry) extends BundleEntry(url, parent) { + class DirEntry(val bundle: Bundle, bundleUrl: URL, parent: DirEntry) extends BundleEntry(bundleUrl, parent) { - /** - * @return true - */ def isDirectory: Boolean = true override def elements: Iterator[AbstractFile] = { - new Iterator[AbstractFile]() { - val dirs = bundle.getEntryPaths(fullName) - var nextEntry = prefetch() - - def hasNext() = { - if (nextEntry == null) - nextEntry = prefetch() - - nextEntry != null - } - - def next() = { - if (hasNext()) { - val entry = nextEntry - nextEntry = null - entry - } - else { - throw new NoSuchElementException() - } - } - - private def prefetch() = { - if (dirs.hasMoreElements) { - val entry = dirs.nextElement.asInstanceOf[String] - var entryUrl = bundle.getResource("/" + entry) - - // Bundle.getResource seems to be inconsistent with respect to requiring - // a trailing slash - if (entryUrl == null) - entryUrl = bundle.getResource("/" + removeTralingSlash(entry)) - - // If still null OSGi wont let use load that resource for some reason - if (entryUrl == null) { - null - } - else { - if (entry.endsWith(".class")) - new FileEntry(bundle, entryUrl, DirEntry.this) - else - new DirEntry(bundle, entryUrl, DirEntry.this) - } + + @tailrec + def removeTrailingSlash(s: String): String = + if (s == null || s.length == 0) s + else if (s.last == '/') removeTrailingSlash(s.substring(0, s.length - 1)) + else s + + import scala.collection.JavaConverters._ + bundle.getEntryPaths(fullName).asScala.map { + case entry: String => + val entryUrl = Option(Option(bundle.getResource("/" + entry)). + // Bundle.getReource seems to be inconsistent with respect to requiring + // a trailing slash. + getOrElse(Option(bundle.getResource("/" + removeTrailingSlash(entry))). + // If the bundle is a xml file like activemq.xml, the entry META-INF cannot be reached so return the root URL + getOrElse(bundle.getResource("/")))) + entryUrl match { + case None => + throw new IllegalStateException("For some reason, OSGi will not let use the entry " + entry + " of bundleID " + bundle.getBundleId) + case Some(url) => + if (entry.endsWith(".class")) new FileEntry(bundle, url, DirEntry.this) + else new DirEntry(bundle, url, DirEntry.this) } - else - null - } - - private def removeTralingSlash(s: String): String = - if (s == null || s.length == 0) - s - else if (s.last == '/') - removeTralingSlash(s.substring(0, s.length - 1)) - else - s + case _ => + throw new ClassCastException("Items other than Strings found in an OSGi bundle's entry paths.") } } def lookupName(name: String, directory: Boolean): AbstractFile = { val entry = bundle.getEntry(fullName + "/" + name) - nullOrElse(entry) { entry => - if (directory) + Option(entry) match { + case None => null + case Some(_) if directory => new DirEntry(bundle, entry, DirEntry.this) - else + case Some(_) => new FileEntry(bundle, entry, DirEntry.this) } } override def lookupPathUnchecked(path: String, directory: Boolean) = lookupPath(path, directory) + def lookupNameUnchecked(name: String, directory: Boolean) = lookupName(path, directory) def iterator = elements def absolute = unsupported("absolute() is unsupported") - def create = unsupported("create() is unsupported") - def delete = unsupported("create() is unsupported") + + def create() { + unsupported("create() is unsupported") + } + + def delete() { + unsupported("create() is unsupported") + } } class FileEntry(val bundle: Bundle, url: URL, parent: DirEntry) extends BundleEntry(url, parent) { - /** - * @return false - */ def isDirectory: Boolean = false - override def sizeOption: Option[Int] = Some(bundle.getEntry(fullName).openConnection().getContentLength()) + + override def sizeOption: Option[Int] = Some(bundle.getEntry(fullName).openConnection().getContentLength) + override def elements: Iterator[AbstractFile] = Iterator.empty + def lookupName(name: String, directory: Boolean): AbstractFile = null override def lookupPathUnchecked(path: String, directory: Boolean) = lookupPath(path, directory) + def lookupNameUnchecked(name: String, directory: Boolean) = lookupName(path, directory) def iterator = elements def absolute = unsupported("absolute() is unsupported") - def create = unsupported("create() is unsupported") - def delete = unsupported("create() is unsupported") + + def create() { + unsupported("create() is unsupported") + } + + def delete() { + unsupported("create() is unsupported") + } } /** * Create an array of {@link AbstractFile}s for a given array of bundles. * If a bundle has a file: URL, a {@PlainFile} is being used, otherwise w */ - def create(bundles: Array[Bundle]) : List[AbstractFile] = { - var result : List[AbstractFile] = List() + def create(bundles: Array[Bundle]): List[AbstractFile] = { + var result: List[AbstractFile] = List() for (bundle <- bundles; val index = bundles.indexOf(bundle)) { - var url = bundle.getResource("/"); - if (url == null) { - url = bundle.getResource(""); - } - - if (url != null) { - if ("file" == url.getProtocol()) { - try { - result = new PlainFile(new File(url.toURI())) :: result; - } - catch { - case e: URISyntaxException => throw new IllegalArgumentException("Can't determine url of bundle " + bundle, e); - } - } - else { - result = Bundles.create(bundle) :: result; - } - } - else { - LOG.warn("Cannot retreive resources from Bundle. Skipping " + bundle.getSymbolicName()); - } + var url = bundle.getResource("/") + if (url == null) url = bundle.getResource("") + + if (url != null) { + if ("file" == url.getProtocol) { + try result = new PlainFile(new File(url.toURI)) :: result + catch { + case e: URISyntaxException => + throw new IllegalArgumentException("Can't determine url of bundle " + bundle, e) + } + } else result = Bundles.create(bundle) :: result + } + else LOG.warn("Cannot retreive resources from Bundle. Skipping " + bundle.getSymbolicName); } - result; + result } /** - * Create a new { @link AbstractFile } instance representing an + * Create a new { @link AbstractFile } instance representing an * { @link org.osgi.framework.Bundle } * * @param bundle the bundle */ def create(bundle: Bundle): AbstractFile = { require(bundle != null, "bundle must not be null") - new DirEntry(bundle, bundle.getResource("/"), null) } - - /** - * Evaluate f on s if s is not null. - * @param s - * @param f - * @return f(s) if s is not null, null otherwise. - */ - def nullOrElse[S, T](s: S)(f: S => T): T = - if (s == null) null.asInstanceOf[T] - else f(s) - - /** - * @param t - * @param default - * @return t or default if null. - */ - def valueOrElse[T](t: T)(default: => T) = - if (t == null) default - else t } diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/ScalaCompiler.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/ScalaCompiler.scala index aa37a49..35ab65b 100644 --- a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/ScalaCompiler.scala +++ b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/ScalaCompiler.scala @@ -19,12 +19,14 @@ */ package org.fusesource.slang.scala.deployer.compiler -import tools.nsc.reporters.{AbstractReporter, Reporter} +import tools.nsc.reporters._ import org.apache.commons.logging.LogFactory -import tools.nsc.io.{VirtualDirectory, PlainFile, AbstractFile} -import tools.nsc.{Interpreter, Global, Settings} +import tools.nsc.io.{VirtualDirectory, AbstractFile} +import tools.nsc.{Global, Settings} import tools.nsc.util._ +class ScalaCompileFailure (path : String) extends Exception ("Slang Scala compiler failed to compile " + path) + /** * Scala compiler that uses a provided list of bundles as the compiler * classpath @@ -33,12 +35,12 @@ class ScalaCompiler(bundles: List[AbstractFile]) { final val LOG = LogFactory.getLog(classOf[ScalaCompiler]) - def compile(sources: List[AbstractFile]) : AbstractFile = { - LOG.info("Compiling " + sources) + def compile(source: AbstractFile) : AbstractFile = { + LOG.debug("Compiling " + source) val dir = new VirtualDirectory("memory", None) settings.outputDirs.setSingleOutput(dir) - settings.verbose.value = true + settings.verbose.value = false settings.debug.value = false /* Set to true for logging debugging info. */ settings.Ylogcp.value = false /* Set to true for classpath informations. */ /* Yno-predefs and Yno-imports may be useful */ @@ -59,10 +61,18 @@ class ScalaCompiler(bundles: List[AbstractFile]) { stack trace to stdout. */ val run = try { new compiler.Run } catch {case e : Throwable => LOG.debug ("Failed to instantiate internal compiler.Run framework: " + e.getMessage) - e.printStackTrace() + //e.printStackTrace() throw e } - run.compileFiles(sources) + + /* TODO: We are using the compiler in this bundle, and we access stateful + things, such as reports. Even though, at first glance, things seem local + enough not to worry about threads, we should worry about synchronising + access to the reporter. Even more than that: Accessing the compiler + concurrently inherently seems to be a bad idea. */ + reporter.reset() + run.compileFiles (List(source)) + if (reporter.ERROR.count != 0) throw new ScalaCompileFailure (source.path) dir @@ -70,17 +80,16 @@ class ScalaCompiler(bundles: List[AbstractFile]) { lazy val settings = new Settings - lazy val reporter = new AbstractReporter { - def displayPrompt = println("compiler:") + lazy val reporter = new StoreReporter { - def display(position: Position, msg: String, severity: Severity): Unit = { - LOG.warn(position + ":" + msg) + override def info0(pos: Position, msg: String, severity: Severity, force: Boolean) { + LOG.debug(pos + ":" + msg) + super.info0(pos, msg: String, severity, force) } - val settings = ScalaCompiler.this.settings } - lazy val compiler = new Global(settings, reporter) { + lazy val compiler = new Global(settings, reporter) { override def classPath = { require(!forMSIL, "MSIL not supported") @@ -94,7 +103,10 @@ class ScalaCompiler(bundles: List[AbstractFile]) { and injecting our OSGi bundles into this internal classpath. */ override def rootLoader = { val cp = classPath.asInstanceOf[ClassPath[AbstractFile]] - (new loaders.JavaPackageLoader (cp)).asInstanceOf[LazyType] + new loaders.JavaPackageLoader (cp) match { + case loader : LazyType => loader + case _ => throw new Exception ("Failed to create rootLoader of embedded Scala compiler.") + } } def createClassPath [T] (original: ClassPath[T]) = { diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/VirtualDirectory.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/VirtualDirectory.scala deleted file mode 100644 index 1e0c6cd..0000000 --- a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/compiler/VirtualDirectory.scala +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (C) FuseSource, Inc. - * http://fusesource.com - * - * 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. - */ -import scala.collection.{mutable=>mut} -import scala.tools.nsc.io.{VirtualFile, AbstractFile} - -package org.fusesource.slang.scala.deployer.compiler { - - -/** - * Temporary workaround for a bug in scala.tools.nsc.io.VirtualDirecotry - * This file can be removed as soon as Scala 2.8.0 is out - * - * NSC -- new Scala compiler - * Copyright 2005-2009 LAMP/EPFL - -class VirtualDirectory(val name: String, maybeContainer: Option[VirtualDirectory]) extends AbstractFile { - - def path: String = - maybeContainer match { - case None => name - case Some(parent) => parent.path+'/'+ name - } - def container = maybeContainer.get - def isDirectory = true - var lastModified: Long = System.currentTimeMillis - private def updateLastModified { - lastModified = System.currentTimeMillis - } - override def file = null - override def input = error("directories cannot be read") - override def output = error("directories cannot be written") - - private val files = mut.Map.empty[String, AbstractFile] - - // the toList is so that the directory may continue to be - // modified while its elements are iterated - def elements = files.values.toList.elements - - override def lookupName(name: String, directory: Boolean): AbstractFile = { - files.get(name) match { - case None => null - case Some(file) => - if (file.isDirectory == directory) - file - else - null - } - } - - override def fileNamed(name: String): AbstractFile = { - val existing = lookupName(name, false) - if (existing == null) { - val newFile = new VirtualFile(name, path+'/'+name) - files(name) = newFile - newFile - } else { - existing - } - } - - override def subdirectoryNamed(name: String): AbstractFile = { - val existing = lookupName(name, true) - if (existing == null) { - val dir = new VirtualDirectory(name, Some(this)) - files(name) = dir - dir - } else { - existing - } - } -} -*/ - -} diff --git a/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/parser/ScriptParser.scala b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/parser/ScriptParser.scala new file mode 100644 index 0000000..3b149b0 --- /dev/null +++ b/scala/deployer/src/main/scala/org/fusesource/slang/scala/deployer/parser/ScriptParser.scala @@ -0,0 +1,75 @@ +/** + * Copyright (C) Crossing-Tech SA, 2012. + * Contact: + * + * 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 org.fusesource.slang.scala.deployer.parser + +import scala.util.parsing.combinator._ + +sealed abstract class Item +case class Code(code: String) extends Item + +abstract class Commented extends Item +case class Comment(code: String) extends Commented +case class Manifest() extends Commented + +object ManifestParser extends RegexParsers { + + override def skipWhitespace = false + + private def spaces = regex("""([ \t\n]|\n[ \t]*\*[ \t]*)*""".r) + + private def manifestHeader = spaces ~> regex("""OSGI-MANIFEST:""".r) <~ spaces ^^ {case _: String => {}} + + private def manifest = regex("""\**""".r) ~> manifestHeader + + def parse(comment: Comment): Boolean = comment match { + case Comment(c) => parse(manifest, c) match { + case Success((), _) => true + case Failure(msg, _) => false + case Error(msg, _) => throw new Exception("Slang deployer parsing error: " + msg) + } + } + +} + +object ScriptParser extends RegexParsers { + + override def skipWhitespace = false + + private def spaces = regex("""[ \t\n]*""".r) + + private def openComment = spaces ~> regex("""/\*""".r) + + private def closeComment = regex("""\*/""".r) <~ spaces + + private def textComment = regex("""^((?!\*/).|\n)*""".r) ^^ (Comment(_)) + + private def comment: Parser[Commented] = openComment ~> textComment <~ closeComment ^^ { + case c: Comment => ManifestParser.parse(c) match { + case true => Manifest() + case false => c + } + } + + /* NOTE: Use + instead of * in the following regexp + to avoid infinite loops while parsing. */ + private def code: Parser[Code] = regex("""^((?!/\*).|\n)+""".r) ^^ (Code(_)) + + private def items: Parser[List[Item]] = ((comment | code) *) + + def parse(s: String): ParseResult[List[Item]] = parse(items, s) + +} diff --git a/scala/deployer/src/test/resources/SimpleTest.scala b/scala/deployer/src/test/resources/SimpleTest.scala index 08696c1..b9de63b 100644 --- a/scala/deployer/src/test/resources/SimpleTest.scala +++ b/scala/deployer/src/test/resources/SimpleTest.scala @@ -2,6 +2,9 @@ * Copyright (C) FuseSource, Inc. * http://fusesource.com * + * Copyright (C) Crossing-Tech SA, 2012. + * Contact: + * * 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 @@ -14,6 +17,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/** OSGI-MANIFEST: + */ + class SimpleTest { def isWorking = true @@ -26,4 +33,4 @@ package org.test { -} \ No newline at end of file +} diff --git a/scala/deployer/src/test/resources/TestWithActivator.scala b/scala/deployer/src/test/resources/TestWithActivator.scala index a591f53..69ac6e1 100644 --- a/scala/deployer/src/test/resources/TestWithActivator.scala +++ b/scala/deployer/src/test/resources/TestWithActivator.scala @@ -29,4 +29,4 @@ class TestWithActivator extends BundleActivator { } } -} \ No newline at end of file +} diff --git a/scala/deployer/src/test/scala/org/fusesource/slang/scala/deployer/ScalaTransformerTest.scala b/scala/deployer/src/test/scala/org/fusesource/slang/scala/deployer/ScalaTransformerTest.scala index 3ecf0fc..2824a54 100644 --- a/scala/deployer/src/test/scala/org/fusesource/slang/scala/deployer/ScalaTransformerTest.scala +++ b/scala/deployer/src/test/scala/org/fusesource/slang/scala/deployer/ScalaTransformerTest.scala @@ -2,6 +2,9 @@ * Copyright (C) FuseSource, Inc. * http://fusesource.com * + * Copyright (C) Crossing-Tech SA, 2012. + * Contact: + * * 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 @@ -19,67 +22,61 @@ package org.fusesource.slang.scala.deployer import org.junit.{Before, Test} import org.junit.Assert.{assertNotNull,assertTrue,assertEquals} import java.util.jar.JarInputStream -import java.io.{ByteArrayInputStream, FileOutputStream, ByteArrayOutputStream, File} +import java.io.File import tools.nsc.io.AbstractFile -import org.osgi.framework.BundleContext /** * Test cases for {@link ScalaTransformer} */ class ScalaTransformerTest { - var transformer : ScalaTransformer = null + //var transformer : ScalaTransformer = null + var libraries : List[AbstractFile] = Nil lazy val repository = - if (System.getProperty("maven.local.repo") != null) { - new File(System.getProperty("maven.local.repo")) + if (System.getProperty ("maven.local.repo") != null) { + new File (System.getProperty ("maven.local.repo")) } else { - new File(new File(System.getProperty("user.home"), ".m2"), "repository") + new File (new File (System.getProperty ("user.home"), ".m2"), "repository") } @Before - def createTransformer = { - val library = new File(repository, "org/scala-lang/scala-library/2.9.1/scala-library-2.9.1.jar") - transformer = ScalaTransformer.create(List(AbstractFile.getFile(library))) + def createTransformer() { + val scalaLib = new File (repository, "org/scala-lang/scala-library/2.9.1/scala-library-2.9.1.jar") + libraries = List (AbstractFile.getFile(scalaLib)) } @Test - def testCompile = { - val source = this.getClass.getClassLoader.getResource("SimpleTest.scala") - val result = transformer.compile(source) + def testCompile() { + val source = new ScalaSource (this.getClass.getClassLoader.getResource ("SimpleTest.scala"), libraries) + val result = source.compile () - assertNotNull(result.lookupName("SimpleTest.class", false)) - assertNotNull(result.lookupPath("org/test/AnotherSimpleTest.class", false)) + assertNotNull (result.lookupName ("SimpleTest.class", false)) + assertNotNull (result.lookupPath ("org/test/AnotherSimpleTest.class", false)) } @Test - def testTransform = { - val source = this.getClass.getClassLoader.getResource("SimpleTest.scala") - val result = new ByteArrayOutputStream - - transformer.transform(source, result) - - var jar = new JarInputStream(new ByteArrayInputStream(result.toByteArray)) - var entries = getEntries(jar) - assertTrue(entries.contains("SimpleTest.class")) - assertTrue(entries.contains("org/test/AnotherSimpleTest.class")) + def testTransform() { + val source = new ScalaSource (this.getClass.getClassLoader.getResource("SimpleTest.scala"), libraries) + val result = source.transform () + var jar = new JarInputStream (result) + var entries = getEntries (jar) + assertTrue (entries.contains ("SimpleTest.class")) + assertTrue (entries.contains ("org/test/AnotherSimpleTest.class")) } @Test - def testTransformWithActivator = { - val source = this.getClass.getClassLoader.getResource("TestWithActivator.scala") - val result = new ByteArrayOutputStream() - - transformer.transform(source, result) - - var jar = new JarInputStream(new ByteArrayInputStream(result.toByteArray)) - var entries = getEntries(jar) - assertTrue(entries.contains("org/test/TestWithActivator.class")) + def testTransformWithActivator() { + val source = new ScalaSource (this.getClass.getClassLoader.getResource ("TestWithActivator.scala"), libraries) + val result = source.transform () + var jar = new JarInputStream (result) + var entries = getEntries (jar) + assertTrue (entries.contains ("org/test/TestWithActivator.class")) val manifest = jar.getManifest; - assertEquals("BundleActivator class should have been automatically detected", - "org.test.TestWithActivator", manifest.getMainAttributes.getValue("Bundle-Activator")) - assertTrue("Bundle-SymbolicName should have a decent value", - manifest.getMainAttributes().getValue("Bundle-SymbolicName").endsWith("TestWithActivator")); + assertEquals ("BundleActivator class should have been automatically detected", + "org.test.TestWithActivator", manifest.getMainAttributes.getValue ("Bundle-Activator")) + assertTrue ("Bundle-SymbolicName should have a decent value", + manifest.getMainAttributes.getValue("Bundle-SymbolicName").endsWith("TestWithActivator")); } diff --git a/scala/itests/pom.xml b/scala/itests/pom.xml index ab9530e..4606087 100644 --- a/scala/itests/pom.xml +++ b/scala/itests/pom.xml @@ -1,49 +1,47 @@ - - - - - - scala - org.fusesource.slang - 1.0.0-SNAPSHOT - - 4.0.0 - - org.fusesource.slang.scala - org.fusesource.scaraf.itests - Slang :: Scala :: Integration tests - - - - org.ops4j.pax.exam - pax-exam - - - org.ops4j.pax.exam - pax-exam-container-default - - - org.ops4j.pax.exam - pax-exam-junit - - - - \ No newline at end of file + + + + + + scala + org.fusesource.slang + 1.0.0-SNAPSHOT + + 4.0.0 + + org.fusesource.slang.scala + org.fusesource.scaraf.itests + Slang :: Scala :: Integration tests + + + + org.ops4j.pax.exam + pax-exam + + + org.ops4j.pax.exam + pax-exam-container-default + + + org.ops4j.pax.exam + pax-exam-junit + + + + diff --git a/scala/pom.xml b/scala/pom.xml index 65a6c46..490f3fe 100644 --- a/scala/pom.xml +++ b/scala/pom.xml @@ -1,116 +1,115 @@ - + 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. + --> - 4.0.0 + 4.0.0 - - parent - org.fusesource.slang - 1.0.0-SNAPSHOT - + + parent + org.fusesource.slang + 1.0.0-SNAPSHOT + - scala - pom - 1.0.0-SNAPSHOT - Slang :: Scala + scala + pom + Slang :: Scala - - deployer - itests - common - + + deployer + itests + common + - - - scala-tools.org - Scala-Tools Maven2 Repository - http://scala-tools.org/repo-releases - - false - - - + + + scala-tools.org + Scala-Tools Maven2 Repository + http://scala-tools.org/repo-releases + + false + + + - - - org.scala-lang - scala-compiler - - - - - - scala-tools.org - Scala-Tools Maven2 Repository - http://scala-tools.org/repo-releases - - false - - - - - - install - src/main/scala - src/test/scala - - - org.scala-tools - maven-scala-plugin - 2.15.2 - - - - compile - testCompile - - - - - - org.apache.felix - maven-bundle-plugin - ${felix.plugin.version} - true - - - - - - - org.scala-lang - scala-library - ${scala.version} - - - org.scala-lang - scala-compiler - ${scala.version} - - - org.scalamodules - scalamodules.core - ${scalamodules.version} - + + org.scala-lang + scala-compiler + - - \ No newline at end of file + + + scala-tools.org + Scala-Tools Maven2 Repository + http://scala-tools.org/repo-releases + + false + + + + + + install + src/main/scala + src/test/scala + + + org.scala-tools + maven-scala-plugin + 2.15.2 + + + + compile + testCompile + + + + + + org.apache.felix + maven-bundle-plugin + ${felix.plugin.version} + true + + + + + + + + org.scala-lang + scala-library + ${scala.version} + + + org.scala-lang + scala-compiler + ${scala.version} + + + org.scalamodules + scalamodules.core + ${scalamodules.version} + + + + +