pii-network - acl permission- authorize/revoke access network but not to himself - acl

I'm using online Playground of Hyperledger Composer (https://composer-playground.mybluemix.net/).
I'm tring to modify acl file from "pii-network" example.
I would like to have authorized access only if participant want to authorize another member and not himself... How can I do it?
I did the following change to ACL file but it does not work as I expected (it authorize/revoke anyone and not anyone without himself):
rule AuthorizeAccessTransaction {
description: "Allow all participants to submit AuthorizeAccess transactions"
participant(p): "org.acme.model.Doctor"
operation: CREATE
resource(r): "org.acme.model.AuthorizeAccess"
condition: (r.getIdentifier() != p.getIdentifier())
action: ALLOW
}
rule RevokeAccessTransaction {
description: "Allow all participants to submit RevokeAccess transactions"
participant(p): "org.acme.model.Doctor"
operation: CREATE
resource(r): "org.acme.model.RevokeAccess"
condition: (r.getIdentifier() != p.getIdentifier())
action: ALLOW
}
rule OwnRecordFullAccess {
description: "Allow all participants full access to their own record"
participant(p): "org.acme.model.Doctor"
operation: ALL
resource(r): "org.acme.model.Doctor"
condition: (r.getIdentifier() === p.getIdentifier())
action: ALLOW
}
rule ForeignRecordConditionalAccess {
description: "Allow participants access to other people's records if granted"
participant(p): "org.acme.model.Doctor"
operation: ALL
resource(r): "org.acme.model.Doctor"
condition: (r.authorized && r.authorized.indexOf(p.getIdentifier()) > -1)
action: ALLOW
}
rule SystemACL {
description: "System ACL to permit all access"
participant: "org.hyperledger.composer.system.Participant"
operation: ALL
resource: "org.hyperledger.composer.system.**"
action: ALLOW
}
I followed instruction from https://www.youtube.com/watch?v=VTX-9VyO6OU&feature=youtu.be and I changed the .acl file like I showed
Does anyone know what is the problem? What did I wrong?
I show also cto file here:
namespace org.acme.model
concept Specialization {
o String hospital
o String hospital_ward //reparto ospedaliero
o String city
o String county
o String zip
o String field //campo medico di specializzazione
}
participant Doctor identified by username {
o String username
o String firstName
o String lastName
o Specialization specialization
o DateTime dob optional
o String[] authorized optional
}
abstract transaction DoctorTransaction {
o String username
}
transaction AuthorizeAccess extends DoctorTransaction {
}
transaction RevokeAccess extends DoctorTransaction {
}
event DoctorEvent {
o DoctorTransaction doctorTransaction
}

Use relationship as datatype for your authorized users.
participant Doctor identified by username {
o String username
o String firstName
o String lastName
o Specialization specialization
o DateTime dob optional
--> Doctor[] authorized optional
Then use this function to check condition in permissions.acl
rule ForeignRecordConditionalAccess {
description: "Allow participants access to other people's records if granted"
participant(p): "org.acme.model.Doctor"
operation: ALL
resource(r): "org.acme.model.Doctor"
condition: (
r.authorized.some(function (doc){
return doc.$identifier === p.$identifier;
})
)
action: ALLOW
}

Related

How do you specify a join table's name and columns in a Grails 4 domain class?

I'm currently upgrading a Grails 4.0.1 application to Grails 4.0.11 and I'm running into an issue with the join tables for one of my domain classes.
My Account class has multiple hasMany relationships with my class CompletionStatus:
import grails.compiler.GrailsCompileStatic
import grails.gorm.hibernate.mapping.MappingBuilder
#GrailsCompileStatic
class Account {
String name
static hasMany = [successCompletionStatuses: CompletionStatus, noShowCompletionStatuses: CompletionStatus,
partialSegmentAttendanceCompletionStatuses: CompletionStatus]
// Have distinct join tables for the CompletionStatus associations
// https://github.com/grails/grails-core/issues/10811#issuecomment-337931115
static mapping = MappingBuilder.orm {
property('successCompletionStatuses') {
joinTable { name "account_success_completion_statuses" }
}
property('noShowCompletionStatuses') {
joinTable { name "account_no_show_completion_statuses" }
}
property('partialSegmentAttendanceCompletionStatuses') {
joinTable {
name "account_partial_completion_statuses"
key {
name "account_partial_completion_statuses_id"
}
column {
name "different_completion_status_id"
}
}
}
}
static constraints = {
name blank: false
}
}
import grails.compiler.GrailsCompileStatic
#GrailsCompileStatic
class CompletionStatus {
String courseType
String status
static constraints = {
courseType nullable: true
status blank: false
}
}
The mapping in Account works as expected in Grails 4.0.1, with multiple join tables being created in the database. However, when running the application in Grails 4.0.11 I get an error at startup which I eventually discovered can be prevented by removing the multiple hasMany relationships from Account:
Caused by: java.lang.NullPointerException: null
at org.hibernate.mapping.Column.getCanonicalName(Column.java:362)
at org.hibernate.mapping.Table.getColumn(Table.java:233)
at org.hibernate.mapping.Table.addColumn(Table.java:257)
at org.grails.orm.hibernate.cfg.GrailsDomainBinder.bindSimpleValue(GrailsDomainBinder.java:2938)
at org.grails.orm.hibernate.cfg.GrailsDomainBinder.bindCollectionSecondPass(GrailsDomainBinder.java:450)
at org.grails.orm.hibernate.cfg.GrailsDomainBinder$GrailsCollectionSecondPass.doSecondPass(GrailsDomainBinder.java:3406)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1693)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1661)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:286)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:473)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:84)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689)
at org.grails.orm.hibernate.cfg.HibernateMappingContextConfiguration.buildSessionFactory(HibernateMappingContextConfiguration.java:287)
at org.grails.orm.hibernate.connections.HibernateConnectionSourceFactory.create(HibernateConnectionSourceFactory.java:86)
at org.grails.orm.hibernate.connections.AbstractHibernateConnectionSourceFactory.create(AbstractHibernateConnectionSourceFactory.java:39)
at org.grails.orm.hibernate.connections.AbstractHibernateConnectionSourceFactory.create(AbstractHibernateConnectionSourceFactory.java:23)
at org.grails.datastore.mapping.core.connections.AbstractConnectionSourceFactory.create(AbstractConnectionSourceFactory.java:64)
at org.grails.datastore.mapping.core.connections.AbstractConnectionSourceFactory.create(AbstractConnectionSourceFactory.java:52)
at org.grails.datastore.mapping.core.connections.ConnectionSourcesInitializer.create(ConnectionSourcesInitializer.groovy:24)
at org.grails.orm.hibernate.HibernateDatastore.<init>(HibernateDatastore.java:212)
at sun.reflect.GeneratedConstructorAccessor64.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:175)
... 123 common frames omitted
I found I was able to get the database tables account_success_completion_statuses and account_no_show_completion_statuses created by specifying the mapping in Acccount like so, but I am still unable to customize the table name and column names for account_partial_completion_statuses:
static mapping = MappingBuilder.orm {
property('successCompletionStatuses') {
joinTable "account_success_completion_statuses"
}
property('noShowCompletionStatuses') {
joinTable "account_no_show_completion_statuses"
}
property('partialSegmentAttendanceCompletionStatuses') {
joinTable {
name "account_partial_completion_statuses"
key "account_partial_completion_statuses_id"
// This isn't setting the column name
column "different_completion_status_id"
// This isn't setting the column name either
// column { name "different_completion_status_id" }
}
}
}
With the mapping above, a database table named account_completion_status is created instead of account_partial_completion_statuses, and it has columns account_partial_completion_statuses_id and completion_status_id.
For more context in case it is needed, I'm using MySQL 5.6.51 and the library mysql:mysql-connector-java:5.1.48 in my application.
I found the solution in the Grails documentation on joinTable!
The join tables are created properly in the database and the application runs fine when I specify my Account class like so:
import grails.compiler.GrailsCompileStatic
#GrailsCompileStatic
class Account {
String name
static hasMany = [successCompletionStatuses: CompletionStatus,
noShowCompletionStatuses: CompletionStatus,
partialSegmentAttendanceCompletionStatuses: CompletionStatus]
// Have distinct join tables for the CompletionStatus associations
// https://docs.grails.org/3.1.1/ref/Database%20Mapping/joinTable.html
static mapping = {
successCompletionStatuses joinTable: [
name: 'account_success_completion_statuses']
noShowCompletionStatuses joinTable: [
name: 'account_no_show_completion_statuses']
partialSegmentAttendanceCompletionStatuses joinTable: [
name: 'account_partial_completion_statuses',
key: 'account_partial_completion_statuses_id',
column: 'different_completion_status_id']
}
static constraints = {
name blank: false
}
}

Laravel HasManyThrough, accesing Primary/Secondary user referrals

I have an app where I need to track a user's primary and secondary referrals. My user table looks like:
users table
name, varchar
email, varchar
referral_id, varchar
referrer_id, varchar
I currently access the primary referrals/referrer using the following:
public function referrer()
{
return $this->belongsTo(User::class, 'referrer_id', 'referral_id');
}
public function primaryReferrals()
{
return $this->hasMany(User::class, 'referrer_id', 'referral_id');
}
I'm trying to figure out how I can access the "secondary" referrals...for example.
User A invites User B. User B then invites Users D, E, F. From the User A model I want to be able to call something like $user->secondaryReferrals and get users D, E, F.
I'm guessing I need to use something like:
public function secondaryReferrals()
{
return $this->hasManyThrough(
User::class,
User::class,
'referrer_id',
'referrer_id',
'referral_id',
'referral_id'
);
}
However I'm not clear on what keys/ids I should be supplying...or if this is even the right bit to use. I tried a mix but I keep getting ambiguous column name errors when using sqlite and other errors when using the mysql driver.
UserA: {
referer_id: null,
referral_id: 'abc'
}
UserB: {
referrer_id: 'abc',
referral_id: 'def'
}
UserC: {
referrer_id: 'def',
referral_id: 'ghi'
}
I want to be able to access User C from User A through User B's referral code.
You can't use a HasManyThrough relationship here because there is no way to specify a table alias.
You can use the primaryReferrals relationship twice:
public function getSecondaryReferralsAttribute()
{
$this->load('primaryReferrals.primaryReferrals');
return $this->primaryReferrals->pluck('primaryReferrals')->flatten();
}
// $user->secondaryReferrals

Restrictive ACL for Trading network in Hyperledger Composer

I couldn't solve my problem, so i try to explain it again:
There are 2 Participants (Provider). Both of them holds own Wallet and Account and they want to exchange Cash to Tokens or visa versa. They should have just READ-access to their own assets, because of fraud, security etc. But for transactions they need UPDATE-access. Here is my code:
org.acme.biznet.cto:
namespace org.acme.biznet
abstract participant Member identified by memberId {
o String memberId
o String name
o String email
}
// Sensorbesitzer, z.B private Personen, Haushalte etc.
participant Provider identified by providerId extends Member {
o String providerId
--> SDTWallet sdtWallet
--> Account account
}
// SDT Token Wallet von den Netzwerkteilnehmern.
asset SDTWallet identified by sdtWalletId {
o String sdtWalletId
o Double balance default = 0.0
--> Member owner
}
// Geldkonto von den Netzwerkteilnehmern.
asset Account identified by accountId {
o String accountId
o Double balance default = 0.0
--> Member owner
}
// Cash gegen Tokens getauscht.
transaction TradeCashToTokens {
o Double cashRate default = 2.0
o Double cashValue default = 1.0 range = [1.0,]
--> SDTWallet fromSDT
--> SDTWallet toSDT
--> Account fromCash
--> Account toCash
}
// Tokens gegen Cash getauscht.
transaction TradeTokensToCash {
o Double tokenRate default = 0.5
o Double tokenValue default = 2.0 range = [2.0,]
--> SDTWallet fromSDT
--> SDTWallet toSDT
--> Account fromCash
--> Account toCash
}
and logic.js:
/**
* Cash to tokens transaction
* #param {org.acme.biznet.TradeCashToTokens} UpdateValues
* #transaction
*/
function TradeCashToTokens(UpdateValues) {
//determine change in tokens value from the rate
var tokensChange = (UpdateValues.cashRate * UpdateValues.cashValue);
if(UpdateValues.fromCash.balance < UpdateValues.cashValue) {
throw new Error('Insufficient cash funds!');
} else if (tokensChange > UpdateValues.fromSDT.balance) {
throw new Error('Not enough tokens for this transaction!');
}
//alert("Fehler!");
//update values of exchanger1 cash account
console.log('#### exchanger1 cash balance before: ' + UpdateValues.fromCash.balance);
UpdateValues.fromCash.balance -= UpdateValues.cashValue;
console.log('#### exchanger1 cash balance after: ' + UpdateValues.fromCash.balance);
//update values of exchanger2 cash account
console.log('#### exchanger2 cash balance before: ' + UpdateValues.toCash.balance);
UpdateValues.toCash.balance += UpdateValues.cashValue;
console.log('#### exchanger2 cash balance after: ' + UpdateValues.toCash.balance);
//update values of exchanger1 token wallet
console.log('#### exchanger1 token balance before: ' + UpdateValues.toSDT.balance);
UpdateValues.toSDT.balance += tokensChange;
console.log('#### exchanger1 token balance after: ' + UpdateValues.toSDT.balance);
//update values of exchanger2 token wallet
console.log('#### exchanger2 token balance before: ' + UpdateValues.fromSDT.balance);
UpdateValues.fromSDT.balance -= tokensChange;
console.log('#### exchanger2 token balance after: ' + UpdateValues.fromSDT.balance);
console.log(UpdateValues.cashValue + ' EUR exchanged to ' + tokensChange + ' SDT Tokens with actual rate of ' + UpdateValues.cashRate);
return getAssetRegistry('org.acme.biznet.SDTWallet')
.then(function (assetRegistry) {
return assetRegistry.updateAll([UpdateValues.toSDT,UpdateValues.fromSDT]);
})
.then(function () {
return getAssetRegistry('org.acme.biznet.Account')
.then(function (assetRegistry) {
return assetRegistry.updateAll([UpdateValues.toCash,UpdateValues.fromCash]);
});
});
}
and permissions.acl:
//****************PROVIDER_PARTICIPANTS**********************
//Provider has access only to their own profile
rule ProviderAccessOwnProfile {
description: "Allow providers to access only their profile"
participant(p): "org.acme.biznet.Provider"
operation: READ, UPDATE
resource(r): "org.acme.biznet.Provider"
condition: (r.getIdentifier() === p.getIdentifier())
action: ALLOW
}
//Provider has read only access to other Providers
rule ProviderReadAccessProviders {
description: "Allow provider read access to other providers"
participant: "org.acme.biznet.Provider"
operation: READ
resource: "org.acme.biznet.Provider"
action: ALLOW
}
//****************PROVIDER_ASSETS**********************
rule ProvidersReadAccesstoAccount {
description: "Traders see their own BankAccount only"
participant: "org.acme.biznet.Provider"
operation: READ
resource: "org.acme.biznet.Account"
action: ALLOW
}
rule ProvidersReadAccesstoSDTWallet {
description: "Providers see their own SDT Wallet only"
participant: "org.acme.biznet.Provider"
operation: READ
resource: "org.acme.biznet.SDTWallet"
action: ALLOW
}
//Provider can submit CashToToken transaction
rule ProvidercanUpdateAccountthroughTransactionOnly {
description: "Allow trader to submit trade transactions"
participant(p): "org.acme.biznet.Provider"
operation: READ, UPDATE
resource(r): "org.acme.biznet.Account"
transaction(tx): "org.acme.biznet.TradeCashToTokens"
condition: (p.getIdentifier() === r.owner.getIdentifier() &&
r.getIdentifier() === tx.toCash.getIdentifier())
action: ALLOW
}
//Provider can submit CashToToken transaction
rule ProvidercanUpdateSDTWalletthroughTransactionOnly {
description: "Allow trader to submit trade transactions"
participant(p): "org.acme.biznet.Provider"
operation: READ, UPDATE
resource(r): "org.acme.biznet.SDTWallet"
transaction(tx): "org.acme.biznet.TradeCashToTokens"
condition: (p.getIdentifier() === r.owner.getIdentifier() &&
r.getIdentifier() === tx.fromSDT.getIdentifier())
action: ALLOW
}
//****************PROVIDER_TRANSACTIONS**********************
//Provider can submit CashToTokens transaction
rule ProviderSubmitCashToTokenTransactions {
description: "Allow provider to submit cash to tokens transactions"
participant: "org.acme.biznet.Provider"
operation: CREATE, READ
resource: "org.acme.biznet.TradeCashToTokens"
action: ALLOW
}
//Provider can submit TokenToCash transaction
rule ProviderSubmitTokensToCashTransactions {
description: "Allow provider to submit tokens to cash transactions"
participant: "org.acme.biznet.Provider"
operation: CREATE, READ
resource: "org.acme.biznet.TradeTokensToCash"
action: ALLOW
}
//****************PROVIDER_HISTORY**********************
//Provider can see the history of own transactions only
rule ProviderSeeOwnHistoryOnly {
description: "Proviers should be able to see the history of their own
transactions only"
participant(p): "org.acme.biznet.Provider"
operation: READ
resource(r): "org.hyperledger.composer.system.HistorianRecord"
condition: (r.participantInvoking.getIdentifier() != p.getIdentifier())
action: DENY
}
//*********************NETWORK***************************
rule SystemACL {
description: "System ACL to permit all access"
participant: "org.hyperledger.composer.system.Participant"
operation: ALL
resource: "org.hyperledger.composer.system.**"
action: ALLOW
}
rule NetworkAdminUser {
description: "Grant business network administrators full access to user
resources"
participant: "org.hyperledger.composer.system.NetworkAdmin"
operation: ALL
resource: "**"
action: ALLOW
}
rule NetworkAdminSystem {
description: "Grant business network administrators full access to system
resources"
participant: "org.hyperledger.composer.system.NetworkAdmin"
operation: ALL
resource: "org.hyperledger.composer.system.**"
action: ALLOW
}
And when i want to try make transactions as Provider, e.g. TradeCachToTokens, it says t: Participant 'org.acme.biznet.Provider#P1' does not have 'UPDATE' access to resource 'org.acme.biznet.SDTWallet#SDT1'
please see the screenshot: cash_to_tokens
Provider(P1) should get UPDATE-access for Wallet and Account, if he make transaction, but not only his own, for his opposite (P2) too.
Whats the problem here?
UPDATED ANSWER: the answer is (May 10):
You are updating the registries org.acme.biznet.SDTWallet and org.acme.bixnet.Account - and I see you have rules to allow updates to occur from the transaction TradeCashToTokens or indeed TradeTokensToCash. I think the problem is the condition should be || and not && - one resource at a time is evaluated, and the resource owner can be TRUE in the conditional match. As the trxn is invoked by the participant, should always evaluate TRUE (unless he's not the resource owner of course), part A of the condition ; for the target resource (toCash or toSDT), you compare it with the owner of the resource (being updated in your transaction function code - names as above). Note the rules are based allowing the invoking participant update the 2 target resources (based on participant, not Account - ps I think the reason your 'SDT' rule failed is because the rule says 'fromSDT' (evaluates to one target resource only).
Suggest a set of rules like:
rule UpdateAccountsviaTradeCashToTokens {
description: "Allow source/target providers defined in trxn (ie 2)- to access/update their Accounts from, trxn TradeCashToTokens only"
participant(p): "org.acme.biznet.Provider"
operation: READ, UPDATE
resource(r): "org.acme.biznet.Account"
transaction(tx): "org.acme.biznet.TradeCashToTokens"
condition: ( p.getIdentifier() === r.owner.getIdentifier() || tx.toCash.owner.getIdentifier() === r.owner.getIdentifier() )
action: ALLOW
}
rule UpdateSDTWalletsviaTradeCashToTokens {
description: "Allow source/target providers defined in trxn (ie 2)- to access/update their SDT Wallets from, trxn TradeCashToTokens only"
participant(p): "org.acme.biznet.Provider"
operation: READ, UPDATE
resource(r): "org.acme.biznet.SDTWallet"
transaction(tx): "org.acme.biznet.TradeCashToTokens"
condition: ( p.getIdentifier() === r.owner.getIdentifier() || tx.toSDT.owner.getIdentifier() === r.owner.getIdentifier() )
action: ALLOW
}
Similarly - for the other transaction TradeTokenstoCash you can have
rule UpdateAccountsviaTradeTokensToCash {
description: "Allow source/target providers defined in trxn (ie 2)- to access/update their Accounts from, trxn TradeTokensToCash only"
participant(p): "org.acme.biznet.Provider"
operation: READ, UPDATE
resource(r): "org.acme.biznet.Account"
transaction(tx): "org.acme.biznet.TradeTokensToCash"
condition: ( p.getIdentifier() === r.owner.getIdentifier() || tx.toCash.owner.getIdentifier() === r.owner.getIdentifier() )
action: ALLOW
}
rule UpdateSDTWalletsviaTradeTokenstoCash {
description: "Allow source/target providers defined in trxn (ie 2)- to access/update their SDT Wallets from, trxn TradeTokenstoCash only"
participant(p): "org.acme.biznet.Provider"
operation: READ, UPDATE
resource(r): "org.acme.biznet.SDTWallet"
transaction(tx): "org.acme.biznet.TradeTokenstoCash"
condition: ( p.getIdentifier() === r.owner.getIdentifier() || tx.toSDT.owner.getIdentifier() === r.owner.getIdentifier() )
action: ALLOW
}
You will still need your PROVIDER_TRANSACTIONS rules.
You are correct to say you will need the PROVIDER_ASSETS rules - before the transaction update based rules (ie mentioned above).
I have created an ACL tutorial - that I will incorporate into the Composer docs in due course (for benefit of others too) - similar to what you've done.
https://github.com/mahoney1/test/blob/master/acl_dynamic.md
Hope this helps, have tried your complete ruleset with changes and it works; if you wish me to post the complete set of rules, let me know.

Querying two tables in grails

I have two domain classes in my grails project. the first one is user and second one is contact. the user has one to many relationship with the contact class, i.e one user has many contacts. the user class is like this
package contacts
class User {
String name
String email
String password
static constraints = {
name(nullable: false)
email(nullable: false,email: true,blank: false )
password(nullable: false,size: 6..8,blank: false,password:true)
}
static hasMany = [contacts: Contact]
String toString(){
return name
}
}
and the contact class is like this
package contacts
class Contact {
String firstName
String lastName
String email
String phone
String address
Date dateCreated
static constraints = {
firstName(nullable: false)
lastName(nullable: true)
email(nullable: false)
phone(nullable: true)
address(nullable: true)
dateCreated()
}
static belongsTo = [user: User]
}
when I compile this, it creates two tables named user and contact, the contact table has user_id as foreign key from user table which is called id in user table. Now I want retrieve all the contacts of some specific user. I am wondering how to do this. I have tried different approaches of dynamic querying but failed. Can anybody help me to solve this?
As long as you have the User object, then it is as simple as:
def contacts = user.contacts
If a userId is being passed to some service to retrieve them you can do:
def getUserContacts(Long userId) {
def user = User.load(userId)
def contacts = Contact.findAllByUser(user)
}

Grails hibernate/Searchable stops server to start by giving the exception below

We are using Grails 2.1.1 and Searchable plugin 0.6.4 in our Grails applications and implemented searchable on some domains which are indicated below with all the mappings.
class User {
.....
static hasMany = [userEducations : UserEducations , userWorkings : UserWorkings ]
......
static searchable = {
content: spellCheck 'include'
all termVector: "yes"
userEducations component: true
userWorkings component: true
}
......
}
class UserEducations {
.....
Schools schools
.....
static belongsTo = [user : User ]
......
static searchable = {
content: spellCheck 'include'
all termVector: "yes"
schools component: true
}
......
}
class UserWorkings {
.....
Organizations organizations
.....
static belongsTo = [user : User ]
....
static searchable = {
content: spellCheck 'include'
all termVector: "yes"
organizations component: true
}
......
}
class Schools {
......
static searchable = true
......
}
class Organizations {
......
static searchable = true
......
}
The data is saving successfully with correct mapping and constraints.
The problem starts when we have the drowslike below in table user with relationship
User a1 -> UserEducations b1 -> Schools d1
and
User a2 -> UserEducations b2 -> Schools d1
or
User a1 -> UserWorkings c1 -> Organizations e1
and
User a2 - > UserWorkings c2 -> Organizations e1
(We are not sure about above fact may be the problem happened due to large no. of data.)
Then when we try to start the server then we receive below exception and server wouldn't start
We have tried by removing searchable index and restarting again then it also not start.
The server starts only when we truncate tables corresponding to above 5 domains.
18:30:54,133 [Compass Gps Index [pool-5-thread-5]] ERROR indexer.ScrollableHibernateIndexEntitiesIndexer - {hibernate}: Failed to index the database
org.compass.core.engine.SearchEngineException: Processor [4]: Failed to add job [Job Create [alias [Organizations] uid [Organizations#456#]] Resource [{Organizations} [stored/uncompressed,indexed,omitNorms<alias:Organizations>],[stored/uncompressed,indexed,omitNorms,omitTf<$/Organizations/id:456>],[stored/uncompressed,indexed<active:true>],[stored/uncompressed,indexed<dateCreated:2013-02-28-14-03-05-0-PM>],[stored/uncompressed,indexed,tokenized<aaa:109122482450911>],[stored/uncompressed,indexed<lastUpdated:2013-02-28-14-03-05-0-PM>],[stored/uncompressed,indexed,tokenized<name:Asc>],[stored/uncompressed,indexed<version:0>],[stored/uncompressed,indexed,omitNorms,omitTf<$/uid:Bs#456#>]]] after [10000ms] and backlog size [100]
at org.compass.core.lucene.engine.transaction.support.AbstractConcurrentTransactionProcessor$Processor.addJob(AbstractConcurrentTransactionProcessor.java:496)
at org.compass.core.lucene.engine.transaction.support.AbstractConcurrentTransactionProcessor.create(AbstractConcurrentTransactionProcessor.java:158)
at org.compass.core.lucene.engine.LuceneSearchEngine.createOrUpdate(LuceneSearchEngine.java:290)
at org.compass.core.lucene.engine.LuceneSearchEngine.create(LuceneSearchEngine.java:268)
at org.compass.core.impl.DefaultCompassSession.create(DefaultCompassSession.java:413)
at org.compass.core.impl.DefaultCompassSession.create(DefaultCompassSession.java:397)
at org.compass.core.impl.ExistingCompassSession.create(ExistingCompassSession.java:305)
at org.compass.gps.device.hibernate.indexer.ScrollableHibernateIndexEntitiesIndexer$RowBuffer.flush(ScrollableHibernateIndexEntitiesIndexer.java:212)
at org.compass.gps.device.hibernate.indexer.ScrollableHibernateIndexEntitiesIndexer$RowBuffer.close(ScrollableHibernateIndexEntitiesIndexer.java:206)
at org.compass.gps.device.hibernate.indexer.ScrollableHibernateIndexEntitiesIndexer.performIndex(ScrollableHibernateIndexEntitiesIndexer.java:151)
at org.compass.gps.device.support.parallel.ConcurrentParallelIndexExecutor$1$1.doInCompassWithoutResult(ConcurrentParallelIndexExecutor.java:104)
at org.compass.core.CompassCallbackWithoutResult.doInCompass(CompassCallbackWithoutResult.java:29)
at org.compass.core.CompassTemplate.execute(CompassTemplate.java:133)
at org.compass.gps.impl.SingleCompassGps.executeForIndex(SingleCompassGps.java:147)
at org.compass.gps.device.support.parallel.ConcurrentParallelIndexExecutor$1.call(ConcurrentParallelIndexExecutor.java:102)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:679)
Our problem is similar to below post
http://grails.1312388.n4.nabble.com/hibernate-Searchable-failing-to-index-on-program-start-td4119566.html
We have tried our best to sort out the problem but no luck.
Please help us to solve this problem.
Go to your console and enter:
grails install-searchable-config
Then open myproject/grails-app/conf/Searchable.groovy and search for the following and change the values as I pointed out.
mirrorChanges = false
bulkIndexOnStartup = false
in Bootstrap.groovy:
class BootStrap {
def searchableService
def init = { servletContext ->
// do any data loading you would normally do
// Manually start the mirroring process to ensure that it comes after the automated migrations.
println "Performing bulk index"
searchableService.reindex()
println "Starting mirror service"
searchableService.startMirroring()
}
}