Have the following:
var jdt : JDateTime = null
try {
jdt = new JDateTime(timeString, "YYYY-MM-DD hh:mm:ss,mss")
} catch {
case e : Exception => return Option.empty
}
I would like jdt to be val, as it is a constant value. Is there any scala syntax trick that can work here? Like, in case of exception, set null etc.
Not a syntax trick, just library usage:
import scala.util.Try
val jdt = Try(new JDateTime(timeString, "YYYY-MM-DD hh:mm:ss,mss")).toOption.orNull
Alhough, I would suggest leaving it at Option instead of using null.
This is also allowed since try produces a result:
val jdt =
try {
new JDateTime(timeString, "YYYY-MM-DD hh:mm:ss,mss")
} catch {
case e : Exception => return Option.empty
}
Even in java I can do this with final since I defer setting the variable until I have to:
final Date date;
try {
date = new SimpleDateFormat().parse("");
} catch (ParseException e) {
return null;
}
Related
I might be asking for trouble with this setup but I am trying to get MongoDB with MongoJack to work with Kotlin classes.
I've managed to overcome a few problems already for which I will outline my solutions if others have the same problem but I am still struggling with one issue.
Kotlin class:
data class TestDocument (
#ObjectId #JsonProperty("_id") var id:String? = null,
#JsonProperty("name") var name:String,
var date: Date = Date(),
var decimal: BigDecimal = BigDecimal.valueOf(123.456)
)
If I use the default setup (using the ObjectMapper mongoJack configured) like this:
object MongoClientTestDefault {
val log: Log = Logging.getLog(MongoClientTest::class.java)
val mongoClient : MongoClient
val database: MongoDatabase
val testCol: MongoCollection<TestDocument>
init {
val mongoUrl = "mongodb://localhost:27017/"
log.info("Initialising MongoDB Client with $mongoUrl")
try {
mongoClient = MongoClients.create(mongoUrl)
database = mongoClient.getDatabase("unitTest")
testCol = JacksonMongoCollection.builder()
.build(
mongoClient,
"unitTest",
"testCol",
TestDocument::class.java,
UuidRepresentation.STANDARD
)
log.info("Successfully initialised MongoDB client")
}catch(e: Exception){
log.error(Strings.EXCEPTION,e)
log.error("Mongo URL: ", mongoUrl)
throw e //we need to throw this as otherwise it complains that the val fields haven't been initialised
}
}
}
Then the serialization (or persisting to Mongo) works ok but the deserialization from Mongo to the Kotlin class fails because without the Kotlin jackson module Jackson and MongoJack don't know how to handle the Kotlin constructor it seems.
To fix that you have to use the jacksonObjectMapper() and pass that to the builder:
testCol = JacksonMongoCollection.builder()
.withObjectMapper(jacksonObjectMapper)
.build(
mongoClient,
"unitTest",
"testCol",
TestDocument::class.java,
UuidRepresentation.STANDARD
)
That will solve the deserialization but now the Dates are causing problems.
So in order to fix that I do the following
object MongoClientTest {
val log: Log = Logging.getLog(MongoClientTest::class.java)
val mongoClient : MongoClient
val database: MongoDatabase
val testCol: MongoCollection<TestDocument>
var jacksonObjectMapper = jacksonObjectMapper()
init {
val mongoUrl = "mongodb://localhost:27017/"
MongoJackModule.configure(jacksonObjectMapper)
log.info("Initialising MongoDB Client with $mongoUrl")
try {
mongoClient = MongoClients.create(mongoUrl)
database = mongoClient.getDatabase("unitTest")
testCol = JacksonMongoCollection.builder()
.withObjectMapper(jacksonObjectMapper)
.build(
mongoClient,
"unitTest",
"testCol",
TestDocument::class.java,
UuidRepresentation.STANDARD
)
log.info("Successfully initialised MongoDB client")
}catch(e: Exception){
log.error(Strings.EXCEPTION,e)
log.error("Mongo URL: ", mongoUrl)
throw e //we need to throw this as otherwise it complains that the val fields haven't been initialised
}
}
}
I take the jacksonObjectMapper and call MongoJackModule.configure(jacksonObjectMapper) and then pass it to the builder.
Now serialization (persiting to Mongo) and deserialization (loading from Mongo and converting to Pojo) works but there is one Exception thrown (which seems to be handled).
var testDoc = TestDocument(name="TestName")
MongoClientTest.testCol.drop()
var insertOneResult = MongoClientTest.testCol.insertOne(testDoc)
This call works and it creates the document in Mongo but this exception is printed:
java.lang.UnsupportedOperationException: Cannot call setValue() on constructor parameter of com.acme.MongoJackTest$TestDocument
at com.fasterxml.jackson.databind.introspect.AnnotatedParameter.setValue(AnnotatedParameter.java:118)
at org.mongojack.internal.stream.JacksonCodec.lambda$getIdWriter$4(JacksonCodec.java:120)
at org.mongojack.internal.stream.JacksonCodec.generateIdIfAbsentFromDocument(JacksonCodec.java:77)
at com.mongodb.internal.operation.Operations.bulkWrite(Operations.java:450)
at com.mongodb.internal.operation.Operations.insertOne(Operations.java:375)
at com.mongodb.internal.operation.SyncOperations.insertOne(SyncOperations.java:177)
at com.mongodb.client.internal.MongoCollectionImpl.executeInsertOne(MongoCollectionImpl.java:476)
at com.mongodb.client.internal.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:459)
at com.mongodb.client.internal.MongoCollectionImpl.insertOne(MongoCollectionImpl.java:453)
at org.mongojack.MongoCollectionDecorator.insertOne(MongoCollectionDecorator.java:528)
It is trying to create and set the _id on the TestDocument but struggles with the primary constructor of the Kotlin class.
I've tried to add a 'default aka empty' constructor:
data class TestDocument (
#ObjectId #JsonProperty("_id") var id:String? = null,
#JsonProperty("name") var name:String,
var date: Date = Date(),
var decimal: BigDecimal = BigDecimal.valueOf(123.456)
){
#JsonCreator constructor(): this(name = "")
}
But that doesn't fix it.
Any ideas how I could fix this?
This is the code in MongoJack that is catching the exception:
private Consumer<BsonObjectId> getIdWriter(final T t) {
final Optional<BeanPropertyDefinition> maybeBpd = getIdElementSerializationDescription(t.getClass());
return maybeBpd.<Consumer<BsonObjectId>>map(beanPropertyDefinition -> (bsonObjectId) -> {
try {
if (bsonObjectId != null) {
beanPropertyDefinition.getMutator().setValue(
t,
extractIdValue(bsonObjectId, beanPropertyDefinition.getRawPrimaryType())
);
}
} catch (Exception e) {
e.printStackTrace();
}
}).orElseGet(() -> (bsonObjectId) -> {
});
}
I guess it could just be ignored but that could flood the logs.
I want to create a macro that generates this code for me:
if (myEntity.get(Attack) == null) myEntity.add(new Attack());
if (myEntity.get(Confused) == null) myEntity.add(new Confused());
if (myEntity.get(Defend) == null) myEntity.add(new Defend());
if (myEntity.get(Offense) == null) myEntity.add(new Offense());
In code I'd like to declare/use it like this:
EntityMacroUtils.addComponents(myEntity, Attack, Confused, Defend, Offense);
The current macro function looks like this:
macro public static function addComponents(entity:ExprOf<Entity>, components:Array<ExprOf<Class<Component>>>):Expr
{
var exprs:Array<Expr> = [];
for (componentClass in components)
{
var instance = macro $e { new $componentClass() }; // problem is here
var expr = macro if ($entity.get($componentClass) == null) $entity.add(instance);
exprs.push(expr);
}
return macro $b{ exprs };
}
This macro function is incorrect, I get the error:
EntityMacroUtils.hx:17: characters 22-43 : Type not found : $componentClass
The problem is I don't know how to define new $componentClass(). How would I solve this?
I also want to avoid to have Type.createInstance in the output code.
One way to programmatically generate instantiation code is by using "old school" enums AST building (compatible Haxe 3.0.1+):
// new pack.age.TheClass()
return {
expr:ENew({name:"TheClass", pack:["pack", "age"], params:[]}, []),
pos:Context.currentPos()
};
An improved syntax using reification is possible:
// new pack.age.TheClass()
var typePath = { name:"TheClass", pack:["pack", "age"], params:[] };
return macro new $typePath();
Now, for a convenient "instantiation helper" function syntax, we need to do some contorsions to extract a type path from the expression we receive in the macro function:
// new Foo(), new pack.Bar(), new pack.age.Baz()
instantiate(Foo, pack.Bar, pack.age.Baz);
macro static function instantiate(list:Array<Expr>)
{
var news = [for (what in list) {
var tp = makeTypePath(what);
macro new $tp();
}];
return macro $b{news};
}
#if macro
static function makeTypePath(of:Expr, ?path:Array<String>):TypePath
{
switch (of.expr)
{
case EConst(CIdent(name)):
if (path != null) {
path.unshift(name);
name = path.pop();
}
else path = [];
return { name:name, pack:path, params:[] };
case EField(e, field):
if (path == null) path = [field];
else path.unshift(field);
return makeTypePath(e, path);
default:
throw "nope";
}
}
#end
In case anyone is in need for answers, I got this Thanks to ousado on the Haxe IRC chat:
If you do it in macro alone you can do this:
var ct = macro : pack.age.SomeTypename;
var tp = switch ct { case TPath(tp):tp; case _: throw "nope"; }
var expr = macro new $tp();
..or, if you explicitly construct tp:
var tp = {sub:'SomeTypeName',params:[],pack:['pack','age'],name:"SomeModuleName"}
As you can see, the complex type path is explicitly given here.
Unfortunately, Haxe don't really have a concise syntax for types in expression positions. You can pass ( _ : TypeName ) to provide an expression that contains a ComplexType.
But if you want to pass a type as argument, you could do it like this:
import haxe.macro.Expr;
using haxe.macro.Tools;
class Thing {
public function new(){}
}
class OtherThing {
public function new(){}
}
class TMacroNew {
macro static function instances( arr:Array<Expr> ) {
var news = [for (e in arr) {
var ct = switch e.expr { case EParenthesis({expr:ECheckType(_,ct)}):ct; case _: throw "nope"; };
var tp = switch ct { case TPath(tp):tp; case _: throw "nope"; };
macro new $tp();
}];
trace( (macro $b{news}).toString());
return macro $b{news};
}
static function main(){
instances( (_:Thing), (_:Thing), (_:OtherThing) );
}
}
..if you want a list of types, you might want to go for a parameter list like ( _ : L< One,Two,Three> ).
The accepted answer is problematic because it breaks when type parameters are involved, or when support for non-nominal types should be included.
I updated the example using two alternatives for a more concise notation for the list of types, while still allowing syntax for actual types.
import haxe.macro.Expr;
using haxe.macro.Tools;
class Thing {
public function new(){}
}
class OtherThing {
public function new(){}
}
class TPThing<T>{
public function new(){}
}
class TMacroNew {
macro static function instances( e:Expr ) {
var tps = switch e.expr {
case EParenthesis({expr:ECheckType(_,TPath({params:tps}))}):tps;
case ENew({params:tps},_):tps;
case _: throw "not supported";
}
var type_paths = [ for (tp in tps) switch tp {
case TPType(TPath(tp)):tp;
case _: throw "not supported";
}];
var news = [for (tp in type_paths) macro new $tp()];
trace( (macro $b{news}).toString());
return macro $b{news};
}
static function main(){
instances( (_:L<Thing,Thing,OtherThing,TPThing<Int>> ) );
instances( new L<Thing,Thing,OtherThing,TPThing<Int>>() );
}
}
Edit:
The L in L< ... > could be any valid type name. Its only purpose is allowing to write a comma-separated list of types in valid syntax. Since macro functions take expressions as arguments, we have to use an expression that allows/requires a type, like: ( _ :T ), new T(), var v:T, function(_:T):T {}.
I'm looking for a cleaner way to check if a s3path is empty or not.
My current code looks like this,
if (!s3Path.isEmpty) {
try {
var rdd = sc.textFile(s3Path)
rdd.partitions.size
} catch {
case _: org.apache.hadoop.mapred.InvalidInputException =>
(sc.parallelize(List()))
}
}
I want to do it without creating an RDD.
I check s3path and see if its valid then I pass it to Spark to create RDD like below
public boolean checkIfS3PathsValid(String bucketName, String key)
{
try{
ObjectListing list = s3.listObjects(bucketName,key);
List<S3ObjectSummary> objectInfoList = list.getObjectSummaries();
if(objectInfoList.size()>0)
{
return true;
}
else
{
return false;
}
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
}
here s3 is com.amazonaws.services.s3.AmazonS3
and you initialise it by
s3=AmazonS3Client(new PropertiesCredentials("path of your s3 credential file"));
So in you code call the checkIfS3PathsValid and see if it return true. If Yes , then only you create RDD using sc.textfile other wise you ignore that s3path.
In my REST API service layer, I have a class ProductService.
The following logic exists in all my functions: Do Validate, if validation fails i throw invalid exception, if passes, i continue to the next try-catch and throw general-error in case of failure:
def addProduct(request:AddProductRequest): BaseResponse[String] =
{
try
{
request.validate
}
catch
{
case ex: Exception => {
Logger.error("Failed to add product, Validation failed", ex);
val errorResponse:ErrorResponse[String] = new ErrorResponseList().InvalidParameters
errorResponse.addMessage(ex.getMessage)
return errorResponse
}
}
try
{
val addedProductId = productRepository.addProduct(request.language, request.tenantId, request.product)
DTOResponse(addedProductId)
}
catch
{
case ex: Exception => {
Logger.error("Failed to add product to tenant Id="+request.tenantId+" language="+request.language, ex);
val errorResponse:ErrorResponse[String] = new ErrorResponseList().GeneralError
errorResponse.addMessage(ex.getMessage())
return errorResponse
}
}
}
Now, instead of repeating the request.validate with the same try and catch for all functions, i created a base class with the following function:
abstract class ServiceBase {
def validate[T](request:BaseRequest)
{
try
{
request.validate
}
catch
{
case ex: Exception => {
Logger.error("Validation failed", ex);
val errorResponse:ErrorResponse[String] = new ErrorResponseList().InvalidParameters
errorResponse.addMessage(ex.getMessage)
return errorResponse
}
}
}
So now, my addProduct(..) will look like:
validate(request)
..the rest of the code - the 2nd try-catch
This saves alot of lines.
The problem is that if validation fails, it will never return. I get the following errors in ServiceBase:
Multiple markers at this line
- enclosing method validate has result type Unit: return value discarded
- enclosing method validate has result type Unit: return value discarded
- a pure expression does nothing in statement position; you may be omitting necessary
parentheses
validate has no return type (and thus defaults to returning Unit), in ServiceBase your signature for validate should look like this:
def validate[T](request:BaseRequest): BaseResponse[String] =
(assuming you want to return a BaseResponse[String])
this may be useful to someone, someday, functional programming.. Did we say ^_^
Changed the ServiceBase validate to:
def validate[T](request:BaseRequest):Option[BaseResponse[T]] =
{
try
{
request.validate
None
}
catch
{
case ex: Exception => {
Logger.error("Validation failed", ex);
val errorResponse:ErrorResponse[T] = new ErrorResponseList().InvalidParameters
errorResponse.addMessage(ex.getMessage)
return Some(errorResponse)
}
}
}
And now i do:
def getProducts(request:GetProductsRequest) :BaseResponse[ProductSearchResults] =
{
validate[ProductSearchResults](request).getOrElse(
{
try
{
val products = productRepository.getProducts(request.language,request.tenantId,request.productIds)
val foundProducts = for (product <- products) yield (product.id)
val notFoundProducts = request.productIds filterNot (foundProducts.toSet)
val responseWrapper = new ProductSearchResults(products, notFoundProducts)
DTOResponse(responseWrapper)
}
catch
{
case ex: Exception => {
Logger.error("Failed to get products from tenant Id="+request.tenantId+" language="+request.language, ex);
val errorResponse:ErrorResponse[ProductSearchResults] = new ErrorResponseList().GeneralError
errorResponse.addMessage(ex.getMessage())
return errorResponse
}
}})
}
I validate my function, if an exception is thrown during validation, i wish to stop the method in the catch and return, from some reason, it will continue and only caught in the main try /catch.
Code:
def updateProduct(request: UpdateProductRequest): BaseResponse[String] =
{
try
{
try
{
ValidateUpdateProductRequest(request)
}
catch
{
case ex: Exception => {
val errorResponse:ErrorResponse[String] = ErrorResponse(ErrorCode.InvalidParameters, ex.getMessage, 500)
errorResponse // <=- This does not return from function.. In debug i get here
}
}
val deleteProductResult = productRepository.updateProduct(request) //I dont want to get here !!
DTOResponse(deleteProductResult)
}
catch
{
case ex: Exception => {
Logger.error("Failed to update product Id = " +request.product.id, ex);
var errorResponse:ErrorResponse[String] = ErrorResponse(ErrorCode.GeneralError, ex.getMessage, 500)
errorResponse
}
}
}
I Understand in scala the last line of function is the only place function will return, so how do i return from the catch ?
The reason is i want to use different ErrorCode in the BaseResponse[string]
Thanks!
Whenever you have an inner expression that you want to propagate to the outermost level to be the result, you can either assign it to a temporary variable in an outer expression, or use return. So, for example:
def foo: Int = {
try { bar }
catch { case ikte: IKnowTheAnswerException => return 42 }
lotsOfMath
}
def foo: Int = {
val iKnowIt = {
try { bar }
catch { case ikte: IKnowTheAnswerException => Some(42) }
}
iKnowIt.getOrElse( lotsOfMath )
}
Even though the second pattern seems uselessly wordy, keep in mind that jumping out of the method with a return is not always obvious, especially in longer methods. So the second can, in some cases, be clearer to read (especially when you know to expect the pattern).