In Chisel3, what imports do I need for the Printable examples? - chisel

I am trying to follow the example here:
https://github.com/freechipsproject/chisel3/wiki/Printing-in-Chisel#custom-printing
As in the example, I overrode def toPrintable: Printable with a concatenation of p"..." + strings.
In my scala file I
import chisel3._
But I get Scala compile warnings that it doesn't know what Printable is nor does it know what to do with the p interpolator, which makes me think I don't have the right imports.
Is there something I need to import other than chisel3._?
Here is a bit more information on what I am doing and what error I am getting.
I am modifying this file:
https://github.com/chipsalliance/rocket-chip/blob/e6a6c67f30d668e702ddbef93789e9b4f709b237/src/main/scala/tilelink/Bundles.scala
Here is what I have added:
...
import Chisel._
import chisel3.{Printable}
...
final class TLBundleA(params: TLBundleParameters)
extends TLBundleBase(params) with TLAddrChannel
{
...
override def toPrintable: Printable = {
p"A:\t" +
p"opcode[${opcode}]\t" +
p"param[${param}]\t" +
p"size[${size}]\t" +
p"source[${source}]\t" +
p"address[${address}]\t" +
p"user[${user}]\t" +
p"mask[${mask}]\t" +
p"data[${data}]\t" +
p"corrupt[${corrupt}]\n"
}
}
It seems that (unlike import chisel3._), import.chisel3.{Printable} works, but I am not doing something right with the p"...". I get this series of error:
[error] rocket-chip/src/main/scala/tilelink/Bundles.scala:189:5: value p is not a member of StringContext
[error] p"A:\t" +
[error] ^
[error] rocket-chip/src/main/scala/tilelink/Bundles.scala:190:5: value p is not a member of StringContext
[error] p"opcode[${opcode}]\t" +
[error] ^
...
[error] rocket-chip/src/main/scala/tilelink/Bundles.scala:198:5: value p is not a member of StringContext
[error] p"corrupt[${corrupt}]\n"
[error] ^
[error] 10 errors found
[error] (Compile / compileIncremental) Compilation failed
EDIT TO UPDATE:
After looking at the Chisel3 source, I added
import chisel3.{Printable, PrintableHelper}
and now I get a new error which looks more like I am just messing up my string:
[error] (run-main-0) java.lang.IllegalArgumentException: requirement failed
[error] java.lang.IllegalArgumentException: requirement failed
[error] at scala.Predef$.require(Predef.scala:268)
[error] at chisel3.printf$.escaped$1(Printf.scala:28)
[error] at chisel3.printf$.$anonfun$format$3(Printf.scala:32)
[error] ocketchiat chisel3.printf$.$anonfun$format$3$adapted(Printf.scala:32)
[error] at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:238)
[error] at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:36)
[error] at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:33)
[error] at scala.collection.immutable.StringOps.foreach(StringOps.scala:33)
[error] at scala.collection.TraversableLike.map(TraversableLike.scala:238)
[error] at scala.collection.TraversableLike.map$(TraversableLike.scala:231)
[error] at scala.collection.immutable.StringOps.map(StringOps.scala:33)
[error] at chisel3.printf$.format(Printf.scala:32)
[error] at chisel3.internal.firrtl.Emitter.emit(Emitter.scala:75)
[error] at chisel3.internal.firrtl.Emitter.$anonfun$moduleDefn$4(Emitter.scala:140)
EDIT TO UPDATE:
I added more verbosity to the requires in the printf code and I think it doesn't like tabs (\t):
[error] (run-main-0) java.lang.IllegalArgumentException: requirement failed: char to Int 9 must be >= 32
[error] java.lang.IllegalArgumentException: requirement failed: char to Int 9 must be >= 32
EDIT TO UPDATE:
I modified chisel3 to be OK with tabs, and now the code compiles and runs, but this is one of the resulting verilog strings which is obviously not right. I tried the string2Printable import you suggested but still get the following:
freechips.rocketchip.system.DefaultConfig.v: $fwrite(32'h80000002,"PLIC_TL_IFC A:\tPrintables(ArrayBuffer(PString(opcode[), Decimal(UInt<3>(IO in unelaborated TLMonitor)), PString(]\t)))Printables(ArrayBuffer(PString(param[), Decimal(UInt<3>(IO in unelaborated TLMonitor)), PString(]\t)))Printables(ArrayBuffer(PString(size[), Decimal(UInt<2>(IO in unelaborated TLMonitor)), PString(]\t)))Printables(ArrayBuffer(PString(source[), Decimal(UInt<9>(IO in unelaborated TLMonitor)), PString(]\t)))Printables(ArrayBuffer(PString(address[), Decimal(UInt<28>(IO in unelaborated TLMonitor)), PString(]\t)))Printables(ArrayBuffer(PString(user[), PString(None), PString(]\t)))Printables(ArrayBuffer(PString(mask[), Decimal(UInt<8>(IO in unelaborated TLMonitor)), PString(]\t)))Printables(ArrayBuffer(PString(data[), Decimal(UInt<64>(IO in unelaborated TLMonitor)), PString(]\t)))Printables(ArrayBuffer(PString(corrupt[), Decimal(Bool(IO in unelaborated TLMonitor)), PString(]\n)))"); // #[Monitor.scala 577:40:freechips.rocketchip.system.DefaultConfig.fir#64082.10]
freechips.rocketchip.system.DefaultConfig.v: $fwrite(32'h80000002,"PLIC_TL_IFC TLBundleD(opcode -> %d, param -> %d, size -> %d, source -> %d, sink -> %d, denied -> %d, data -> %d, corrupt -> %d)",io_in_d_bits_opcode,2'h0,io_in_d_bits_size,io_in_d_bits_source,1'h0,1'h0,io_in_d_bits_data,1'h0); // #[Monitor.scala 578:40:freechips.rocketchip.system.DefaultConfig.fir#64090.10]
For reference, here is the callsite where I am actually calling this:
when (bundle.a.fire()) { printf(p"$prefix ${bundle.a.bits}")}
when (bundle.d.fire()) { printf(p"$prefix ${bundle.d.bits}")}
Note the bundle.d.bits looks "okay" because I haven't given it a specific toPrintable function, but bundle.a.bits is a mess.
EDIT: RESOLVED
The problem is I had changed my toPrintable in the course of debugging to be of the form
override def toPrintable: Printable = {
"A:\t" +
p"opcode[$opcode]\t" +
...
p"corrupt[$corrupt]\n"
}
}
The first un-p string was making the rest of them just be normal strings, ignoring the p. I put the first p"A:\t" back and now it interpolates correctly.

The import chisel3._ should be enough. Try looking at the examples in the Chisel3 repo such as LFSR16.scala. If that doesn't help can you show the error message.

Related

Liquibase Maven update Fails with Type Incompatibility Error

I am running through the tutorial of liquibase being new. After following instructions I am able to run liquibase update command with no issues, but if I try mvn liquibase:update I receive the following error:
[ERROR] Failed to execute goal org.liquibase:liquibase-maven-plugin:3.9.0:update (default-cli) on project LiquibaseDemoMysql: A type incompatibility occurred while executing org.liquibase:liquibase-maven-plugin:3.9.0:update: class java.time.LocalDateTime cannot be cast to class java.lang.String (java.time.LocalDateTime and java.lang.String are in module java.base of loader 'bootstrap')
[ERROR] -----------------------------------------------------
[ERROR] realm = plugin>org.liquibase:liquibase-maven-plugin:3.9.0
[ERROR] strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy
[ERROR] urls[0] = file:/C:/Users/duart/.m2/repository/org/liquibase/liquibase-maven-plugin/3.9.0/liquibase-maven-plugin-3.9.0.jar
[ERROR] urls[1] = file:/C:/Users/duart/.m2/repository/mysql/mysql-connector-java/8.0.23/mysql-connector-java-8.0.23.jar
[ERROR] urls[2] = file:/C:/Users/duart/.m2/repository/com/google/protobuf/protobuf-java/3.11.4/protobuf-java-3.11.4.jar
[ERROR] urls[3] = file:/C:/Users/duart/.m2/repository/javax/enterprise/cdi-api/1.0/cdi-api-1.0.jar
[ERROR] urls[4] = file:/C:/Users/duart/.m2/repository/org/eclipse/sisu/org.eclipse.sisu.inject/0.3.2/org.eclipse.sisu.inject-0.3.2.jar
[ERROR] urls[5] = file:/C:/Users/duart/.m2/repository/org/apache/maven/maven-builder-support/3.3.9/maven-builder-support-3.3.9.jar
[ERROR] urls[6] = file:/C:/Users/duart/.m2/repository/com/google/guava/guava/18.0/guava-18.0.jar
[ERROR] urls[7] = file:/C:/Users/duart/.m2/repository/org/eclipse/aether/aether-util/1.0.2.v20150114/aether-util-1.0.2.v20150114.jar
[ERROR] urls[8] = file:/C:/Users/duart/.m2/repository/com/google/inject/guice/4.0/guice-4.0-no_aop.jar
[ERROR] urls[9] = file:/C:/Users/duart/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar
[ERROR] urls[10] = file:/C:/Users/duart/.m2/repository/org/codehaus/plexus/plexus-interpolation/1.21/plexus-interpolation-1.21.jar
[ERROR] urls[11] = file:/C:/Users/duart/.m2/repository/org/codehaus/plexus/plexus-utils/3.0.22/plexus-utils-3.0.22.jar
[ERROR] urls[12] = file:/C:/Users/duart/.m2/repository/org/codehaus/plexus/plexus-component-annotations/1.6/plexus-component-annotations-1.6.jar
[ERROR] urls[13] = file:/C:/Users/duart/.m2/repository/org/sonatype/plexus/plexus-sec-dispatcher/1.3/plexus-sec-dispatcher-1.3.jar
[ERROR] urls[14] = file:/C:/Users/duart/.m2/repository/org/sonatype/plexus/plexus-cipher/1.4/plexus-cipher-1.4.jar
[ERROR] urls[15] = file:/C:/Users/duart/.m2/repository/org/apache/commons/commons-lang3/3.4/commons-lang3-3.4.jar
[ERROR] urls[16] = file:/C:/Users/duart/.m2/repository/org/liquibase/liquibase-core/3.9.0/liquibase-core-3.9.0.jar
[ERROR] urls[17] = file:/C:/Users/duart/.m2/repository/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar
[ERROR] urls[18] = file:/C:/Users/duart/.m2/repository/org/yaml/snakeyaml/1.24/snakeyaml-1.24.jar
[ERROR] urls[19] = file:/C:/Users/duart/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
[ERROR] urls[20] = file:/C:/Users/duart/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar
[ERROR] Number of foreign imports: 1
[ERROR] import: Entry[import from realm ClassRealm[maven.api, parent: null]]
[ERROR]
[ERROR] -----------------------------------------------------
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException
Not sure what is causing this and haven't been able to find anything online regarding this. Has anyone had this issue and no how to resolve it?
I updated the version of the liquibase-maven-plugin from 3.9.0 to the latest. The version they have you. the version given in the tutorial is outdated.

Error: Attempted to instantiate a Module without wrapping it in Module()

Top module is as follows;
class PE (DataWidth: Int, NumLinks: Int, NumEntries: Int, FifoDepth: Int) extends Module {
val io = IO(new Bundle {
...
})
...
}
I think that this is ordinary style for the chisel3.
I do the following run of sbt to lint the code;
sbt 'test:runMain noc.PEMain'
Then I get bellow error messages;
[warn] Multiple main classes detected. Run 'show discoveredMainClasses' to see the list
[info] Running noc.NoCMain
[info] [0.002] Elaborating design...
[error] (run-main-0) chisel3.internal.ChiselException: Error: attempted to instantiate a Module without wrapping it in Module().
[error] chisel3.internal.ChiselException: Error: attempted to instantiate a Module without wrapping it in Module().
[error] at chisel3.internal.throwException$.apply(Error.scala:13)
[error] at chisel3.core.BaseModule.<init>(Module.scala:90)
[error] at chisel3.core.UserModule.<init>(UserModule.scala:18)
[error] at chisel3.core.ImplicitModule.<init>(UserModule.scala:102)
[error] at chisel3.core.LegacyModule.<init>(UserModule.scala:127)
[error] at noc.NumGen.<init>(NoC.scala:328)
[error] at noc.FanIn_Link.<init>(NoC.scala:376)
[error] at noc.PE$$anonfun$12.apply(NoC.scala:490)
[error] at noc.PE$$anonfun$12.apply(NoC.scala:490)
[error] at chisel3.core.Module$.do_apply(Module.scala:49)
[error] at noc.PE.<init>(NoC.scala:490)
[error] at noc.NoCMain$$anonfun$1.apply(NoCMain.scala:27)
[error] at noc.NoCMain$$anonfun$1.apply(NoCMain.scala:27)
...
[error] at chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:297)
[error] at chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:295)
[error] at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
[error] at chisel3.internal.Builder$.build(Builder.scala:295)
[error] at chisel3.Driver$.elaborate(Driver.scala:93)
[error] at chisel3.Driver$.execute(Driver.scala:140)
[error] at chisel3.iotesters.setupTreadleBackend$.apply(TreadleBackend.scala:139)
...
[error] at logger.Logger$$anonfun$makeScope$1.apply(Logger.scala:138)
[error] at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
[error] at logger.Logger$.makeScope(Logger.scala:136)
...
[error] at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
[error] at chisel3.iotesters.Driver$.execute(Driver.scala:38)
[error] at chisel3.iotesters.Driver$.execute(Driver.scala:100)
[error] at noc.NoCMain$.delayedEndpoint$noc$NoCMain$1(NoCMain.scala:27)
[error] at noc.NoCMain$delayedInit$body.apply(NoCMain.scala:26)
[error] at scala.Function0$class.apply$mcV$sp(Function0.scala:34)
...
[error] at scala.App$class.main(App.scala:76)
[error] at noc.NoCMain$.main(NoCMain.scala:26)
[error] at noc.NoCMain.main(NoCMain.scala)
[error] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
[error] at java.lang.Thread.run(Thread.java:745)
And lastly this error:
[error] (Test / runMain) Nonzero exit code: 1
I find warning of:
[warn] Multiple main classes detected. Run 'show discoveredMainClasses' to see the list
Command of sbt 'show discoveredMainClasses' shows;
[info] Loading settings from plugins.sbt ...
[info] Loading project definition from /Users/hoge/Desktop/NoC/project
[info] Loading settings from build.sbt ...
[info] Set current project to en-noc (in build file:/Users/hoge/Desktop/NoC/)
[info] *
[success] Total time: 1 s, completed 2019/10/22 2:08:49
What does this error message mean and how can I fix it?
sbt 'testOnly noc.PETester'
introduced;
[info] at chisel3.core.LegacyModule.<init>(UserModule.scala:127)
This is caused at
val io = IO(new Bundle {
val No = Output(Vec(NumLinks, UInt((log2Ceil(NumLinks)).W)))
})
It seem that the main problem is:
attempted to instantiate a Module without wrapping it in Module()
This may rise due to you making a new instance of a class extended from Module but you are probably not wrapping it into one.
For example you in your code you are doing something like:
val test = new module_class
where as you should be doing
val test = Module(new module_class)

