package org.axway.grapes.server.db.mongo;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.ServerAddress;
import com.sun.jersey.api.NotFoundException;
import org.axway.grapes.server.config.DataBaseConfig;
import org.axway.grapes.server.core.options.FiltersHolder;
import org.axway.grapes.server.db.DataUtils;
import org.axway.grapes.server.db.RepositoryHandler;
import org.axway.grapes.server.db.datamodel.*;
import org.axway.grapes.server.db.datamodel.DbCredential.AvailableRoles;
import org.jongo.Jongo;
import org.jongo.MongoCollection;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.net.UnknownHostException;
import java.util.*;
import java.util.regex.Pattern;
/**
* Mongodb Handler
*
* <p>Repository Handler designed for mongodb<p/>
*
* @author jdcoffre
*/
public class MongodbHandler implements RepositoryHandler {
// cache for credentials
private LoadingCache<String, DbCredential> credentialCache;
// DB connection
private final DB db;
public MongodbHandler(final DataBaseConfig config) throws UnknownHostException {
final ServerAddress address = new ServerAddress(config.getHost() , config.getPort());
final MongoClient mongo = new MongoClient(address);
db = mongo.getDB(config.getDatastore());
if(config.getUser() != null && config.getPwd() != null){
db.authenticate(config.getUser(), config.getPwd());
}
// Init credentials' cache
credentialCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
new CacheLoader<String, DbCredential>() {
public DbCredential load(String user) {
return getCredential(user);
}
});
}
/**
* Initialize a connection with the database using Jongo.
*
* <p>WARNING: The database connection is closed only when Datastore instance is garbage collected!!!!</p>
*
* @return Jongo instance
*/
private Jongo getJongoDataStore() {
return new Jongo(db);
}
@Override
public void store(final DbCredential credential) {
final Jongo datastore = getJongoDataStore();
final MongoCollection dbCredentials = datastore.getCollection(DbCollections.DB_CREDENTIALS);
final DbCredential dbCredential = getCredential(credential.getUser());
if(dbCredential == null){
dbCredentials.save(credential);
}
else{
dbCredentials.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, dbCredential.getUser())).with(credential);
}
credentialCache.invalidate(credential.getUser());
}
@Override
public void addUserRole(final String user, final AvailableRoles role) {
final DbCredential credential = getCredential(user);
if(credential == null){
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
final Jongo datastore = getJongoDataStore();
final MongoCollection credentials = datastore.getCollection(DbCollections.DB_CREDENTIALS);
if(!credential.getRoles().contains(role)){
credential.addRole(role);
credentials.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, user))
.with("{ $set: { \""+ DbCredential.ROLES_FIELD + "\": #}} " , credential.getRoles());
}
credentialCache.invalidate(credential.getUser());
}
@Override
public void removeUserRole(final String user, final AvailableRoles role) {
final DbCredential credential = getCredential(user);
if(credential == null){
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
final Jongo datastore = getJongoDataStore();
final MongoCollection credentials = datastore.getCollection(DbCollections.DB_CREDENTIALS);
if(credential.getRoles().contains(role)){
credential.removeRole(role);
credentials.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, user))
.with("{ $set: { \""+ DbCredential.ROLES_FIELD + "\": #}} " , credential.getRoles());
}
credentialCache.invalidate(credential.getUser());
}
@Override
public DbCredential getCredential(final String user) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_CREDENTIALS)
.findOne(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, user))
.as(DbCredential.class);
}
@Override
public void store(final DbLicense license) {
final Jongo datastore = getJongoDataStore();
final MongoCollection dbLicenses = datastore.getCollection(DbCollections.DB_LICENSES);
final DbLicense dbLicense = getLicense(license.getName());
if(dbLicense == null){
dbLicenses.save(license);
}
else {
dbLicenses.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, dbLicense.getName())).with(license);
}
}
@Override
public List<String> getLicenseNames(final FiltersHolder filters) {
final Jongo datastore = getJongoDataStore();
final Iterable<DbLicense> dbLicenses = datastore.getCollection(DbCollections.DB_LICENSES)
.find().as(DbLicense.class);
final List<String> licenseNames = new ArrayList<String>();
for(DbLicense dbLicense: dbLicenses){
if(filters.shouldBeInReport(dbLicense)){
licenseNames.add(dbLicense.getName());
}
}
return licenseNames;
}
@Override
public List<DbLicense> getAllLicenses() {
final Jongo datastore = getJongoDataStore();
final Iterator<DbLicense> licenses = datastore.getCollection(DbCollections.DB_LICENSES)
.find().as(DbLicense.class).iterator();
return Lists.newArrayList(licenses);
}
@Override
public DbLicense getLicense(final String name) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_LICENSES)
.findOne(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, name))
.as(DbLicense.class);
}
@Override
public void deleteLicense(final String name) {
final DbLicense license = getLicense(name);
if(license == null){
throw new NotFoundException("The license does not exist: " + name);
}
else{
final Jongo datastore = getJongoDataStore();
datastore.getCollection(DbCollections.DB_LICENSES)
.remove(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, name));
}
}
@Override
public List<DbArtifact> getArtifacts(final FiltersHolder filters) {
final Jongo datastore = getJongoDataStore();
final List<DbArtifact> artifacts = new ArrayList<DbArtifact>();
final Iterable<DbArtifact> dbArtifacts = datastore.getCollection(DbCollections.DB_ARTIFACTS)
.find(JongoUtils.generateQuery(filters.getArtifactFieldsFilters()))
.as(DbArtifact.class);
for(DbArtifact dbArtifact: dbArtifacts){
artifacts.add(dbArtifact);
}
return artifacts;
}
@Override
public void addLicenseToArtifact(final DbArtifact artifact, final String licenseId) {
final Jongo datastore = getJongoDataStore();
final MongoCollection artifacts = datastore.getCollection(DbCollections.DB_ARTIFACTS);
artifact.addLicense(licenseId);
artifacts.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, artifact.getGavc()))
.with("{ $set: { \""+ DbArtifact.LICENCES_DB_FIELD + "\": #}} " , artifact.getLicenses());
}
@Override
public void removeLicenseFromArtifact(final DbArtifact artifact, final String licenseId) {
final Jongo datastore = getJongoDataStore();
final MongoCollection artifacts = datastore.getCollection(DbCollections.DB_ARTIFACTS);
if(artifact.getLicenses().contains(licenseId)){
artifact.removeLicense(licenseId);
artifacts.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, artifact.getGavc()))
.with("{ $set: { \""+ DbArtifact.LICENCES_DB_FIELD + "\": #}} " , artifact.getLicenses());
}
}
@Override
public void approveLicense(final DbLicense license, final Boolean approved) {
final Jongo datastore = getJongoDataStore();
final MongoCollection licenses = datastore.getCollection(DbCollections.DB_LICENSES);
licenses.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, license.getName()))
.with("{ $set: { \""+ DbLicense.APPROVED_DB_FIELD + "\": #}} " , approved);
}
@Override
public void store(final DbArtifact artifact) {
final Jongo datastore = getJongoDataStore();
final MongoCollection dbArtifacts = datastore.getCollection(DbCollections.DB_ARTIFACTS);
final DbArtifact dbArtifact = getArtifact(artifact.getGavc());
if(dbArtifact == null){
dbArtifacts.save(artifact);
}
else{
// Important: merge existing license and new ones :
// * because an existing license could have been manually enforce by a user
// * because all Grapes clients are not to send license information
for(String license: dbArtifact.getLicenses()){
artifact.addLicense(license);
}
dbArtifacts.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, dbArtifact.getGavc())).with(artifact);
}
}
@Override
public List<String> getGavcs(final FiltersHolder filters) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_ARTIFACTS).distinct(DbCollections.DEFAULT_ID)
.query(JongoUtils.generateQuery(filters.getArtifactFieldsFilters())).as(String.class);
}
@Override
public List<String> getGroupIds(final FiltersHolder filters) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_ARTIFACTS).distinct(DbArtifact.GROUPID_DB_FIELD).as(String.class);
}
@Override
public List<String> getArtifactVersions(final DbArtifact artifact) {
final Jongo datastore = getJongoDataStore();
final Map<String,Object> params = new HashMap<String, Object>();
params.put(DbArtifact.GROUPID_DB_FIELD, artifact.getGroupId());
params.put(DbArtifact.ARTIFACTID_DB_FIELD, artifact.getArtifactId());
params.put(DbArtifact.CLASSIFIER_DB_FIELD, artifact.getClassifier());
params.put(DbArtifact.EXTENSION_DB_FIELD, artifact.getExtension());
return datastore.getCollection(DbCollections.DB_ARTIFACTS).distinct(DbArtifact.VERSION_DB_FIELD).
query(JongoUtils.generateQuery(params)).as(String.class);
}
@Override
public DbArtifact getArtifact(final String gavc) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_ARTIFACTS)
.findOne(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, gavc))
.as(DbArtifact.class);
}
@Override
public void deleteArtifact(final String gavc) {
final DbArtifact artifact = getArtifact(gavc);
if(artifact == null){
throw new NotFoundException("The artifact does not exist: " + gavc);
}
else{
final Jongo datastore = getJongoDataStore();
datastore.getCollection(DbCollections.DB_ARTIFACTS)
.remove(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, gavc));
}
}
@Override
public void updateDoNotUse(final DbArtifact artifact, final Boolean doNotUse) {
final Jongo datastore = getJongoDataStore();
final MongoCollection artifacts = datastore.getCollection(DbCollections.DB_ARTIFACTS);
artifacts.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, artifact.getGavc()))
.with("{ $set: { \""+ DbArtifact.DO_NOT_USE + "\": #}} " , doNotUse);
}
@Override
public void updateDownloadUrl(final DbArtifact artifact, final String downLoadUrl) {
final Jongo datastore = getJongoDataStore();
final MongoCollection artifacts = datastore.getCollection(DbCollections.DB_ARTIFACTS);
artifacts.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, artifact.getGavc()))
.with("{ $set: { \""+ DbArtifact.DOWNLOAD_URL_DB_FIELD + "\": #}} " , downLoadUrl);
}
@Override
public void updateProvider(final DbArtifact artifact, final String provider) {
final Jongo datastore = getJongoDataStore();
final MongoCollection artifacts = datastore.getCollection(DbCollections.DB_ARTIFACTS);
artifacts.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, artifact.getGavc()))
.with("{ $set: { \""+ DbArtifact.PROVIDER + "\": #}} " , provider);
}
@Override
public List<DbModule> getAncestors(final DbArtifact artifact, final FiltersHolder filters) {
final Jongo datastore = getJongoDataStore();
final Map<String, Object> queryParams = filters.getModuleFieldsFilters();
queryParams.put(DbModule.USE_DB_FIELD, artifact.getGavc());
final Iterable<DbModule> results = datastore.getCollection(DbCollections.DB_MODULES)
.find(JongoUtils.generateQuery(queryParams))
.as(DbModule.class);
final List<DbModule> ancestors = new ArrayList<DbModule>();
for(DbModule ancestor: results){
ancestors.add(ancestor);
}
return ancestors;
}
@Override
public void store(final DbModule module) {
final Jongo datastore = getJongoDataStore();
final MongoCollection dbModules = datastore.getCollection(DbCollections.DB_MODULES);
final DbModule dbModule = getModule(module.getId());
// has to be done due to mongo limitation: https://jira.mongodb.org/browse/SERVER-267
module.updateHasAndUse();
if(dbModule == null){
dbModules.save(module);
}
else{
// let's keep the old build info and override with new values if any
final Map<String,String> consolidatedBuildInfo = dbModule.getBuildInfo();
consolidatedBuildInfo.putAll(module.getBuildInfo());
module.setBuildInfo(consolidatedBuildInfo);
dbModules.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, dbModule.getId())).with(module);
}
}
@Override
public List<String> getModuleNames(final FiltersHolder filters) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_MODULES)
.distinct(DbModule.NAME_DB_FIELD)
.query(JongoUtils.generateQuery(filters.getModuleFieldsFilters()))
.as(String.class);
}
@Override
public List<String> getModuleVersions(final String name, final FiltersHolder filters) {
final Map<String, Object> params = filters.getModuleFieldsFilters();
params.put(DbModule.NAME_DB_FIELD, name);
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_MODULES).distinct(DbModule.VERSION_DB_FIELD).
query(JongoUtils.generateQuery(params)).as(String.class);
}
@Override
public DbModule getModule(final String moduleId) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_MODULES)
.findOne(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, moduleId))
.as(DbModule.class);
}
@Override
public List<DbModule> getModules(final FiltersHolder filters) {
final Jongo datastore = getJongoDataStore();
final List<DbModule> modules = new ArrayList<DbModule>();
final Iterable<DbModule> dbModules = datastore.getCollection(DbCollections.DB_MODULES)
.find(JongoUtils.generateQuery(filters.getModuleFieldsFilters()))
.as(DbModule.class);
for(DbModule dbModule: dbModules){
modules.add(dbModule);
}
return modules;
}
@Override
public void deleteModule(final String moduleId) {
final DbModule module = getModule(moduleId);
if(module == null){
throw new NotFoundException("The module does not exist: " + moduleId);
}
else{
final Jongo datastore = getJongoDataStore();
datastore.getCollection(DbCollections.DB_MODULES)
.remove(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, moduleId));
}
}
@Override
public void promoteModule(final DbModule module) {
final Jongo datastore = getJongoDataStore();
final MongoCollection modules = datastore.getCollection(DbCollections.DB_MODULES);
modules.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, module.getId()))
.with("{ $set: { \""+ DbModule.PROMOTION_DB_FIELD + "\": #}} " , Boolean.TRUE);
}
@Override
public DbModule getRootModuleOf(final String gavc){
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_MODULES)
.findOne(JongoUtils.generateQuery(DbModule.HAS_DB_FIELD, gavc))
.as(DbModule.class);
}
@Override
public DbModule getModuleOf(final String gavc) {
final DbModule module = getRootModuleOf(gavc);
// It may be a submodule...
if(module != null && !module.getArtifacts().contains(gavc)){
for(DbModule submodule: DataUtils.getAllSubmodules(module)){
if(submodule.getArtifacts().contains(gavc)){
return submodule;
}
}
}
return module;
}
@Override
public List<String> getOrganizationNames() {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_ORGANIZATION).distinct(DbCollections.DEFAULT_ID).as(String.class);
}
@Override
public DbOrganization getOrganization(String name) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_ORGANIZATION)
.findOne(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, name))
.as(DbOrganization.class);
}
@Override
public void deleteOrganization(String organizationId) {
final Jongo datastore = getJongoDataStore();
datastore.getCollection(DbCollections.DB_ORGANIZATION)
.remove(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, organizationId));
}
@Override
public void store(DbOrganization organization) {
final Jongo datastore = getJongoDataStore();
final MongoCollection dbOrganizations = datastore.getCollection(DbCollections.DB_ORGANIZATION);
if(getOrganization(organization.getName()) == null){
dbOrganizations.save(organization);
}
else{
dbOrganizations.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, organization.getName())).with(organization);
}
}
@Override
public void addModulesOrganization(final String corporateGidPrefix, final DbOrganization organization){
final Jongo datastore = getJongoDataStore();
datastore.getCollection(DbCollections.DB_MODULES)
.update("{ "+DbModule.HAS_DB_FIELD+" :#}", Pattern.compile(corporateGidPrefix + "*"))
.multi()
.with("{$set: " + JongoUtils.generateQuery(DbModule.ORGANIZATION_DB_FIELD, organization.getName()) + "}");
}
@Override
public void removeModulesOrganization(final String corporateGidPrefix, final DbOrganization organization){
final Jongo datastore = getJongoDataStore();
datastore.getCollection(DbCollections.DB_MODULES)
.update("{ $and: [" +
"{ " + DbModule.HAS_DB_FIELD + " :#} ," +
JongoUtils.generateQuery(DbModule.ORGANIZATION_DB_FIELD, organization.getName()) + "]}"
, Pattern.compile(corporateGidPrefix + "*"))
.multi()
.with("{$set: { " + DbModule.ORGANIZATION_DB_FIELD + " : \"\"}}");
}
@Override
public void removeModulesOrganization(final DbOrganization organization){
final Jongo datastore = getJongoDataStore();
datastore.getCollection(DbCollections.DB_MODULES)
.update(JongoUtils.generateQuery(DbModule.ORGANIZATION_DB_FIELD, organization.getName()))
.with("{$set: { "+DbModule.ORGANIZATION_DB_FIELD+" : \"\"}}");
}
@Override
public List<DbOrganization> getAllOrganizations() {
final Jongo datastore = getJongoDataStore();
final Iterable<DbOrganization> organizations = datastore
.getCollection(DbCollections.DB_ORGANIZATION).find().as(DbOrganization.class);
return Lists.newArrayList(organizations);
}
@Override
public void store(final DbProduct dbProduct) {
final Jongo datastore = getJongoDataStore();
final MongoCollection dbProducts = datastore.getCollection(DbCollections.DB_PRODUCT);
if(getOrganization(dbProduct.getName()) == null){
dbProducts.save(dbProduct);
}
else {
dbProducts.update(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, dbProduct.getName())).with(dbProduct);
}
}
@Override
public DbProduct getProduct(final String name) {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_PRODUCT)
.findOne(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, name))
.as(DbProduct.class);
}
@Override
public List<String> getProductNames() {
final Jongo datastore = getJongoDataStore();
return datastore.getCollection(DbCollections.DB_PRODUCT).distinct(DbCollections.DEFAULT_ID).as(String.class);
}
@Override
public void deleteProduct(String name) {
final Jongo datastore = getJongoDataStore();
datastore.getCollection(DbCollections.DB_PRODUCT)
.remove(JongoUtils.generateQuery(DbCollections.DEFAULT_ID, name));
}
}