Merged from branch
git-svn-id: https://svn.d4science.research-infrastructures.eu/gcube/trunk/accounting/accounting-analytics@119744 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
parent
2641fab5f2
commit
035c3459bf
|
@ -14,8 +14,8 @@ import java.util.Set;
|
||||||
|
|
||||||
import org.gcube.accounting.analytics.exception.NoAvailableScopeException;
|
import org.gcube.accounting.analytics.exception.NoAvailableScopeException;
|
||||||
import org.gcube.accounting.analytics.exception.NoUsableAccountingPersistenceQueryFound;
|
import org.gcube.accounting.analytics.exception.NoUsableAccountingPersistenceQueryFound;
|
||||||
import org.gcube.accounting.analytics.persistence.AccountingPersistenceQuery;
|
import org.gcube.accounting.analytics.persistence.AccountingPersistenceBackendQuery;
|
||||||
import org.gcube.accounting.analytics.persistence.AccountingPersistenceQueryFactory;
|
import org.gcube.accounting.analytics.persistence.AccountingPersistenceBackendQueryFactory;
|
||||||
import org.gcube.accounting.datamodel.AggregatedUsageRecord;
|
import org.gcube.accounting.datamodel.AggregatedUsageRecord;
|
||||||
import org.gcube.accounting.datamodel.SingleUsageRecord;
|
import org.gcube.accounting.datamodel.SingleUsageRecord;
|
||||||
import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord;
|
import org.gcube.accounting.datamodel.usagerecords.ServiceUsageRecord;
|
||||||
|
@ -60,7 +60,7 @@ public class ResourceRecordQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected AccountingPersistenceQuery accountingPersistenceQuery;
|
protected AccountingPersistenceBackendQuery accountingPersistenceQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiate the ResourceRecord for the current scope
|
* Instantiate the ResourceRecord for the current scope
|
||||||
|
@ -70,7 +70,7 @@ public class ResourceRecordQuery {
|
||||||
* instance which can query in that scope
|
* instance which can query in that scope
|
||||||
*/
|
*/
|
||||||
public ResourceRecordQuery() throws NoAvailableScopeException, NoUsableAccountingPersistenceQueryFound {
|
public ResourceRecordQuery() throws NoAvailableScopeException, NoUsableAccountingPersistenceQueryFound {
|
||||||
this.accountingPersistenceQuery = AccountingPersistenceQueryFactory.getInstance();
|
this.accountingPersistenceQuery = AccountingPersistenceBackendQueryFactory.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,7 +83,7 @@ public class ResourceRecordQuery {
|
||||||
*/
|
*/
|
||||||
public ResourceRecordQuery(String scope) throws NoAvailableScopeException, NoUsableAccountingPersistenceQueryFound {
|
public ResourceRecordQuery(String scope) throws NoAvailableScopeException, NoUsableAccountingPersistenceQueryFound {
|
||||||
ScopeProvider.instance.set(scope);
|
ScopeProvider.instance.set(scope);
|
||||||
this.accountingPersistenceQuery = AccountingPersistenceQueryFactory.getInstance();
|
this.accountingPersistenceQuery = AccountingPersistenceBackendQueryFactory.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static JSONObject getPaddingJSONObject(Map<Calendar, Info> unpaddedResults) throws JSONException{
|
protected static JSONObject getPaddingJSONObject(Map<Calendar, Info> unpaddedResults) throws JSONException{
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.gcube.accounting.analytics.persistence;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.gcube.accounting.analytics.Filter;
|
||||||
|
import org.gcube.accounting.analytics.Info;
|
||||||
|
import org.gcube.accounting.analytics.TemporalConstraint;
|
||||||
|
import org.gcube.accounting.datamodel.AggregatedUsageRecord;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public abstract class AccountingPersistenceBackendQuery {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AccountingPersistenceBackendQuery.class);
|
||||||
|
|
||||||
|
protected abstract void prepareConnection(AccountingPersistenceBackendQueryConfiguration configuration) throws Exception;
|
||||||
|
|
||||||
|
protected abstract Map<Calendar, Info> reallyQuery(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType,
|
||||||
|
TemporalConstraint temporalConstraint, List<Filter> filters) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the persistence obtaining a Map where the date is the key and
|
||||||
|
* the #Info is the value. The result is relative to an Usage Record Type,
|
||||||
|
* respect a TemporalConstraint and can be applied one or more filters.
|
||||||
|
* @param usageRecordType the Usage Record Type of interest
|
||||||
|
* @param temporalConstraint the TemporalConstraint (interval and aggregation)
|
||||||
|
* @param filters the filter for the query. If null or empty string get all
|
||||||
|
* data. The filters are evaluated in the order the are presented and are
|
||||||
|
* considered in AND
|
||||||
|
* @return the Map containing for each date in the required interval the
|
||||||
|
* requested data
|
||||||
|
* @throws Exception if fails
|
||||||
|
*/
|
||||||
|
public Map<Calendar, Info> query(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType,
|
||||||
|
TemporalConstraint temporalConstraint, List<Filter> filters) throws Exception{
|
||||||
|
logger.trace("Request query: UsageRecordType={}, {}={}, {}s={}", usageRecordType.newInstance().getUsageRecordType(),
|
||||||
|
TemporalConstraint.class.getSimpleName(), temporalConstraint.toString(),
|
||||||
|
Filter.class.getSimpleName(), filters);
|
||||||
|
return reallyQuery(usageRecordType, temporalConstraint, filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of key valid for queries a certain usage record type
|
||||||
|
* @param usageRecordType the usage record type
|
||||||
|
* @return a set containing the list of key
|
||||||
|
* @throws Exception if fails
|
||||||
|
*/
|
||||||
|
public abstract Set<String> getKeys(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType) throws Exception;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of possible values for a key for a certain usage record type
|
||||||
|
* @param usageRecordType the usage record type
|
||||||
|
* @param key the key
|
||||||
|
* @return a set containing the list of possible values
|
||||||
|
* @throws Exception if fails
|
||||||
|
*/
|
||||||
|
public abstract Set<String> getPossibleValuesForKey(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType, String key) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the connection to persistence
|
||||||
|
* @throws Exception if the close fails
|
||||||
|
*/
|
||||||
|
public abstract void close() throws Exception;
|
||||||
|
|
||||||
|
}
|
|
@ -9,12 +9,12 @@ import org.gcube.common.resources.gcore.ServiceEndpoint;
|
||||||
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class AccountingPersistenceQueryConfiguration extends AccountingPersistenceConfiguration {
|
public class AccountingPersistenceBackendQueryConfiguration extends AccountingPersistenceConfiguration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Constructor
|
* Default Constructor
|
||||||
*/
|
*/
|
||||||
public AccountingPersistenceQueryConfiguration(){
|
public AccountingPersistenceBackendQueryConfiguration(){
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ public class AccountingPersistenceQueryConfiguration extends AccountingPersisten
|
||||||
* @param username the username to connect to persistence
|
* @param username the username to connect to persistence
|
||||||
* @param password the password to connect to persistence
|
* @param password the password to connect to persistence
|
||||||
*/
|
*/
|
||||||
public AccountingPersistenceQueryConfiguration(URI uri, String username, String password){
|
public AccountingPersistenceBackendQueryConfiguration(URI uri, String username, String password){
|
||||||
super(uri, username, password);
|
super(uri, username, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ public class AccountingPersistenceQueryConfiguration extends AccountingPersisten
|
||||||
* @param persistenceClassName The classname of the persistence to instantiate
|
* @param persistenceClassName The classname of the persistence to instantiate
|
||||||
* @throws Exception if fails
|
* @throws Exception if fails
|
||||||
*/
|
*/
|
||||||
public AccountingPersistenceQueryConfiguration(String persistenceClassName) throws Exception{
|
public AccountingPersistenceBackendQueryConfiguration(String persistenceClassName) throws Exception{
|
||||||
super.init();
|
super.init();
|
||||||
ServiceEndpoint serviceEndpoint = getServiceEndpoint(SERVICE_ENDPOINT_CATEGORY, SERVICE_ENDPOINT_NAME, persistenceClassName);
|
ServiceEndpoint serviceEndpoint = getServiceEndpoint(SERVICE_ENDPOINT_CATEGORY, SERVICE_ENDPOINT_NAME, persistenceClassName);
|
||||||
setValues(serviceEndpoint, persistenceClassName);
|
setValues(serviceEndpoint, persistenceClassName);
|
|
@ -0,0 +1,82 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package org.gcube.accounting.analytics.persistence;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
|
||||||
|
import org.gcube.accounting.analytics.exception.NoAvailableScopeException;
|
||||||
|
import org.gcube.accounting.analytics.exception.NoUsableAccountingPersistenceQueryFound;
|
||||||
|
import org.gcube.common.scope.api.ScopeProvider;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
||||||
|
*/
|
||||||
|
public abstract class AccountingPersistenceBackendQueryFactory {
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(AccountingPersistenceBackendQueryFactory.class);
|
||||||
|
|
||||||
|
private static Map<String, AccountingPersistenceBackendQuery> accountingPersistenceQueries;
|
||||||
|
|
||||||
|
static {
|
||||||
|
accountingPersistenceQueries = new HashMap<String, AccountingPersistenceBackendQuery>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return AccountingPersistenceQuery instance
|
||||||
|
* @throws NoAvailableScopeException if no configuration is found on IS for
|
||||||
|
* the current scope
|
||||||
|
* @throws NoUsableAccountingPersistenceQueryFound if fails to instantiate
|
||||||
|
* the #AccountingPersistenceQuery
|
||||||
|
*/
|
||||||
|
public synchronized static AccountingPersistenceBackendQuery getInstance() throws NoAvailableScopeException, NoUsableAccountingPersistenceQueryFound {
|
||||||
|
String scope = ScopeProvider.instance.get();
|
||||||
|
if(scope==null){
|
||||||
|
throw new NoAvailableScopeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountingPersistenceBackendQuery accountingPersistenceQuery = accountingPersistenceQueries.get(scope);
|
||||||
|
if(accountingPersistenceQuery==null){
|
||||||
|
|
||||||
|
try {
|
||||||
|
ServiceLoader<AccountingPersistenceBackendQuery> serviceLoader = ServiceLoader.load(AccountingPersistenceBackendQuery.class);
|
||||||
|
for (AccountingPersistenceBackendQuery found : serviceLoader) {
|
||||||
|
try {
|
||||||
|
String foundClassName = found.getClass().getSimpleName();
|
||||||
|
logger.debug("Testing {}", foundClassName);
|
||||||
|
|
||||||
|
AccountingPersistenceBackendQueryConfiguration configuration = new AccountingPersistenceBackendQueryConfiguration(foundClassName);
|
||||||
|
found.prepareConnection(configuration);
|
||||||
|
accountingPersistenceQuery = found;
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.debug(String.format("%s not initialized correctly. It will not be used", found.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(Exception e){
|
||||||
|
throw new NoUsableAccountingPersistenceQueryFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(accountingPersistenceQuery==null){
|
||||||
|
throw new NoUsableAccountingPersistenceQueryFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
accountingPersistenceQueries.put(scope, accountingPersistenceQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountingPersistenceQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected AccountingPersistenceBackendQueryFactory(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -12,21 +12,25 @@ import org.gcube.accounting.analytics.Filter;
|
||||||
import org.gcube.accounting.analytics.Info;
|
import org.gcube.accounting.analytics.Info;
|
||||||
import org.gcube.accounting.analytics.TemporalConstraint;
|
import org.gcube.accounting.analytics.TemporalConstraint;
|
||||||
import org.gcube.accounting.datamodel.AggregatedUsageRecord;
|
import org.gcube.accounting.datamodel.AggregatedUsageRecord;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class AccountingPersistenceQuery {
|
public class AccountingPersistenceQuery {
|
||||||
|
|
||||||
|
private static final AccountingPersistenceQuery accountingPersistenceQuery;
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(AccountingPersistenceQuery.class);
|
private AccountingPersistenceQuery(){}
|
||||||
|
|
||||||
protected abstract void prepareConnection(AccountingPersistenceQueryConfiguration configuration) throws Exception;
|
static {
|
||||||
|
accountingPersistenceQuery = new AccountingPersistenceQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static synchronized AccountingPersistenceQuery getInstance(){
|
||||||
|
return accountingPersistenceQuery;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract Map<Calendar, Info> reallyQuery(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType,
|
|
||||||
TemporalConstraint temporalConstraint, List<Filter> filters) throws Exception;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the persistence obtaining a Map where the date is the key and
|
* Query the persistence obtaining a Map where the date is the key and
|
||||||
|
@ -43,10 +47,7 @@ public abstract class AccountingPersistenceQuery {
|
||||||
*/
|
*/
|
||||||
public Map<Calendar, Info> query(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType,
|
public Map<Calendar, Info> query(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType,
|
||||||
TemporalConstraint temporalConstraint, List<Filter> filters) throws Exception{
|
TemporalConstraint temporalConstraint, List<Filter> filters) throws Exception{
|
||||||
logger.trace("Request query: UsageRecordType={}, {}={}, {}s={}", usageRecordType.newInstance().getUsageRecordType(),
|
return AccountingPersistenceBackendQueryFactory.getInstance().query(usageRecordType, temporalConstraint, filters);
|
||||||
TemporalConstraint.class.getSimpleName(), temporalConstraint.toString(),
|
|
||||||
Filter.class.getSimpleName(), filters);
|
|
||||||
return reallyQuery(usageRecordType, temporalConstraint, filters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +56,9 @@ public abstract class AccountingPersistenceQuery {
|
||||||
* @return a set containing the list of key
|
* @return a set containing the list of key
|
||||||
* @throws Exception if fails
|
* @throws Exception if fails
|
||||||
*/
|
*/
|
||||||
public abstract Set<String> getKeys(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType) throws Exception;
|
public Set<String> getKeys(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType) throws Exception {
|
||||||
|
return AccountingPersistenceBackendQueryFactory.getInstance().getKeys(usageRecordType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,12 +68,16 @@ public abstract class AccountingPersistenceQuery {
|
||||||
* @return a set containing the list of possible values
|
* @return a set containing the list of possible values
|
||||||
* @throws Exception if fails
|
* @throws Exception if fails
|
||||||
*/
|
*/
|
||||||
public abstract Set<String> getPossibleValuesForKey(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType, String key) throws Exception;
|
public Set<String> getPossibleValuesForKey(@SuppressWarnings("rawtypes") Class<? extends AggregatedUsageRecord> usageRecordType, String key) throws Exception {
|
||||||
|
return AccountingPersistenceBackendQueryFactory.getInstance().getPossibleValuesForKey(usageRecordType, key);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the connection to persistence
|
* Close the connection to persistence
|
||||||
* @throws Exception if the close fails
|
* @throws Exception if the close fails
|
||||||
*/
|
*/
|
||||||
public abstract void close() throws Exception;
|
public void close() throws Exception {
|
||||||
|
AccountingPersistenceBackendQueryFactory.getInstance().close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,80 +3,14 @@
|
||||||
*/
|
*/
|
||||||
package org.gcube.accounting.analytics.persistence;
|
package org.gcube.accounting.analytics.persistence;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.ServiceLoader;
|
|
||||||
|
|
||||||
import org.gcube.accounting.analytics.exception.NoAvailableScopeException;
|
|
||||||
import org.gcube.accounting.analytics.exception.NoUsableAccountingPersistenceQueryFound;
|
|
||||||
import org.gcube.common.scope.api.ScopeProvider;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
* @author Luca Frosini (ISTI - CNR) http://www.lucafrosini.com/
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
public abstract class AccountingPersistenceQueryFactory {
|
public class AccountingPersistenceQueryFactory {
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(AccountingPersistenceQueryFactory.class);
|
|
||||||
|
|
||||||
private static Map<String, AccountingPersistenceQuery> accountingPersistenceQueries;
|
public static AccountingPersistenceQuery getInstance() {
|
||||||
|
return AccountingPersistenceQuery.getInstance();
|
||||||
static {
|
|
||||||
accountingPersistenceQueries = new HashMap<String, AccountingPersistenceQuery>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return AccountingPersistenceQuery instance
|
|
||||||
* @throws NoAvailableScopeException if no configuration is found on IS for
|
|
||||||
* the current scope
|
|
||||||
* @throws NoUsableAccountingPersistenceQueryFound if fails to instantiate
|
|
||||||
* the #AccountingPersistenceQuery
|
|
||||||
*/
|
|
||||||
public synchronized static AccountingPersistenceQuery getInstance() throws NoAvailableScopeException, NoUsableAccountingPersistenceQueryFound {
|
|
||||||
String scope = ScopeProvider.instance.get();
|
|
||||||
if(scope==null){
|
|
||||||
throw new NoAvailableScopeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountingPersistenceQuery accountingPersistenceQuery = accountingPersistenceQueries.get(scope);
|
|
||||||
if(accountingPersistenceQuery==null){
|
|
||||||
|
|
||||||
try {
|
|
||||||
ServiceLoader<AccountingPersistenceQuery> serviceLoader = ServiceLoader.load(AccountingPersistenceQuery.class);
|
|
||||||
for (AccountingPersistenceQuery found : serviceLoader) {
|
|
||||||
try {
|
|
||||||
String foundClassName = found.getClass().getSimpleName();
|
|
||||||
logger.debug("Testing {}", foundClassName);
|
|
||||||
|
|
||||||
AccountingPersistenceQueryConfiguration configuration = new AccountingPersistenceQueryConfiguration(foundClassName);
|
|
||||||
found.prepareConnection(configuration);
|
|
||||||
accountingPersistenceQuery = found;
|
|
||||||
break;
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.debug(String.format("%s not initialized correctly. It will not be used", found.getClass().getSimpleName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(Exception e){
|
|
||||||
throw new NoUsableAccountingPersistenceQueryFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(accountingPersistenceQuery==null){
|
|
||||||
throw new NoUsableAccountingPersistenceQueryFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
accountingPersistenceQueries.put(scope, accountingPersistenceQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
return accountingPersistenceQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected AccountingPersistenceQueryFactory(){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue