jdo: Programmatically create multiple persistence-units in DataNucleus - jdo

I have two different data sources for which I need two different PersistenceManagerFactory. This I can always get by writing a persistence.xml file. But I want this to be represented programmatically. Though the second data-source remains relatively unchanged, the first data-source may have additions to it via plugins. These plugins can come with one or more JDO annotated classes. A persitance.xml wouldn't be such a good idea here because I want them to be loaded at runtime.
In Hibernate (and with JPA) this would be possible by creating a configuration object and adding all annotated classes to it. Whenever I see a new plugin being loaded, I can always shutdown the SessionFactory and reload it with the extra classes from plugin by looking at #Entity annotation.
Is there a similar way to do it in DataNucleus/JDO?
I tried searching it on Google, but all I end up is at DataNucleus site explaining how to write a persitence.xml file.

The code below in Scala demonstrates how you can create a PersistenceManager dynamically. You just have to populate a map and pass it to JDOHelper#getPersistenceManagerFactory.
private val pu = props(db.driver, db.url, db.username, db.password)
private val pmf = JDOHelper.getPersistenceManagerFactory(pu.asJava)
private val pm = pmf.getPersistenceManager.asInstanceOf[JDOPersistenceManager]
Below you can see an example on how you will probably be willing to populate such map in the case of H2, MongoDB and PostgreSQL:
def props(driver: String, url: String, username: String, password: String): Map[String, Any] =
driver match {
case "org.h2.Driver" =>
Map[String, Any](
"javax.jdo.option.Mapping" -> "h2",
"datanucleus.schema.autoCreateAll" -> "true",
"javax.jdo.PersistenceManagerFactoryClass" -> "org.datanucleus.api.jdo.JDOPersistenceManagerFactory",
"javax.jdo.option.ConnectionDriverName" -> driver,
"javax.jdo.option.ConnectionURL" -> url,
"javax.jdo.option.ConnectionUserName" -> username,
"javax.jdo.option.ConnectionPassword" -> password
)
case "org.postgresql.Driver" =>
Map[String, Any](
"datanucleus.schema.autoCreateAll" -> "true",
"javax.jdo.PersistenceManagerFactoryClass" -> "org.datanucleus.api.jdo.JDOPersistenceManagerFactory",
"javax.jdo.option.ConnectionDriverName" -> driver,
"javax.jdo.option.ConnectionURL" -> url,
"javax.jdo.option.ConnectionUserName" -> username,
"javax.jdo.option.ConnectionPassword" -> password,
"javax.jdo.option.RetainValues" -> "true",
"javax.jdo.option.RestoreValues" -> "true",
"javax.jdo.option.Optimistic" -> "true",
"javax.jdo.option.NontransactionalWrite" -> "false",
"javax.jdo.option.NontransactionalRead" -> "true",
"javax.jdo.option.Multithreaded" -> "true",
"javax.jdo.option.IgnoreCache" -> "false"
)
case "mongodb.jdbc.MongoDriver" =>
Map[String, Any](
"javax.jdo.option.Mapping" -> "mongo",
"datanucleus.schema.autoCreateAll" -> "true",
"javax.jdo.PersistenceManagerFactoryClass" -> "org.datanucleus.api.jdo.JDOPersistenceManagerFactory",
"javax.jdo.option.ConnectionDriverName" -> driver,
"javax.jdo.option.ConnectionURL" -> url,
"javax.jdo.option.ConnectionUserName" -> username,
"javax.jdo.option.ConnectionPassword" -> password
)
case _ => throw new IllegalArgumentException(s"unknown driver %s".format(driver))
}

Related

What version of rusqlite should I use?