PlayFramework 2.5 - Testing not using in-memory databse

I have been following tutorials on PlayFramework, but it seems that they are all outdated and for older version of Play.
I want to try JUnit test using mysql databse not h2 in-memory database.
I am using ebean ORC (which clearly has different api than it used to have inside play 2.2, and the api is not really well documented).
Anyway I want to try JUnit test on mysql databse, but I am always getting a configuration error.
This is how the JUnit test class looks:
public class ModelsTest extends WithApplication {
public Application app;
#Before
public void setUp() throws FileNotFoundException, IOException {
java.util.Properties externalProps=new java.util.Properties();
externalProps.load(new FileInputStream("resources/test-ebean.properties"));
ServerConfig config = new ServerConfig();
config.setName("test");
config.setDefaultServer(true);
config.loadFromProperties(externalProps);
EbeanServer server = EbeanServerFactory.create(config);
app = Helpers.fakeApplication();
Helpers.start(app);
}
#Test
public void createAndRetrieveUser() {
new User("bob#bob.bob", "admin", "admin").save();
User bob = User.find.where().eq("email", "bob#bob.bob").findUnique();
assertNotNull(bob);
assertEquals("admin", bob.login);
}
#After
public void stopApp() {
Helpers.stop(app);
}
test-ebean.properties file :
ebean.ddl.generate=true
ebean.ddl.run=true
datasource.default=db
datasource.db.username="root"
datasource.db.password="root"
datasource.db.databaseUrl="jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8"
datasource.db.databaseDriver=com.mysql.jdbc.Driver
When running test I am getting this error:
[error] Test ModelsTest.createAndRetrieveUser failed:` play.api.Configuration$$anon$1: Configuration error[null], took 4.969 sec
[error] at play.api.Configuration$.configError(Configuration.scala:154)
[error] at play.api.Configuration.reportError(Configuration.scala:806)
[error] at play.Configuration.reportError(Configuration.java:366)
[error] at play.db.ebean.DefaultEbeanConfig$EbeanConfigParser.parse(DefaultEbeanConfig.java:81)
[error] at play.db.ebean.DefaultEbeanConfig$EbeanConfigParser.get(DefaultEbeanConfig.java:60)
[error] at play.db.ebean.DefaultEbeanConfig$EbeanConfigParser.get(DefaultEbeanConfig.java:44)
[error] at com.google.inject.internal.ProviderInternalFactory.provision(ProviderInternalFactory.java:81)
[error] at com.google.inject.internal.BoundProviderFactory.provision(BoundProviderFactory.java:72)
[error] at com.google.inject.internal.ProviderInternalFactory.circularGet(ProviderInternalFactory.java:61)
[error] at com.google.inject.internal.BoundProviderFactory.get(BoundProviderFactory.java:62)
[error] at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46)
[error] at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1103)
[error] at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
[error] at com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:145)
[error] at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:41)
[error] at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38)
[error] at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62)
[error] at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:104)
[error] at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:85)
[error] at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:267)
[error] at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46)
[error] at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1103)
[error] at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
[error] at com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:145)
[error] at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:41)
[error] at com.google.inject.internal.FactoryProxy.get(FactoryProxy.java:56)
[error] at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:46)
[error] at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1103)
[error] at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
[error] at com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:145)
[error] at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:41)
[error] at com.google.inject.internal.InternalInjectorCreator$1.call(InternalInjectorCreator.java:205)
[error] at com.google.inject.internal.InternalInjectorCreator$1.call(InternalInjectorCreator.java:199)
[error] at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1092)
[error] at com.google.inject.internal.InternalInjectorCreator.loadEagerSingletons(InternalInjectorCreator.java:199)
[error] at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:180)
[error] at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:110)
[error] at com.google.inject.Guice.createInjector(Guice.java:96)
[error] at com.google.inject.Guice.createInjector(Guice.java:84)
[error] at play.api.inject.guice.GuiceBuilder.injector(GuiceInjectorBuilder.scala:181)
[error] at play.api.inject.guice.GuiceApplicationBuilder.build(GuiceApplicationBuilder.scala:123)
[error] at play.api.test.FakeApplication.<init>(Fakes.scala:209)
[error] at play.test.FakeApplication.<init>(FakeApplication.java:51)
[error] at play.test.Helpers.fakeApplication(Helpers.java:124)
[error] at play.test.WithApplication.provideFakeApplication(WithApplication.java:46)
[error] at play.test.WithApplication.provideApplication(WithApplication.java:33)
[error] at play.test.WithApplication.startPlay(WithApplication.java:51)
[error] ...
[error] Caused by: java.lang.NullPointerException
[error] at play.db.ebean.DefaultEbeanConfig$EbeanConfigParser.parse(DefaultEbeanConfig.java:79)
[error] ... 78 more
[error] Test ModelsTest.createAndRetrieveUser failed: java.lang.NullPointerException: null, took 4.979 sec
[error] at play.test.Helpers.stop(Helpers.java:376)
[error] at ModelsTest.stopApp(ModelsTest.java:58)
[error] ...
[error] Failed: Total 1, Failed 1, Errors 0, Passed 0
[error] Failed tests:
[error] ModelsTest
[error] (test:testOnly) sbt.TestsFailedException: Tests unsuccessful
I just started learning play (but actually most of tutorials are outdated) and I have spent more time trying to configure it to run than actually coding. I guess I should look up another framework.
You don't need to set up a whole database just for testing (you are free to, of course). Play relies strongly on In-Memory databases (e.g. during development) and you can utilize this also in your tests:
#Test
public void findById() {
running(fakeApplication(inMemoryDatabase("test")), () -> {
User bob = User.findById(21l);
assertEquals("bob#bob.bob", bob.email);
assertEquals("admin", bob.login);
});
}
On the other hand if you really want to test the database access code you can go as far as creating a Database test object:
Database database = Databases.createFrom(
"com.mysql.jdbc.Driver",
"jdbc:mysql://localhost/test"
);
Which again can be in-memory:
Database database = Databases.inMemory(
"mydatabase",
ImmutableMap.of(
"MODE", "MYSQL"
),
ImmutableMap.of(
"logStatements", true
)
);
Just don't forget to release the resources after the test:
#After
public void shutdownDatabase() {
database.shutdown();
}

Creating a generic reusable function with implicit and types in Scala

I have a block of code that gets replicated literally all over the place. This block of code (about 10 lines) handles inbound actions, validates them, handles the JSON serialization, and calls an internal function, preparing a result.
I know it's possible to reduce this to a one-liner call to a common helper function, but because of the implicits, the types, and my not-yet-great knowledge of Scala syntax I keep running into problems.
The code (two separate examples, to demonstrate how they differ):
val authenticate = GPAuthenticatedAction(parse.json) { implicit request =>
request.body.validate[AuthenticationRequest] match {
case JsSuccess(request, _) => {
val (status, response) = performAuthentication(request)
status(Json.toJson(response.asInstanceOf[AuthenticationResponse]))
}
case e: JsError => NotAcceptable(Json.toJson(GPError.MalformedJSON.value(e.toString + " REQUEST: " + request.body.toString)))
}
}
val register = GPAuthenticatedAction(parse.json) { implicit request =>
request.body.validate[RegistrationRequest] match {
case JsSuccess(request, _) => {
val (status, response) = performRegistration(request)
status(Json.toJson(response.asInstanceOf[RegistrationResponse]))
}
case e: JsError => NotAcceptable(Json.toJson(GPError.MalformedJSON.value(e.toString + " REQUEST: " + request.body.toString)))
}
}
As you can see – very, very nearly identical, with the exception of the request type (AuthenticationRequest versus RegistrationRequest), and the response type (AuthenticationResponse versus RegistrationResponse). Otherwise it's boilerplate.
There should be a way to distill this down to something like:
val register = GPAuthenticatedAction(parse.son) from(RegistrationRequest, RegistrationResponse)
I took a crack at defining a from[I,O](request: I, response: O) but this led to a bunch of problems (no information about what JSON was deserializing, etc). So, I tried to abstract this out a bit by creating a couple of traits, GPRequest and GPResult:
trait GPRequest[T] {
implicit val format = Json.format[T]
}
trait GPResponse[T] {
implicit val format: Format[T] = Json.format[T]
def from(error: GPError): GPResponse
}
And then tried to define a function such as:
def from[I: GPRequest, O: GPResponse](request: Request) { implicit request =>
request.body.validate[I] match {
case JsSuccess(request, _) => {
val (status, response) = performAuthentication(request)
status(Json.toJson(response.asInstanceOf[O]))
}
case e: JsError => NotAcceptable(Json.toJson(GPError.MalformedJSON.value(e.toString + " REQUEST: " + request.body.toString)))
}
}
But this leads to all forms of problems. I've pasted the compiler errors below, but the general gist is:
JSON can't figure out what to do with the implicit serialization (Format/Reads/Writes). Can't find apply and unapply of the actual type.
Type parameter errors on GPRequest, GPResponse.
Can't declare the request as implicit (my Scala syntax is probably screwed up here).
And it gets worse.
The the bottom line question: Does anyone have a clean design pattern that achieves what I'm looking for here? (Or alternatively, can someone that knows Play and Scala give some guidance on where to go from here). This really seems like it should be possible but I'm stuck figuring out the next step.
For the intrepid, here are the errors:
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:33: trait GPRequest takes type parameters
[error] object AuthenticationRequest extends GPRequest {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:121: trait GPResponse takes type parameters
[error] def okResponse(response: GPProcedureResult, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(response.code), instance) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:121: trait GPResponse takes type parameters
[error] def okResponse(response: GPProcedureResult, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(response.code), instance) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:109: trait GPResponse takes type parameters
[error] def okResponse(response: Results.Status, instance: GPResponse): (Results.Status, GPResponse) = { (response, instance) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:109: trait GPResponse takes type parameters
[error] def okResponse(response: Results.Status, instance: GPResponse): (Results.Status, GPResponse) = { (response, instance) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:37: trait GPResponse takes type parameters
[error] case class AuthenticationResponse(token: Option[GPToken], id: Option[GPID], error: Option[GPError]) extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:98: trait GPResponse takes type parameters
[error] def errorResponse(error: GPProcedureResult, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(error.code), instance.from(error.failed.getOrElse(GPError.errorForCode(error.code)))) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:98: trait GPResponse takes type parameters
[error] def errorResponse(error: GPProcedureResult, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(error.code), instance.from(error.failed.getOrElse(GPError.errorForCode(error.code)))) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:85: trait GPResponse takes type parameters
[error] def errorResponse(error: GPError, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(error), instance.from(error)) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:85: trait GPResponse takes type parameters
[error] def errorResponse(error: GPError, instance: GPResponse): (Results.Status, GPResponse) = { (resultCodeFor(error), instance.from(error)) }
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:43: trait GPResponse takes type parameters
[error] object AuthenticationResponse extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:53: trait GPRequest takes type parameters
[error] object RegistrationRequest extends GPRequest {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:57: trait GPResponse takes type parameters
[error] case class RegistrationResponse(token: Option[GPToken], id: Option[GPID], error: Option[GPError]) extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:63: trait GPResponse takes type parameters
[error] object RegistrationResponse extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:73: trait GPRequest takes type parameters
[error] object LogoutRequest extends GPRequest {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:77: trait GPResponse takes type parameters
[error] case class LogoutResponse(error: Option[GPError] = None) extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:81: trait GPResponse takes type parameters
[error] object LogoutResponse extends GPResponse {
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:88: trait Request takes type parameters
[error] def from[I: GPRequest, O: GPResponse](request: Request) { implicit request =>
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:88: missing parameter type
[error] def from[I: GPRequest, O: GPResponse](request: Request) { implicit request =>
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:91: type mismatch;
[error] found : Any
[error] required: controllers.GPSecurityService.AuthenticationRequest
[error] val (status, response) = performAuthentication(request)
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/controllers/GPSecurityService.scala:92: Any does not take parameters
[error] status(Json.toJson(response.asInstanceOf[O]))
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:15: No unapply or unapplySeq function found
[error] implicit val format = Json.format[T]
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:19: No unapply or unapplySeq function found
[error] implicit val format: Format[T] = Json.format[T]
[error] ^
[error] /Users/zbeckman/Projects/Glimpulse/Server/project/glimpulse-server/app/utility/GPResponseMapping.scala:20: trait GPResponse takes type parameters
[error] def from(error: GPError): GPResponse
You could write parent case classes instead of your traits and extend your existing types:
AuthenticationRequest, RegistrationRequest, AuthenticationResponse, RegistrationResponse
being a subclass of your new parent case classes.
With this, you could change your signature to extend your generic types like this:
def from[I <: GPRequest, O <: GPResponse]

Validate data fails with Play2.2.0 and Ebean

I am developing an app with Play 2.2.0 and Ebean.
I have a problem when creating directly entities by code, not using a form. Let's say this is my entity :
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import models.SuperEntity;
import com.avaje.ebean.validation.Length;
#Entity
#Table(name = "foos")
public class Foo extends SuperEntity {
#Column(unique = true)
#NotNull
#Length(min = 2, max = 2)
public String code;
}
And this is my unit test :
#Test
public void testCreate() {
running(fakeApplication(), new Runnable() {
#Override
public void run() {
Foo foo = new Foo();
foo.code = "foo1";
foo.save();
}
});
}
The problem is that the validation annotations seems to be totally ignored. If I set the code to null, I have the database error not a Play error.
[error] Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'name' cannot be null
[error] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[error] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
[error] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
[error] at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
[error] at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
[error] at com.mysql.jdbc.Util.getInstance(Util.java:386)
[error] at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1041)
[error] at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4190)
[error] at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4122)
[error] at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570)
[error] at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2731)
[error] at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2818)
[error] at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2157)
[error] at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2460)
[error] at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2377)
[error] at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2361)
[error] at com.jolbox.bonecp.PreparedStatementHandle.executeUpdate(PreparedStatementHandle.java:205)
[error] at com.avaje.ebeaninternal.server.type.DataBind.executeUpdate(DataBind.java:55)
[error] at com.avaje.ebeaninternal.server.persist.dml.InsertHandler.execute(InsertHandler.java:134)
[error] at com.avaje.ebeaninternal.server.persist.dml.DmlBeanPersister.execute(DmlBeanPersister.java:86)
The #Length annotation is ignored as well, I can put the length that I want in the database. The column in the database should be varchar(2) and it is varchar(255).
How can I fix this ? Thanks in advance.
EDIT : I have the same errors with annotations like #Min, #Max on Long or Integer properties...
Ebean does not validate data on save. The validation annotations such as #NotNull and #Column(unique = true) are used during DDL generation to specify table constraints. Regarding the #Length annotation, it is possible that this is not supported by Ebean, though I can't find the documentation to be sure. Documentation is definitely a problem with Ebean. There is a discussion on the Play git repo about the future of Ebean with Play. It includes information about new/active development on Ebean.
javax.validation is not implemented in Ebean. See com.avaje.ebean.validation package, but I'd suggest you not to use it.