I'm learning the rust language. So, I try to build a simple web app using sqlite3. But it gets multiple packages link error.
I saw some solution for this error(ex. this), but they didn't work.
The cause seems to be that the version specification of rusqlite is wrong, but I don't know the correct version specification.
How should I configure the cargo.toml?
Source codes are here.
cargo.toml
[package]
name = "todo"
version = "0.1.0"
edition = "2018"
[dependencies]
actix-web = "4.0.0-beta.3"
actix-rt = "2.2.0"
thiserror = "1.0.29"
askama = "0.10.5"
rusqlite = { version = "0.23", features = ["bundled"] }
r2d2 = "0.8.9"
r2d2-sqlite3 = "0.1.1"
main.rs
use actix_web::{get, App, HttpResponse, HttpServer, ResponseError};
use thiserror::Error;
use askama::Template;
use r2d2::Pool;
use r2d2_sqlite3::SqliteConnectionManager;
use rusqlite::params;
struct TodoEntry {
id: u32,
text: String,
}
#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate {
entries: Vec<TodoEntry>,
}
#[derive(Error, Debug)]
enum MyError {
#[error("Failed to render HTML")]
AskamaError(#[from] askama::Error),
}
impl ResponseError for MyError {}
#[get("/")]
async fn index() -> Result<HttpResponse, MyError> {
let mut entries = Vec::new();
entries.push(TodoEntry {
id: 1,
text: "First entry".to_string(),
});
entries.push(TodoEntry {
id: 2,
text: "Second entry".to_string(),
});
let html = IndexTemplate { entries };
let response_body = html.render()?;
Ok(HttpResponse::Ok()
.content_type("text/html")
.body(response_body))
}
#[actix_rt::main]
async fn main() -> Result<(), actix_web::Error> {
let manager = SqliteConnectionManager::file("todo.db");
let pool = Pool::new(manager).expect("Failed to initialize the connection pool.");
let conn = pool
.get()
.expect("Failed to get the connection from the pool.");
conn.execute(
"CREATE TABLE IF NOT EXISTS todo (
id INTEGER PRIMARY KEY AUTOINCREMENT,
text TEXT NOT NULL
)",
params![],
)?
.expect("Failed to create a table `todo`");
HttpServer::new(move || App::new().service(index))
.bind("127.0.0.1:8080")?
.run()
.await?;
Ok(())
}
And the error messages are here.
error: multiple packages link to native library `sqlite3`, but a native library can be linked only once
package `libsqlite3-sys v0.18.0`
... which is depended on by `rusqlite v0.23.1`
... which is depended on by `todo v0.1.0 (/Users/******/Documents/IntelliJ project/Rust-project/todo)`
links to native library `sqlite3`
package `sqlite3-src v0.2.9`
... which is depended on by `sqlite3-sys v0.12.0`
... which is depended on by `sqlite3 v0.24.0`
... which is depended on by `r2d2-sqlite3 v0.1.1`
... which is depended on by `todo v0.1.0 (/Users/*****/Documents/IntelliJ project/Rust-project/todo)`
also links to native library `sqlite3`
You're directly depending on rusqlite and using r2d2-sqlite3 which itself depends on rusqlite.
Since rusqlite binds to a native library as the message indicates you can't have two versions of rusqlite linking to different versions of sqlite3(-sys), so you need to ensure you use the same version of rusqlite as r2d2.
If you're not going to publish on Cargo, the easiest by far is to leave rusqlite's version as a wildcard ("*"), that way the dependency resolver will give you whatever works for r2d2-sqlite3. Otherwise you need to check the version of r2d2-sqlite3 you're using and match it.
Incidentally... r2d2-sqlite3 0.1.1? That seems to be over 4 years old, the current version seems to be 0.18. I'm slightly surprised r2d2 works, though I guess it changes relatively little (0.8.0 was 4 years ago, current is 0.8.9). Though I'm not sure what the utility of r2d2 is for sqlite3, especially for "a simple web app".

Complication Family support- don't show Complication Family if not supported

I'm wondering how to not show a Complication Family if I'm not supporting it.
Example: Extra Large watch face
In ComplicationController.swift's getLocalizableSampleTemplate and getCurrentTimelineEntry methods I just pass in a handler(nil) when switching on complication.family for Extra Large:
case .extraLarge:
handler(nil)
But that must not be right or all there is to do, because my complication for Extra Large is still able to be chosen:
But it obviously doesn't work or have any data to show:
Does anyone know what I'm missing? Thanks!
UPDATE:
My ComplicationController.swift's getComplicationDescriptors:
func getComplicationDescriptors(handler: #escaping ([CLKComplicationDescriptor]) -> Void) {
let oneSupported = [
CLKComplicationFamily.circularSmall,
.modularSmall,
.utilitarianSmall,
.modularLarge,
.utilitarianLarge,
.graphicExtraLarge,
.graphicCircular
]
let twoSupported = [
CLKComplicationFamily.circularSmall,
.modularSmall,
.utilitarianSmall,
.utilitarianSmallFlat,
.extraLarge,
.graphicBezel,
.graphicCircular,
.graphicCorner,
.graphicRectangular,
.modularLarge,
.utilitarianLarge
]
let descriptors = [
CLKComplicationDescriptor(identifier: ComplicationIdentifier.height.rawValue, displayName: "Complication 1", supportedFamilies: oneSupported)
// Multiple complication support can be added here with more descriptors
,
CLKComplicationDescriptor(identifier: ComplicationIdentifier.price.rawValue, displayName: "Complication 2", supportedFamilies: twoSupported)
]
// Call the handler with the currently supported complication descriptors
handler(descriptors)
}
Also here's my WatchApp.swift which is using that SwiftUI lifecycle (unless I'm mistaken):
struct BlockWatchApp: App {
#WKExtensionDelegateAdaptor(ExtensionDelegate.self) var extensionDelegate
var body: some Scene {
WindowGroup {
NavigationView {
WatchView()
}
}
}
}
If you're building your watchOS App using the SwiftUI lifecycle you set up your supported complications using the getComplicationDescriptors(handler: #escaping ([CLKComplicationDescriptor]) -> Void) method.
To support only certain complications you define which ones you want to support in an array:
func getComplicationDescriptors(handler: #escaping ([CLKComplicationDescriptor]) -> Void) {
let descriptors = [
CLKComplicationDescriptor(identifier: "complication", displayName: "App Name",
supportedFamilies: [CLKComplicationFamily.circularSmall,
CLKComplicationFamily.graphicBezel])
// Multiple complication support can be added here with more descriptors/
// Create a new identifier for each new CLKComplicationDescriptor.
]
// Call the handler with the currently supported complication descriptors
handler(descriptors)
}
This example will only display your complication in the circularSmall and graphicBezel complications. If you want to support all complications use .allCases.
If you're building your App using the watchKit with AppDelegate lifecycle then you define your supported complications in the .plist file in the WatchKit Extension. You should see "ClockKit Complication - Supported Families" then you can add or delete your desired complication support.

Custom Adapter to support RocketPant with Rails

I am using rocket_pants gem to build backend API https://github.com/Sutto/rocket_pants
It have a specific format to output data:
{
"response":[
{"id":1,"title":"Object Title","description":"Object Description"},
{"id":1,"title":"Object Title","description":"Object Description"} ],
"count":2,
"pagination": {
"previous":null,
"next":null,
"current":1,
"per_page":30,
"count":2,
"pages":1}
}
I am using Batman.RailsStorage to persist models. But actions like MyApp.Model.get('all') works fine on the backend but they actually do not parse and load model objects.
Can you guide me how to configure StorageAdapter or write new one to handle such kind of data format?
You could try overriding the collectionJsonNamespace method (defined on Batman.RestStorage).
I see that it's used after a readAll operation to get records from the HTTP response.
For example:
class MyApp.RocketPantsStorage extends Batman.RailsStorage
collectionJsonNamespace: -> "response"
Then in your model
#= require ../rocket_pants_storage
# the storage adapter must be available at load time
class MyApp.SomeModel
#persist MyApp.RocketPantsStorage
Does that work?
With the same approach mentioned in answer from #rmosolgo I build paginator as well.
class MyApp.RocketPantsPaginator extends Batman.ModelPaginator
totalCountKey: "pagination.count"
loadItemsForOffsetAndLimit: (offset, limit) ->
params = #paramsForOffsetAndLimit(offset, limit)
params[k] = v for k,v of #params
#model.load params, (err, records, env) =>
if err?
#markAsFinishedLoading()
#fire('error', err)
else
response = new Batman.Object(env.response)
#set('totalCount', response.get(#totalCountKey));
#updateCache(#offsetFromParams(params), #limitFromParams(params), records)

Logstash additional JARs

I'm trying to add a custom filter to Logstash 1.4.2, which is installed as a Debian package. The contrib plugins (http://logstash.net/docs/1.4.2/contrib-plugins) are also installed. This filter establishes a connection to an sqlite database, and for that it makes use of the JDBC sqlite driver:
require "logstash/filters/base"
require "logstash/namespace"
require "jdbc/sqlite3"
require "java"
class LogStash::Filters::MyFilter < LogStash::Filters::Base
(...)
public
def register
if #database.nil?
raise "You must specify 'database => ...' in your filter"
elsif not File.exists?(#database)
raise "The database does not exist"
end
url = "jdbc:sqlite:" + #database
Java::OrgSqlite::JDBC #initialize the driver
#connection = java.sql.DriverManager.getConnection(url)
#logger.info("Using database", :path => #database)
end # def register
(...)
end # class LogStash::Filters::MyFilter
However, when I try to start logstash I get the following error in the logs :
"cannot load Java class org.sqlite.JDBC"
I've tried to place the sqlite-jdbc-3.7.2.jar file provided by the contrib plugins into several locations, but so far I couldn't figure out what's the right one for JARs required by custom plugins, or whether it is a configuration issue.
Any ideas?
While I couldn't find the way of using external jars with Logstash 1.4, I could get it to work by replacing SQlite JDBC with Sequel, which is already included.
require "logstash/filters/base"
require "logstash/namespace"
require "sequel"
class LogStash::Filters::MyFilter < LogStash::Filters::Base
(...)
public
def register
if #database.nil?
raise "You must specify 'database => ...' in your filter"
elsif not File.exists?(#database)
raise "The database does not exist"
end
#db = Sequel.connect("jdbc:sqlite:#{#database}")
#logger.info("Using database", :path => #database)
end # def register
(...)
end # class LogStash::Filters::MyFilter

Meteor - How can I limit which fields are published to the client?

I want to publish only a limited amount of data to the client.
I've tried to do it like this:
# server
Meteor.publish('users', ->
Meteor.users.find({},
fields:
services: 0
)
)
But the client still receives the whole object.
# client
Meteor.startup( ->
Meteor.subscribe('users')
)
# ...
# in another function
Meteor.users.find().observe( ->
changed: (updated) ->
console.log updated
)
What am I doing wrong?
Meteor.publish '', ->
Posts.find({}, { fields: { title: 1, content: true, secret: false } });
what about add those {}
The code below works for me (coffeescript). The pwd field isn't published.
Server
Meteor.publish "users", (userId) ->
user = Users.find userId,
fields:
pwd: false
return user
Client
Meteor.autosubscribe ->
userId = Session.get SESSION_USER
Meteor.subscribe 'users', userId
The only differences I see are
0 vs false... (should be a matter of taste, only)
Your collection is accessed via Meteor
In the client my subscription is placed inside a autosubscribe callback while you're using the observe method.
Do the fields exists in the result of Meteor.users.find().fetch() in the browsers console, too?

Resources