This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.metadata.repository.cassandra;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import com.google.common.base.Predicate;
023import com.google.common.collect.Iterables;
024import me.prettyprint.cassandra.serializers.LongSerializer;
025import me.prettyprint.cassandra.serializers.StringSerializer;
026import me.prettyprint.cassandra.service.template.ColumnFamilyResult;
027import me.prettyprint.cassandra.service.template.ColumnFamilyTemplate;
028import me.prettyprint.cassandra.service.template.ColumnFamilyUpdater;
029import me.prettyprint.cassandra.service.template.ThriftColumnFamilyTemplate;
030import me.prettyprint.hector.api.Keyspace;
031import me.prettyprint.hector.api.beans.ColumnSlice;
032import me.prettyprint.hector.api.beans.OrderedRows;
033import me.prettyprint.hector.api.beans.Row;
034import me.prettyprint.hector.api.exceptions.HInvalidRequestException;
035import me.prettyprint.hector.api.factory.HFactory;
036import me.prettyprint.hector.api.mutation.MutationResult;
037import me.prettyprint.hector.api.mutation.Mutator;
038import me.prettyprint.hector.api.query.QueryResult;
039import me.prettyprint.hector.api.query.RangeSlicesQuery;
040import org.apache.archiva.checksum.ChecksumAlgorithm;
041import org.apache.archiva.configuration.ArchivaConfiguration;
042import org.apache.archiva.metadata.QueryParameter;
043import org.apache.archiva.metadata.model.ArtifactMetadata;
044import org.apache.archiva.metadata.model.CiManagement;
045import org.apache.archiva.metadata.model.Dependency;
046import org.apache.archiva.metadata.model.FacetedMetadata;
047import org.apache.archiva.metadata.model.IssueManagement;
048import org.apache.archiva.metadata.model.License;
049import org.apache.archiva.metadata.model.MailingList;
050import org.apache.archiva.metadata.model.MetadataFacet;
051import org.apache.archiva.metadata.model.MetadataFacetFactory;
052import org.apache.archiva.metadata.model.Organization;
053import org.apache.archiva.metadata.model.ProjectMetadata;
054import org.apache.archiva.metadata.model.ProjectVersionMetadata;
055import org.apache.archiva.metadata.model.ProjectVersionReference;
056import org.apache.archiva.metadata.model.Scm;
057import org.apache.archiva.metadata.repository.AbstractMetadataRepository;
058import org.apache.archiva.metadata.repository.MetadataRepository;
059import org.apache.archiva.metadata.repository.MetadataRepositoryException;
060import org.apache.archiva.metadata.repository.MetadataResolutionException;
061import org.apache.archiva.metadata.repository.MetadataService;
062import org.apache.archiva.metadata.repository.RepositorySession;
063import org.apache.archiva.metadata.repository.cassandra.model.ArtifactMetadataModel;
064import org.apache.archiva.metadata.repository.cassandra.model.MetadataFacetModel;
065import org.apache.archiva.metadata.repository.cassandra.model.Namespace;
066import org.apache.archiva.metadata.repository.cassandra.model.Project;
067import org.apache.archiva.metadata.repository.cassandra.model.ProjectVersionMetadataModel;
068import org.apache.archiva.metadata.repository.cassandra.model.Repository;
069import org.apache.commons.lang3.StringUtils;
070import org.modelmapper.ModelMapper;
071import org.slf4j.Logger;
072import org.slf4j.LoggerFactory;
073
074import javax.annotation.ParametersAreNonnullByDefault;
075import java.time.Instant;
076import java.time.ZonedDateTime;
077import java.util.*;
078import java.util.function.BiFunction;
079import java.util.function.Consumer;
080import java.util.stream.Collectors;
081import java.util.stream.Stream;
082import java.util.stream.StreamSupport;
083
084import static org.apache.archiva.metadata.model.ModelInfo.STORAGE_TZ;
085import static org.apache.archiva.metadata.repository.cassandra.CassandraUtils.*;
086import static org.apache.archiva.metadata.repository.cassandra.model.ColumnNames.*;
087
088/**
089 * @author Olivier Lamy
090 * @since 2.0.0
091 */
092@ParametersAreNonnullByDefault
093public class CassandraMetadataRepository
094    extends AbstractMetadataRepository implements MetadataRepository
095{
096
097    private static final String ARTIFACT_METADATA_MODEL_KEY = "artifactMetadataModel.key";
098    private Logger logger = LoggerFactory.getLogger( getClass() );
099
100    private ArchivaConfiguration configuration;
101
102    private final CassandraArchivaManager cassandraArchivaManager;
103
104    private final ColumnFamilyTemplate<String, String> projectVersionMetadataTemplate;
105
106    private final ColumnFamilyTemplate<String, String> projectTemplate;
107
108    private final ColumnFamilyTemplate<String, String> artifactMetadataTemplate;
109
110    private final ColumnFamilyTemplate<String, String> metadataFacetTemplate;
111
112    private final ColumnFamilyTemplate<String, String> mailingListTemplate;
113
114    private final ColumnFamilyTemplate<String, String> licenseTemplate;
115
116    private final ColumnFamilyTemplate<String, String> dependencyTemplate;
117
118    private final ColumnFamilyTemplate<String, String> checksumTemplate;
119
120    private final Keyspace keyspace;
121
122    private final StringSerializer ss = StringSerializer.get();
123
124    public CassandraMetadataRepository( MetadataService metadataService,
125                                        ArchivaConfiguration configuration,
126                                        CassandraArchivaManager cassandraArchivaManager )
127    {
128        super( metadataService );
129        this.configuration = configuration;
130        this.cassandraArchivaManager = cassandraArchivaManager;
131        this.keyspace = cassandraArchivaManager.getKeyspace();
132
133        this.projectVersionMetadataTemplate =
134            new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
135                                              cassandraArchivaManager.getProjectVersionMetadataFamilyName(), //
136                                              StringSerializer.get(), //
137                                              StringSerializer.get() );
138
139        this.projectTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
140                                                                 cassandraArchivaManager.getProjectFamilyName(), //
141                                                                 //
142                                                                 StringSerializer.get(), //
143                                                                 StringSerializer.get() );
144
145        this.artifactMetadataTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
146                                                                          cassandraArchivaManager.getArtifactMetadataFamilyName(),
147                                                                          StringSerializer.get(), //
148                                                                          StringSerializer.get() );
149
150        this.metadataFacetTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
151                                                                       cassandraArchivaManager.getMetadataFacetFamilyName(),
152                                                                       //
153                                                                       StringSerializer.get(), //
154                                                                       StringSerializer.get() );
155
156        this.mailingListTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
157                                                                     cassandraArchivaManager.getMailingListFamilyName(),
158                                                                     //
159                                                                     StringSerializer.get(), //
160                                                                     StringSerializer.get() );
161
162        this.licenseTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
163                                                                 cassandraArchivaManager.getLicenseFamilyName(),
164                                                                 //
165                                                                 StringSerializer.get(), //
166                                                                 StringSerializer.get() );
167
168        this.dependencyTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
169                                                                    cassandraArchivaManager.getDependencyFamilyName(),
170                                                                    //
171                                                                    StringSerializer.get(), //
172                                                                    StringSerializer.get() );
173
174        this.checksumTemplate = new ThriftColumnFamilyTemplate<>( cassandraArchivaManager.getKeyspace(), //
175                cassandraArchivaManager.getChecksumFamilyName(),
176                //
177                StringSerializer.get(), //
178                StringSerializer.get() );
179    }
180
181
182    /**
183     * if the repository doesn't exist it will be created
184     *
185     * @param repositoryId
186     * @return
187     */
188    public Repository getOrCreateRepository( String repositoryId )
189        throws MetadataRepositoryException
190    {
191        String cf = cassandraArchivaManager.getRepositoryFamilyName();
192
193        QueryResult<OrderedRows<String, String, String>> result = HFactory //
194            .createRangeSlicesQuery( keyspace, StringSerializer.get(), StringSerializer.get(),
195                                     StringSerializer.get() ) //
196            .setColumnFamily( cf ) //
197            .setColumnNames( REPOSITORY_NAME.toString() ) //
198            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
199            .execute();
200
201        if ( result.get().getCount() < 1 )
202        {
203            // we need to create the repository
204            Repository repository = new Repository( repositoryId );
205
206            try
207            {
208                MutationResult mutationResult = HFactory.createMutator( keyspace, StringSerializer.get() ) //
209                    .addInsertion( repositoryId, cf,
210                                   CassandraUtils.column( REPOSITORY_NAME.toString(), repository.getName() ) ) //
211                    .execute();
212                logger.debug( "time to insert repository: {}", mutationResult.getExecutionTimeMicro() );
213                return repository;
214            }
215            catch ( HInvalidRequestException e )
216            {
217                logger.error( e.getMessage(), e );
218                throw new MetadataRepositoryException( e.getMessage(), e );
219            }
220
221        }
222
223        return new Repository(
224            result.get().getList().get( 0 ).getColumnSlice().getColumnByName( REPOSITORY_NAME.toString() ).getValue() );
225    }
226
227
228    protected Repository getRepository( String repositoryId )
229        throws MetadataRepositoryException
230    {
231
232        QueryResult<OrderedRows<String, String, String>> result = HFactory //
233            .createRangeSlicesQuery( keyspace, StringSerializer.get(), StringSerializer.get(),
234                                     StringSerializer.get() ) //
235            .setColumnFamily( cassandraArchivaManager.getRepositoryFamilyName() ) //
236            .setColumnNames( REPOSITORY_NAME.toString() ) //
237            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
238            .execute();
239        return ( result.get().getCount() > 0 ) ? new Repository( repositoryId ) : null;
240    }
241
242    @Override
243    public void updateNamespace( RepositorySession session, String repositoryId, String namespaceId )
244        throws MetadataRepositoryException
245    {
246        updateOrAddNamespace( repositoryId, namespaceId );
247    }
248
249    private Namespace updateOrAddNamespace( String repositoryId, String namespaceId )
250        throws MetadataRepositoryException
251    {
252        try
253        {
254            Repository repository = getOrCreateRepository( repositoryId );
255
256            String key =
257                new Namespace.KeyBuilder().withNamespace( namespaceId ).withRepositoryId( repositoryId ).build();
258
259            Namespace namespace = getNamespace( repositoryId, namespaceId );
260            if ( namespace == null )
261            {
262                String cf = cassandraArchivaManager.getNamespaceFamilyName();
263                namespace = new Namespace( namespaceId, repository );
264                HFactory.createMutator( keyspace, StringSerializer.get() )
265                    //  values
266                    .addInsertion( key, cf, CassandraUtils.column( NAME.toString(), namespace.getName() ) ) //
267                    .addInsertion( key, cf, CassandraUtils.column( REPOSITORY_NAME.toString(), repository.getName() ) ) //
268                    .execute();
269            }
270
271            return namespace;
272        }
273        catch ( HInvalidRequestException e )
274        {
275            logger.error( e.getMessage(), e );
276            throw new MetadataRepositoryException( e.getMessage(), e );
277        }
278    }
279
280    protected Namespace getNamespace( String repositoryId, String namespaceId )
281    {
282
283        QueryResult<OrderedRows<String, String, String>> result = HFactory //
284            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
285            .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
286            .setColumnNames( REPOSITORY_NAME.toString(), NAME.toString() ) //
287            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
288            .addEqualsExpression( NAME.toString(), namespaceId ) //
289            .execute();
290        if ( result.get().getCount() > 0 )
291        {
292            ColumnSlice<String, String> columnSlice = result.get().getList().get( 0 ).getColumnSlice();
293            return new Namespace( getStringValue( columnSlice, NAME.toString() ), //
294                                  new Repository( getStringValue( columnSlice, REPOSITORY_NAME.toString() ) ) );
295
296        }
297        return null;
298    }
299
300
301    @Override
302    public void removeNamespace( RepositorySession session, String repositoryId, String namespaceId )
303        throws MetadataRepositoryException
304    {
305
306        try
307        {
308            String key = new Namespace.KeyBuilder() //
309                .withNamespace( namespaceId ) //
310                .withRepositoryId( repositoryId ) //
311                .build();
312
313            HFactory.createMutator( cassandraArchivaManager.getKeyspace(), new StringSerializer() ) //
314                .addDeletion( key, cassandraArchivaManager.getNamespaceFamilyName() ) //
315                .execute();
316
317            QueryResult<OrderedRows<String, String, String>> result = HFactory //
318                .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
319                .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
320                .setColumnNames( REPOSITORY_NAME.toString() ) //
321                .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
322                .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
323                .execute();
324
325            for ( Row<String, String, String> row : result.get() )
326            {
327                this.projectTemplate.deleteRow( row.getKey() );
328            }
329
330            result = HFactory //
331                .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
332                .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
333                .setColumnNames( REPOSITORY_NAME.toString() ) //
334                .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
335                .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
336                .execute();
337
338            for ( Row<String, String, String> row : result.get() )
339            {
340                this.projectVersionMetadataTemplate.deleteRow( row.getKey() );
341                removeMailingList( row.getKey() );
342            }
343
344            result = HFactory //
345                .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
346                .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
347                .setColumnNames( REPOSITORY_NAME.toString() ) //
348                .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
349                .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
350                .execute();
351
352            for ( Row<String, String, String> row : result.get() )
353            {
354                this.artifactMetadataTemplate.deleteRow( row.getKey() );
355            }
356
357            result = HFactory //
358                .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
359                .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
360                .setColumnNames( REPOSITORY_NAME.toString() ) //
361                .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
362                .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
363                .execute();
364
365            for ( Row<String, String, String> row : result.get() )
366            {
367                this.metadataFacetTemplate.deleteRow( row.getKey() );
368            }
369
370        }
371        catch ( HInvalidRequestException e )
372        {
373            logger.error( e.getMessage(), e );
374            throw new MetadataRepositoryException( e.getMessage(), e );
375        }
376    }
377
378
379    @Override
380    public void removeRepository( RepositorySession session, final String repositoryId )
381        throws MetadataRepositoryException
382    {
383
384        // TODO use cql queries to delete all
385        List<String> namespacesKey = new ArrayList<>();
386
387        QueryResult<OrderedRows<String, String, String>> result = HFactory //
388            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
389            .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
390            .setColumnNames( REPOSITORY_NAME.toString() ) //
391            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
392            .execute();
393
394        for ( Row<String, String, String> row : result.get().getList() )
395        {
396            namespacesKey.add( row.getKey() );
397        }
398
399        HFactory.createMutator( cassandraArchivaManager.getKeyspace(), ss ) //
400            .addDeletion( namespacesKey, cassandraArchivaManager.getNamespaceFamilyName() ) //
401            .execute();
402
403        //delete repositoryId
404        HFactory.createMutator( cassandraArchivaManager.getKeyspace(), ss ) //
405            .addDeletion( repositoryId, cassandraArchivaManager.getRepositoryFamilyName() ) //
406            .execute();
407
408        result = HFactory //
409            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
410            .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
411            .setColumnNames( REPOSITORY_NAME.toString() ) //
412            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
413            .execute();
414
415        for ( Row<String, String, String> row : result.get() )
416        {
417            this.projectTemplate.deleteRow( row.getKey() );
418        }
419
420        result = HFactory //
421            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
422            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
423            .setColumnNames( REPOSITORY_NAME.toString() ) //
424            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
425            .execute();
426
427        for ( Row<String, String, String> row : result.get() )
428        {
429            this.projectVersionMetadataTemplate.deleteRow( row.getKey() );
430            removeMailingList( row.getKey() );
431        }
432
433        result = HFactory //
434            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
435            .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
436            .setColumnNames( REPOSITORY_NAME.toString() ) //
437            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
438            .execute();
439
440        for ( Row<String, String, String> row : result.get() )
441        {
442            this.artifactMetadataTemplate.deleteRow( row.getKey() );
443        }
444
445        result = HFactory //
446            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
447            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
448            .setColumnNames( REPOSITORY_NAME.toString() ) //
449            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
450            .execute();
451
452        for ( Row<String, String, String> row : result.get() )
453        {
454            this.metadataFacetTemplate.deleteRow( row.getKey() );
455        }
456
457
458    }
459
460    // FIXME this one need peformance improvement maybe a cache?
461    @Override
462    public List<String> getRootNamespaces( RepositorySession session, final String repoId )
463        throws MetadataResolutionException
464    {
465
466        QueryResult<OrderedRows<String, String, String>> result = HFactory //
467            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
468            .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
469            .setColumnNames( NAME.toString() ) //
470            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
471            .execute();
472
473        Set<String> namespaces = new HashSet<String>( result.get().getCount() );
474
475        for ( Row<String, String, String> row : result.get() )
476        {
477            namespaces.add( StringUtils.substringBefore( getStringValue( row.getColumnSlice(), NAME.toString() ), "." ) );
478        }
479
480        return new ArrayList<>( namespaces );
481    }
482
483    // FIXME this one need peformance improvement maybe a cache?
484    @Override
485    public List<String> getChildNamespaces( RepositorySession session, final String repoId, final String namespaceId )
486        throws MetadataResolutionException
487    {
488
489        QueryResult<OrderedRows<String, String, String>> result = HFactory //
490            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
491            .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
492            .setColumnNames( NAME.toString() ) //
493            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
494            .execute();
495
496        List<String> namespaces = new ArrayList<>( result.get().getCount() );
497
498        for ( Row<String, String, String> row : result.get() )
499        {
500            String currentNamespace = getStringValue( row.getColumnSlice(), NAME.toString() );
501            if ( StringUtils.startsWith( currentNamespace, namespaceId ) //
502                && ( StringUtils.length( currentNamespace ) > StringUtils.length( namespaceId ) ) )
503            {
504                // store after namespaceId '.' but before next '.'
505                // call org namespace org.apache.maven.shared -> stored apache
506
507                String calledNamespace = StringUtils.endsWith( namespaceId, "." ) ? namespaceId : namespaceId + ".";
508                String storedNamespace = StringUtils.substringAfter( currentNamespace, calledNamespace );
509
510                storedNamespace = StringUtils.substringBefore( storedNamespace, "." );
511
512                namespaces.add( storedNamespace );
513            }
514        }
515
516        return namespaces;
517
518    }
519
520    // only use for testing purpose
521    protected List<String> getNamespaces( final String repoId )
522        throws MetadataResolutionException
523    {
524
525        QueryResult<OrderedRows<String, String, String>> result = HFactory //
526            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
527            .setColumnFamily( cassandraArchivaManager.getNamespaceFamilyName() ) //
528            .setColumnNames( NAME.toString() ) //
529            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
530            .execute();
531
532        List<String> namespaces = new ArrayList<>( result.get().getCount() );
533
534        for ( Row<String, String, String> row : result.get() )
535        {
536            namespaces.add( getStringValue( row.getColumnSlice(), NAME.toString() ) );
537        }
538
539        return namespaces;
540    }
541
542
543    @Override
544    public void updateProject( RepositorySession session, String repositoryId, ProjectMetadata projectMetadata )
545        throws MetadataRepositoryException
546    {
547
548        QueryResult<OrderedRows<String, String, String>> result = HFactory //
549            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
550            .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
551            .setColumnNames( PROJECT_ID.toString() ) //
552            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
553            .addEqualsExpression( NAMESPACE_ID.toString(), projectMetadata.getNamespace() ) //
554            .addEqualsExpression( PROJECT_ID.toString(), projectMetadata.getId() ) //
555            .execute();
556
557        // project exists ? if yes return nothing to update here
558        if ( result.get().getCount() > 0 )
559        {
560            return;
561        }
562        else
563        {
564            Namespace namespace = updateOrAddNamespace( repositoryId, projectMetadata.getNamespace() );
565
566            String key =
567                new Project.KeyBuilder().withProjectId( projectMetadata.getId() ).withNamespace( namespace ).build();
568
569            String cf = cassandraArchivaManager.getProjectFamilyName();
570            projectTemplate.createMutator()
571                //  values
572                .addInsertion( key, cf, CassandraUtils.column( PROJECT_ID.toString(), projectMetadata.getId() ) ) //
573                .addInsertion( key, cf, CassandraUtils.column( REPOSITORY_NAME.toString(), repositoryId ) ) //
574                .addInsertion( key, cf, CassandraUtils.column( NAMESPACE_ID.toString(), projectMetadata.getNamespace() ) )//
575                .execute();
576        }
577    }
578
579    @Override
580    public List<String> getProjects( RepositorySession session, final String repoId, final String namespace )
581        throws MetadataResolutionException
582    {
583
584        QueryResult<OrderedRows<String, String, String>> result = HFactory //
585            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
586            .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
587            .setColumnNames( PROJECT_ID.toString() ) //
588            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
589            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
590            .execute();
591
592        final Set<String> projects = new HashSet<String>( result.get().getCount() );
593
594        for ( Row<String, String, String> row : result.get() )
595        {
596            projects.add( getStringValue( row.getColumnSlice(), PROJECT_ID.toString() ) );
597        }
598
599        return new ArrayList<>( projects );
600    }
601
602    @Override
603    public void removeProject( RepositorySession session, final String repositoryId, final String namespaceId, final String projectId )
604        throws MetadataRepositoryException
605    {
606
607        String key = new Project.KeyBuilder() //
608            .withProjectId( projectId ) //
609            .withNamespace( new Namespace( namespaceId, new Repository( repositoryId ) ) ) //
610            .build();
611
612        this.projectTemplate.deleteRow( key );
613
614        QueryResult<OrderedRows<String, String, String>> result = HFactory //
615            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
616            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
617            .setColumnNames( ID.toString() ) //
618            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
619            .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
620            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
621            .execute();
622
623        for ( Row<String, String, String> row : result.get() )
624        {
625            this.projectVersionMetadataTemplate.deleteRow( row.getKey() );
626            removeMailingList( row.getKey() );
627        }
628
629        result = HFactory //
630            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
631            .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
632            .setColumnNames( PROJECT_ID.toString() ) //
633            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
634            .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
635            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
636            .execute();
637
638        for ( Row<String, String, String> row : result.get() )
639        {
640            this.artifactMetadataTemplate.deleteRow( row.getKey() );
641        }
642    }
643
644    @Override
645    public List<String> getProjectVersions( RepositorySession session, final String repoId, final String namespace, final String projectId )
646        throws MetadataResolutionException
647    {
648
649        QueryResult<OrderedRows<String, String, String>> result = HFactory //
650            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
651            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
652            .setColumnNames( PROJECT_VERSION.toString() ) //
653            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
654            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
655            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
656            .execute();
657
658        int count = result.get().getCount();
659
660        if ( count < 1 )
661        {
662            return Collections.emptyList();
663        }
664
665        Set<String> versions = new HashSet<String>( count );
666
667        for ( Row<String, String, String> orderedRows : result.get() )
668        {
669            versions.add( getStringValue( orderedRows.getColumnSlice(), PROJECT_VERSION.toString() ) );
670        }
671
672        return new ArrayList<>( versions );
673
674    }
675
676    @Override
677    public ProjectMetadata getProject( RepositorySession session, final String repoId, final String namespace, final String id )
678        throws MetadataResolutionException
679    {
680
681        QueryResult<OrderedRows<String, String, String>> result = HFactory //
682            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
683            .setColumnFamily( cassandraArchivaManager.getProjectFamilyName() ) //
684            .setColumnNames( PROJECT_ID.toString() ) //
685            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
686            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
687            .addEqualsExpression( PROJECT_ID.toString(), id ) //
688            .execute();
689
690        int count = result.get().getCount();
691
692        if ( count < 1 )
693        {
694            return null;
695        }
696
697        ProjectMetadata projectMetadata = new ProjectMetadata();
698        projectMetadata.setId( id );
699        projectMetadata.setNamespace( namespace );
700
701        logger.debug( "getProject repoId: {}, namespace: {}, projectId: {} -> {}", repoId, namespace, id,
702                      projectMetadata );
703
704        return projectMetadata;
705    }
706
707    protected ProjectVersionMetadataModel mapProjectVersionMetadataModel( ColumnSlice<String, String> columnSlice )
708    {
709        ProjectVersionMetadataModel projectVersionMetadataModel = new ProjectVersionMetadataModel();
710        projectVersionMetadataModel.setId( getStringValue( columnSlice, ID.toString() ) );
711        projectVersionMetadataModel.setDescription( getStringValue( columnSlice, DESCRIPTION.toString() ) );
712        projectVersionMetadataModel.setName( getStringValue( columnSlice, NAME.toString() ) );
713        Namespace namespace = new Namespace( getStringValue( columnSlice, NAMESPACE_ID.toString() ), //
714                                             new Repository( getStringValue( columnSlice, REPOSITORY_NAME.toString() ) ) );
715        projectVersionMetadataModel.setNamespace( namespace );
716        projectVersionMetadataModel.setIncomplete(
717            Boolean.parseBoolean( getStringValue( columnSlice, "incomplete" ) ) );
718        projectVersionMetadataModel.setProjectId( getStringValue( columnSlice, PROJECT_ID.toString() ) );
719        projectVersionMetadataModel.setUrl( getStringValue( columnSlice, URL.toString() ) );
720        return projectVersionMetadataModel;
721    }
722
723
724    @Override
725    public void updateProjectVersion( RepositorySession session, String repositoryId, String namespaceId, String projectId,
726                                      ProjectVersionMetadata versionMetadata )
727        throws MetadataRepositoryException
728    {
729        try
730        {
731            Namespace namespace = getNamespace( repositoryId, namespaceId );
732
733            if ( namespace == null )
734            {
735                updateOrAddNamespace( repositoryId, namespaceId );
736            }
737
738            if ( getProject( session, repositoryId, namespaceId, projectId ) == null )
739            {
740                ProjectMetadata projectMetadata = new ProjectMetadata();
741                projectMetadata.setNamespace( namespaceId );
742                projectMetadata.setId( projectId );
743                updateProject( session, repositoryId, projectMetadata );
744            }
745
746        }
747        catch ( MetadataResolutionException e )
748        {
749            throw new MetadataRepositoryException( e.getMessage(), e );
750        }
751
752        QueryResult<OrderedRows<String, String, String>> result = HFactory //
753            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
754            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
755            .setColumnNames( PROJECT_VERSION.toString() ) //
756            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
757            .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
758            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
759            .addEqualsExpression( PROJECT_VERSION.toString(), versionMetadata.getId() ) //
760            .execute();
761
762        ProjectVersionMetadataModel projectVersionMetadataModel = null;
763        boolean creation = true;
764        if ( result.get().getCount() > 0 )
765        {
766            projectVersionMetadataModel =
767                mapProjectVersionMetadataModel( result.get().getList().get( 0 ).getColumnSlice() );
768            creation = false;
769        }
770        else
771        {
772            projectVersionMetadataModel = getModelMapper().map( versionMetadata, ProjectVersionMetadataModel.class );
773        }
774
775        projectVersionMetadataModel.setProjectId( projectId );
776        projectVersionMetadataModel.setNamespace( new Namespace( namespaceId, new Repository( repositoryId ) ) );
777
778        projectVersionMetadataModel.setCiManagement( versionMetadata.getCiManagement() );
779        projectVersionMetadataModel.setIssueManagement( versionMetadata.getIssueManagement() );
780        projectVersionMetadataModel.setOrganization( versionMetadata.getOrganization() );
781        projectVersionMetadataModel.setScm( versionMetadata.getScm() );
782
783        projectVersionMetadataModel.setMailingLists( versionMetadata.getMailingLists() );
784        projectVersionMetadataModel.setDependencies( versionMetadata.getDependencies() );
785        projectVersionMetadataModel.setLicenses( versionMetadata.getLicenses() );
786
787        // we don't test of repository and namespace really exist !
788        String key = new ProjectVersionMetadataModel.KeyBuilder() //
789            .withRepository( repositoryId ) //
790            .withNamespace( namespaceId ) //
791            .withProjectId( projectId ) //
792            .withProjectVersion( versionMetadata.getVersion() ) //
793            .withId( versionMetadata.getId() ) //
794            .build();
795
796        // FIXME nested objects to store!!!
797        if ( creation )
798        {
799            String cf = cassandraArchivaManager.getProjectVersionMetadataFamilyName();
800            Mutator<String> mutator = projectVersionMetadataTemplate.createMutator()
801                //  values
802                .addInsertion( key, cf, column( PROJECT_ID.toString(), projectId ) ) //
803                .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
804                .addInsertion( key, cf, column( NAMESPACE_ID.toString(), namespaceId ) )//
805                .addInsertion( key, cf, column( PROJECT_VERSION.toString(), versionMetadata.getVersion() ) ); //
806
807            addInsertion( mutator, key, cf, DESCRIPTION.toString(), versionMetadata.getDescription() );
808
809            addInsertion( mutator, key, cf, NAME.toString(), versionMetadata.getName() );
810
811            addInsertion( mutator, key, cf, "incomplete", Boolean.toString( versionMetadata.isIncomplete() ) );
812
813            addInsertion( mutator, key, cf, URL.toString(), versionMetadata.getUrl() );
814            {
815                CiManagement ci = versionMetadata.getCiManagement();
816                if ( ci != null )
817                {
818                    addInsertion( mutator, key, cf, "ciManagement.system", ci.getSystem() );
819                    addInsertion( mutator, key, cf, "ciManagement.url", ci.getUrl() );
820                }
821            }
822
823            {
824                IssueManagement issueManagement = versionMetadata.getIssueManagement();
825
826                if ( issueManagement != null )
827                {
828                    addInsertion( mutator, key, cf, "issueManagement.system", issueManagement.getSystem() );
829                    addInsertion( mutator, key, cf, "issueManagement.url", issueManagement.getUrl() );
830                }
831            }
832
833            {
834                Organization organization = versionMetadata.getOrganization();
835                if ( organization != null )
836                {
837                    addInsertion( mutator, key, cf, "organization.name", organization.getName() );
838                    addInsertion( mutator, key, cf, "organization.url", organization.getUrl() );
839                }
840            }
841
842            {
843                Scm scm = versionMetadata.getScm();
844                if ( scm != null )
845                {
846                    addInsertion( mutator, key, cf, "scm.url", scm.getUrl() );
847                    addInsertion( mutator, key, cf, "scm.connection", scm.getConnection() );
848                    addInsertion( mutator, key, cf, "scm.developerConnection", scm.getDeveloperConnection() );
849                }
850            }
851
852            recordMailingList( key, versionMetadata.getMailingLists() );
853
854            recordLicenses( key, versionMetadata.getLicenses() );
855
856            recordDependencies( key, versionMetadata.getDependencies(), repositoryId );
857
858            MutationResult mutationResult = mutator.execute();
859        }
860        else
861        {
862            ColumnFamilyUpdater<String, String> updater = projectVersionMetadataTemplate.createUpdater( key );
863            addUpdateStringValue( updater, PROJECT_ID.toString(), projectId );
864            addUpdateStringValue( updater, REPOSITORY_NAME.toString(), repositoryId );
865            addUpdateStringValue( updater, NAMESPACE_ID.toString(), namespaceId );
866            addUpdateStringValue( updater, PROJECT_VERSION.toString(), versionMetadata.getVersion() );
867            addUpdateStringValue( updater, DESCRIPTION.toString(), versionMetadata.getDescription() );
868
869            addUpdateStringValue( updater, NAME.toString(), versionMetadata.getName() );
870
871            updater.setString( "incomplete", Boolean.toString( versionMetadata.isIncomplete() ) );
872            addUpdateStringValue( updater, URL.toString(), versionMetadata.getUrl() );
873
874            {
875                CiManagement ci = versionMetadata.getCiManagement();
876                if ( ci != null )
877                {
878                    addUpdateStringValue( updater, "ciManagement.system", ci.getSystem() );
879                    addUpdateStringValue( updater, "ciManagement.url", ci.getUrl() );
880                }
881            }
882            {
883                IssueManagement issueManagement = versionMetadata.getIssueManagement();
884                if ( issueManagement != null )
885                {
886                    addUpdateStringValue( updater, "issueManagement.system", issueManagement.getSystem() );
887                    addUpdateStringValue( updater, "issueManagement.url", issueManagement.getUrl() );
888                }
889            }
890            {
891                Organization organization = versionMetadata.getOrganization();
892                if ( organization != null )
893                {
894                    addUpdateStringValue( updater, "organization.name", organization.getName() );
895                    addUpdateStringValue( updater, "organization.url", organization.getUrl() );
896                }
897            }
898            {
899                Scm scm = versionMetadata.getScm();
900                if ( scm != null )
901                {
902                    addUpdateStringValue( updater, "scm.url", scm.getUrl() );
903                    addUpdateStringValue( updater, "scm.connection", scm.getConnection() );
904                    addUpdateStringValue( updater, "scm.developerConnection", scm.getDeveloperConnection() );
905                }
906            }
907
908            // update is a delete record
909            removeMailingList( key );
910            recordMailingList( key, versionMetadata.getMailingLists() );
911
912            removeLicenses( key );
913            recordLicenses( key, versionMetadata.getLicenses() );
914
915            removeDependencies( key );
916            recordDependencies( key, versionMetadata.getDependencies(), repositoryId );
917
918            projectVersionMetadataTemplate.update( updater );
919
920        }
921
922        ArtifactMetadataModel artifactMetadataModel = new ArtifactMetadataModel();
923        artifactMetadataModel.setRepositoryId( repositoryId );
924        artifactMetadataModel.setNamespace( namespaceId );
925        artifactMetadataModel.setProject( projectId );
926        artifactMetadataModel.setProjectVersion( versionMetadata.getVersion() );
927        artifactMetadataModel.setVersion( versionMetadata.getVersion() );
928        updateFacets( versionMetadata, artifactMetadataModel );
929
930    }
931
932
933    @Override
934    public ProjectVersionMetadata getProjectVersion( RepositorySession session, final String repoId, final String namespace,
935                                                     final String projectId, final String projectVersion )
936        throws MetadataResolutionException
937    {
938
939        QueryResult<OrderedRows<String, String, String>> result = HFactory //
940            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
941            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
942            .setColumnNames( PROJECT_VERSION.toString() ) //
943            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
944            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
945            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
946            .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
947            .execute();
948
949        if ( result.get().getCount() < 1 )
950        {
951            return null;
952        }
953
954        String key = result.get().iterator().next().getKey();
955
956        ColumnFamilyResult<String, String> columnFamilyResult = this.projectVersionMetadataTemplate.queryColumns( key );
957
958        if ( !columnFamilyResult.hasResults() )
959        {
960            return null;
961        }
962
963        ProjectVersionMetadata projectVersionMetadata = new ProjectVersionMetadata();
964        projectVersionMetadata.setId( columnFamilyResult.getString( PROJECT_VERSION.toString() ) );
965        projectVersionMetadata.setDescription( columnFamilyResult.getString( DESCRIPTION.toString() ) );
966        projectVersionMetadata.setName( columnFamilyResult.getString( NAME.toString() ) );
967
968        projectVersionMetadata.setIncomplete( Boolean.parseBoolean( columnFamilyResult.getString( "incomplete" ) ) );
969
970        projectVersionMetadata.setUrl( columnFamilyResult.getString( URL.toString() ) );
971        {
972            String ciUrl = columnFamilyResult.getString( "ciManagement.url" );
973            String ciSystem = columnFamilyResult.getString( "ciManagement.system" );
974
975            if ( StringUtils.isNotEmpty( ciSystem ) || StringUtils.isNotEmpty( ciUrl ) )
976            {
977                projectVersionMetadata.setCiManagement( new CiManagement( ciSystem, ciUrl ) );
978            }
979        }
980        {
981            String issueUrl = columnFamilyResult.getString( "issueManagement.url" );
982            String issueSystem = columnFamilyResult.getString( "issueManagement.system" );
983            if ( StringUtils.isNotEmpty( issueSystem ) || StringUtils.isNotEmpty( issueUrl ) )
984            {
985                projectVersionMetadata.setIssueManagement( new IssueManagement( issueSystem, issueUrl ) );
986            }
987        }
988        {
989            String organizationUrl = columnFamilyResult.getString( "organization.url" );
990            String organizationName = columnFamilyResult.getString( "organization.name" );
991            if ( StringUtils.isNotEmpty( organizationUrl ) || StringUtils.isNotEmpty( organizationName ) )
992            {
993                projectVersionMetadata.setOrganization( new Organization( organizationName, organizationUrl ) );
994            }
995        }
996        {
997            String devConn = columnFamilyResult.getString( "scm.developerConnection" );
998            String conn = columnFamilyResult.getString( "scm.connection" );
999            String url = columnFamilyResult.getString( "scm.url" );
1000            if ( StringUtils.isNotEmpty( devConn ) || StringUtils.isNotEmpty( conn ) || StringUtils.isNotEmpty( url ) )
1001            {
1002                projectVersionMetadata.setScm( new Scm( conn, devConn, url ) );
1003            }
1004        }
1005        projectVersionMetadata.setMailingLists( getMailingLists( key ) );
1006        projectVersionMetadata.setLicenses( getLicenses( key ) );
1007        projectVersionMetadata.setDependencies( getDependencies( key ) );
1008        // facets
1009
1010        result = HFactory //
1011            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1012            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1013            .setColumnNames( FACET_ID.toString(), KEY.toString(), VALUE.toString(), NAME.toString() ) //
1014            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
1015            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
1016            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
1017            .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
1018            .execute();
1019
1020        Map<String, Map<String, String>> metadataFacetsPerFacetIds = new HashMap<>();
1021
1022        for ( Row<String, String, String> row : result.get() )
1023        {
1024            ColumnSlice<String, String> columnSlice = row.getColumnSlice();
1025            String facetId = getStringValue( columnSlice, FACET_ID.toString() );
1026            Map<String, String> metaValues = metadataFacetsPerFacetIds.get( facetId );
1027            if ( metaValues == null )
1028            {
1029                metaValues = new HashMap<>();
1030                metadataFacetsPerFacetIds.put( facetId, metaValues );
1031            }
1032            metaValues.put( getStringValue( columnSlice, KEY.toString() ), getStringValue( columnSlice, VALUE.toString() ) );
1033        }
1034
1035        if ( !metadataFacetsPerFacetIds.isEmpty() )
1036        {
1037            for ( Map.Entry<String, Map<String, String>> entry : metadataFacetsPerFacetIds.entrySet() )
1038            {
1039                MetadataFacetFactory metadataFacetFactory = getFacetFactory( entry.getKey() );
1040                if ( metadataFacetFactory != null )
1041                {
1042                    MetadataFacet metadataFacet = metadataFacetFactory.createMetadataFacet();
1043                    metadataFacet.fromProperties( entry.getValue() );
1044                    projectVersionMetadata.addFacet( metadataFacet );
1045                }
1046            }
1047        }
1048
1049        return projectVersionMetadata;
1050    }
1051
1052    protected void recordChecksums( String repositoryId, String artifactMetadataKey, Map<String, String> checksums)
1053    {
1054        if ( checksums == null || checksums.isEmpty() )
1055        {
1056            return;
1057        }
1058        Mutator<String> checksumMutator = this.checksumTemplate.createMutator();
1059        for ( Map.Entry<String, String> entry : checksums.entrySet())
1060        {
1061            // we don't care about the key as the real used one with the projectVersionMetadata
1062            String keyChecksums = UUID.randomUUID().toString();
1063            String cfChecksums = cassandraArchivaManager.getChecksumFamilyName();
1064
1065            addInsertion( checksumMutator, keyChecksums, cfChecksums, ARTIFACT_METADATA_MODEL_KEY,
1066                    artifactMetadataKey );
1067            addInsertion( checksumMutator, keyChecksums, cfChecksums, CHECKSUM_ALG.toString(), entry.getKey());
1068            addInsertion( checksumMutator, keyChecksums, cfChecksums, CHECKSUM_VALUE.toString(),
1069                    entry.getValue() );
1070            addInsertion(checksumMutator, keyChecksums, cfChecksums, REPOSITORY_NAME.toString(), repositoryId);
1071
1072        }
1073        checksumMutator.execute();
1074    }
1075
1076    protected void removeChecksums( String artifactMetadataKey )
1077    {
1078
1079        QueryResult<OrderedRows<String, String, String>> result =
1080                HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1081                        .setColumnFamily( cassandraArchivaManager.getChecksumFamilyName() ) //
1082                        .setColumnNames( CHECKSUM_ALG.toString() ) //
1083                        .setRowCount( Integer.MAX_VALUE ) //
1084                        .addEqualsExpression(ARTIFACT_METADATA_MODEL_KEY, artifactMetadataKey ) //
1085                        .execute();
1086
1087        if ( result.get().getCount() < 1 )
1088        {
1089            return;
1090        }
1091
1092        for ( Row<String, String, String> row : result.get() )
1093        {
1094            this.checksumTemplate.deleteRow( row.getKey() );
1095        }
1096
1097    }
1098
1099    protected Map<String, String> getChecksums( String artifactMetadataKey )
1100    {
1101        Map<String, String> checksums = new HashMap<>();
1102
1103        QueryResult<OrderedRows<String, String, String>> result =
1104                HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1105                        .setColumnFamily( cassandraArchivaManager.getChecksumFamilyName() ) //
1106                        .setColumnNames( ARTIFACT_METADATA_MODEL_KEY, REPOSITORY_NAME.toString(),
1107                                CHECKSUM_ALG.toString(), CHECKSUM_VALUE.toString() ) //
1108                        .setRowCount( Integer.MAX_VALUE ) //
1109                        .addEqualsExpression(ARTIFACT_METADATA_MODEL_KEY, artifactMetadataKey) //
1110                        .execute();
1111        for ( Row<String, String, String> row : result.get() )
1112        {
1113            ColumnFamilyResult<String, String> columnFamilyResult =
1114                    this.checksumTemplate.queryColumns( row.getKey() );
1115
1116            checksums.put(columnFamilyResult.getString(CHECKSUM_ALG.toString()),
1117                    columnFamilyResult.getString(CHECKSUM_VALUE.toString()));
1118        }
1119
1120        return checksums;
1121    }
1122
1123    protected void recordMailingList( String projectVersionMetadataKey, List<MailingList> mailingLists )
1124    {
1125        if ( mailingLists == null || mailingLists.isEmpty() )
1126        {
1127            return;
1128        }
1129        Mutator<String> mailingMutator = this.mailingListTemplate.createMutator();
1130        for ( MailingList mailingList : mailingLists )
1131        {
1132            // we don't care about the key as the real used one with the projectVersionMetadata
1133            String keyMailingList = UUID.randomUUID().toString();
1134            String cfMailingList = cassandraArchivaManager.getMailingListFamilyName();
1135
1136            addInsertion( mailingMutator, keyMailingList, cfMailingList, "projectVersionMetadataModel.key",
1137                          projectVersionMetadataKey );
1138            addInsertion( mailingMutator, keyMailingList, cfMailingList, NAME.toString(), mailingList.getName() );
1139            addInsertion( mailingMutator, keyMailingList, cfMailingList, "mainArchiveUrl",
1140                          mailingList.getMainArchiveUrl() );
1141            addInsertion( mailingMutator, keyMailingList, cfMailingList, "postAddress", mailingList.getPostAddress() );
1142            addInsertion( mailingMutator, keyMailingList, cfMailingList, "subscribeAddress",
1143                          mailingList.getSubscribeAddress() );
1144            addInsertion( mailingMutator, keyMailingList, cfMailingList, "unsubscribeAddress",
1145                          mailingList.getUnsubscribeAddress() );
1146            int idx = 0;
1147            for ( String otherArchive : mailingList.getOtherArchives() )
1148            {
1149                addInsertion( mailingMutator, keyMailingList, cfMailingList, "otherArchive." + idx, otherArchive );
1150                idx++;
1151            }
1152
1153        }
1154        mailingMutator.execute();
1155    }
1156
1157    protected void removeMailingList( String projectVersionMetadataKey )
1158    {
1159
1160        QueryResult<OrderedRows<String, String, String>> result =
1161            HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1162                .setColumnFamily( cassandraArchivaManager.getMailingListFamilyName() ) //
1163                .setColumnNames( NAME.toString() ) //
1164                .setRowCount( Integer.MAX_VALUE ) //
1165                .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1166                .execute();
1167
1168        if ( result.get().getCount() < 1 )
1169        {
1170            return;
1171        }
1172
1173        for ( Row<String, String, String> row : result.get() )
1174        {
1175            this.mailingListTemplate.deleteRow( row.getKey() );
1176        }
1177
1178    }
1179
1180    protected List<MailingList> getMailingLists( String projectVersionMetadataKey )
1181    {
1182        List<MailingList> mailingLists = new ArrayList<>();
1183
1184        QueryResult<OrderedRows<String, String, String>> result =
1185            HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1186                .setColumnFamily( cassandraArchivaManager.getMailingListFamilyName() ) //
1187                .setColumnNames( NAME.toString() ) //
1188                .setRowCount( Integer.MAX_VALUE ) //
1189                .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1190                .execute();
1191        for ( Row<String, String, String> row : result.get() )
1192        {
1193            ColumnFamilyResult<String, String> columnFamilyResult =
1194                this.mailingListTemplate.queryColumns( row.getKey() );
1195
1196            MailingList mailingList = new MailingList();
1197            mailingList.setName( columnFamilyResult.getString( NAME.toString() ) );
1198            mailingList.setMainArchiveUrl( columnFamilyResult.getString( "mainArchiveUrl" ) );
1199            mailingList.setPostAddress( columnFamilyResult.getString( "postAddress" ) );
1200            mailingList.setSubscribeAddress( columnFamilyResult.getString( "subscribeAddress" ) );
1201            mailingList.setUnsubscribeAddress( columnFamilyResult.getString( "unsubscribeAddress" ) );
1202
1203            List<String> otherArchives = new ArrayList<>();
1204
1205            for ( String columnName : columnFamilyResult.getColumnNames() )
1206            {
1207                if ( StringUtils.startsWith( columnName, "otherArchive." ) )
1208                {
1209                    otherArchives.add( columnFamilyResult.getString( columnName ) );
1210                }
1211            }
1212
1213            mailingList.setOtherArchives( otherArchives );
1214            mailingLists.add( mailingList );
1215        }
1216
1217        return mailingLists;
1218    }
1219
1220    protected void recordLicenses( String projectVersionMetadataKey, List<License> licenses )
1221    {
1222
1223        if ( licenses == null || licenses.isEmpty() )
1224        {
1225            return;
1226        }
1227        Mutator<String> licenseMutator = this.licenseTemplate.createMutator();
1228
1229        for ( License license : licenses )
1230        {
1231            // we don't care about the key as the real used one with the projectVersionMetadata
1232            String keyLicense = UUID.randomUUID().toString();
1233            String cfLicense = cassandraArchivaManager.getLicenseFamilyName();
1234
1235            addInsertion( licenseMutator, keyLicense, cfLicense, "projectVersionMetadataModel.key",
1236                          projectVersionMetadataKey );
1237
1238            addInsertion( licenseMutator, keyLicense, cfLicense, NAME.toString(), license.getName() );
1239
1240            addInsertion( licenseMutator, keyLicense, cfLicense, URL.toString(), license.getUrl() );
1241
1242        }
1243        licenseMutator.execute();
1244    }
1245
1246    protected void removeLicenses( String projectVersionMetadataKey )
1247    {
1248
1249        QueryResult<OrderedRows<String, String, String>> result =
1250            HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1251                .setColumnFamily( cassandraArchivaManager.getLicenseFamilyName() ) //
1252                .setColumnNames( NAME.toString() ) //
1253                .setRowCount( Integer.MAX_VALUE ) //
1254                .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1255                .execute();
1256        for ( Row<String, String, String> row : result.get() )
1257        {
1258            this.licenseTemplate.deleteRow( row.getKey() );
1259        }
1260    }
1261
1262    protected List<License> getLicenses( String projectVersionMetadataKey )
1263    {
1264        List<License> licenses = new ArrayList<>();
1265
1266        QueryResult<OrderedRows<String, String, String>> result =
1267            HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1268                .setColumnFamily( cassandraArchivaManager.getLicenseFamilyName() ) //
1269                .setColumnNames( "projectVersionMetadataModel.key" ) //
1270                .setRowCount( Integer.MAX_VALUE ) //
1271                .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1272                .execute();
1273
1274        for ( Row<String, String, String> row : result.get() )
1275        {
1276            ColumnFamilyResult<String, String> columnFamilyResult = this.licenseTemplate.queryColumns( row.getKey() );
1277
1278            licenses.add(
1279                new License( columnFamilyResult.getString( NAME.toString() ), columnFamilyResult.getString( URL.toString() ) ) );
1280        }
1281
1282        return licenses;
1283    }
1284
1285
1286    protected void recordDependencies( String projectVersionMetadataKey, List<Dependency> dependencies,
1287                                       String repositoryId )
1288    {
1289
1290        if ( dependencies == null || dependencies.isEmpty() )
1291        {
1292            return;
1293        }
1294        Mutator<String> dependencyMutator = this.dependencyTemplate.createMutator();
1295
1296        for ( Dependency dependency : dependencies )
1297        {
1298            // we don't care about the key as the real used one with the projectVersionMetadata
1299            String keyDependency = UUID.randomUUID().toString();
1300            String cfDependency = cassandraArchivaManager.getDependencyFamilyName();
1301
1302            addInsertion( dependencyMutator, keyDependency, cfDependency, "projectVersionMetadataModel.key",
1303                          projectVersionMetadataKey );
1304
1305            addInsertion( dependencyMutator, keyDependency, cfDependency, REPOSITORY_NAME.toString(), repositoryId );
1306
1307            addInsertion( dependencyMutator, keyDependency, cfDependency, "classifier", dependency.getClassifier() );
1308
1309            addInsertion( dependencyMutator, keyDependency, cfDependency, "optional",
1310                          Boolean.toString( dependency.isOptional() ) );
1311
1312            addInsertion( dependencyMutator, keyDependency, cfDependency, "scope", dependency.getScope() );
1313
1314            addInsertion( dependencyMutator, keyDependency, cfDependency, "systemPath", dependency.getSystemPath() );
1315
1316            addInsertion( dependencyMutator, keyDependency, cfDependency, "type", dependency.getType() );
1317
1318            addInsertion( dependencyMutator, keyDependency, cfDependency, ARTIFACT_ID.toString(), dependency.getArtifactId() );
1319
1320            addInsertion( dependencyMutator, keyDependency, cfDependency, GROUP_ID.toString(), dependency.getNamespace() );
1321
1322            addInsertion( dependencyMutator, keyDependency, cfDependency, VERSION.toString(), dependency.getVersion() );
1323
1324        }
1325        dependencyMutator.execute();
1326    }
1327
1328    protected void removeDependencies( String projectVersionMetadataKey )
1329    {
1330
1331        QueryResult<OrderedRows<String, String, String>> result =
1332            HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1333                .setColumnFamily( cassandraArchivaManager.getDependencyFamilyName() ) //
1334                .setColumnNames( GROUP_ID.toString() ) //
1335                .setRowCount( Integer.MAX_VALUE ) //
1336                .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1337                .execute();
1338        for ( Row<String, String, String> row : result.get() )
1339        {
1340            this.dependencyTemplate.deleteRow( row.getKey() );
1341        }
1342    }
1343
1344    protected List<Dependency> getDependencies( String projectVersionMetadataKey )
1345    {
1346        List<Dependency> dependencies = new ArrayList<>();
1347
1348        QueryResult<OrderedRows<String, String, String>> result =
1349            HFactory.createRangeSlicesQuery( cassandraArchivaManager.getKeyspace(), ss, ss, ss ) //
1350                .setColumnFamily( cassandraArchivaManager.getDependencyFamilyName() ) //
1351                .setColumnNames( "projectVersionMetadataModel.key" ) //
1352                .setRowCount( Integer.MAX_VALUE ) //
1353                .addEqualsExpression( "projectVersionMetadataModel.key", projectVersionMetadataKey ) //
1354                .execute();
1355
1356        for ( Row<String, String, String> row : result.get() )
1357        {
1358            ColumnFamilyResult<String, String> columnFamilyResult =
1359                this.dependencyTemplate.queryColumns( row.getKey() );
1360
1361            Dependency dependency = new Dependency();
1362            dependency.setClassifier( columnFamilyResult.getString( "classifier" ) );
1363
1364            dependency.setOptional( Boolean.parseBoolean( columnFamilyResult.getString( "optional" ) ) );
1365
1366            dependency.setScope( columnFamilyResult.getString( "scope" ) );
1367
1368            dependency.setSystemPath( columnFamilyResult.getString( "systemPath" ) );
1369
1370            dependency.setType( columnFamilyResult.getString( "type" ) );
1371
1372            dependency.setArtifactId( columnFamilyResult.getString( ARTIFACT_ID.toString() ) );
1373
1374            dependency.setNamespace( columnFamilyResult.getString( GROUP_ID.toString() ) );
1375
1376            dependency.setVersion( columnFamilyResult.getString( VERSION.toString() ) );
1377
1378            dependencies.add( dependency );
1379        }
1380
1381        return dependencies;
1382    }
1383
1384    private Map<String, String> mapChecksums(Map<ChecksumAlgorithm,String> checksums) {
1385        return checksums.entrySet().stream().collect(Collectors.toMap(
1386                e -> e.getKey().name(), e -> e.getValue()
1387        ));
1388    }
1389
1390    private Map<ChecksumAlgorithm, String> mapChecksumsReverse(Map<String,String> checksums) {
1391        return checksums.entrySet().stream().collect(Collectors.toMap(
1392                e -> ChecksumAlgorithm.valueOf(e.getKey()), e -> e.getValue()
1393        ));
1394    }
1395
1396    @Override
1397    public void updateArtifact( RepositorySession session, String repositoryId, String namespaceId, String projectId, String projectVersion,
1398                                ArtifactMetadata artifactMeta )
1399        throws MetadataRepositoryException
1400    {
1401
1402        Namespace namespace = getNamespace( repositoryId, namespaceId );
1403        if ( namespace == null )
1404        {
1405            namespace = updateOrAddNamespace( repositoryId, namespaceId );
1406        }
1407
1408        ProjectMetadata projectMetadata = new ProjectMetadata();
1409        projectMetadata.setId( projectId );
1410        projectMetadata.setNamespace( namespaceId );
1411        updateProject( session, repositoryId, projectMetadata );
1412
1413        String key = new ArtifactMetadataModel.KeyBuilder().withNamespace( namespace ).withProject( projectId ).withId(
1414            artifactMeta.getId() ).withProjectVersion( projectVersion ).build();
1415
1416        // exists?
1417
1418        boolean exists = this.artifactMetadataTemplate.isColumnsExist( key );
1419
1420        if ( exists )
1421        {
1422            // updater
1423            ColumnFamilyUpdater<String, String> updater = this.artifactMetadataTemplate.createUpdater( key );
1424            updater.setLong( FILE_LAST_MODIFIED.toString(), artifactMeta.getFileLastModified().toInstant().toEpochMilli());
1425            updater.setLong( WHEN_GATHERED.toString(), artifactMeta.getWhenGathered().toInstant().toEpochMilli() );
1426            updater.setLong( SIZE.toString(), artifactMeta.getSize() );
1427            addUpdateStringValue( updater, VERSION.toString(), artifactMeta.getVersion() );
1428            removeChecksums(key);
1429            recordChecksums(repositoryId, key, mapChecksums(artifactMeta.getChecksums()));
1430            this.artifactMetadataTemplate.update( updater );
1431        }
1432        else
1433        {
1434            String cf = this.cassandraArchivaManager.getArtifactMetadataFamilyName();
1435            // create
1436            this.artifactMetadataTemplate.createMutator() //
1437                .addInsertion( key, cf, column( ID.toString(), artifactMeta.getId() ) )//
1438                .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
1439                .addInsertion( key, cf, column( NAMESPACE_ID.toString(), namespaceId ) ) //
1440                .addInsertion( key, cf, column( PROJECT.toString(), artifactMeta.getProject() ) ) //
1441                .addInsertion( key, cf, column( PROJECT_VERSION.toString(), projectVersion ) ) //
1442                .addInsertion( key, cf, column( VERSION.toString(), artifactMeta.getVersion() ) ) //
1443                .addInsertion( key, cf, column( FILE_LAST_MODIFIED.toString(), artifactMeta.getFileLastModified().toInstant().toEpochMilli() ) ) //
1444                .addInsertion( key, cf, column( SIZE.toString(), artifactMeta.getSize() ) ) //
1445                .addInsertion( key, cf, column( WHEN_GATHERED.toString(), artifactMeta.getWhenGathered().toInstant().toEpochMilli() ) )//
1446                .execute();
1447            recordChecksums(repositoryId, key, mapChecksums(artifactMeta.getChecksums()));
1448        }
1449
1450        key = new ProjectVersionMetadataModel.KeyBuilder() //
1451            .withRepository( repositoryId ) //
1452            .withNamespace( namespace ) //
1453            .withProjectId( projectId ) //
1454            .withProjectVersion( projectVersion ) //
1455            .withId( artifactMeta.getId() ) //
1456            .build();
1457
1458        QueryResult<OrderedRows<String, String, String>> result = HFactory //
1459            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1460            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
1461            .setColumnNames( VERSION.toString() ) //
1462            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1463            .addEqualsExpression( NAMESPACE_ID.toString(), namespaceId ) //
1464            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
1465            .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
1466            .addEqualsExpression( VERSION.toString(), artifactMeta.getVersion() ) //
1467            .execute();
1468
1469        exists = result.get().getCount() > 0;
1470
1471        if ( !exists )
1472        {
1473            String cf = this.cassandraArchivaManager.getProjectVersionMetadataFamilyName();
1474
1475            projectVersionMetadataTemplate.createMutator() //
1476                .addInsertion( key, cf, column( NAMESPACE_ID.toString(), namespace.getName() ) ) //
1477                .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
1478                .addInsertion( key, cf, column( PROJECT_VERSION.toString(), projectVersion ) ) //
1479                .addInsertion( key, cf, column( PROJECT_ID.toString(), projectId ) ) //
1480                .addInsertion( key, cf, column( VERSION.toString(), artifactMeta.getVersion() ) ) //
1481                .execute();
1482
1483        }
1484
1485        ArtifactMetadataModel artifactMetadataModel = new ArtifactMetadataModel();
1486
1487        artifactMetadataModel.setRepositoryId( repositoryId );
1488        artifactMetadataModel.setNamespace( namespaceId );
1489        artifactMetadataModel.setProject( projectId );
1490        artifactMetadataModel.setProjectVersion( projectVersion );
1491        artifactMetadataModel.setVersion( artifactMeta.getVersion() );
1492        artifactMetadataModel.setFileLastModified( artifactMeta.getFileLastModified() == null
1493                                                       ? ZonedDateTime.now().toInstant().toEpochMilli()
1494                                                       : artifactMeta.getFileLastModified().toInstant().toEpochMilli() );
1495        artifactMetadataModel.setChecksums(mapChecksums(artifactMeta.getChecksums()));
1496
1497        // now facets
1498        updateFacets( artifactMeta, artifactMetadataModel );
1499
1500    }
1501
1502    @Override
1503    public List<String> getArtifactVersions( RepositorySession session, final String repoId, final String namespace, final String projectId,
1504                                             final String projectVersion )
1505        throws MetadataResolutionException
1506    {
1507
1508        QueryResult<OrderedRows<String, String, String>> result = HFactory //
1509            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1510            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
1511            .setColumnNames( VERSION.toString() ) //
1512            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
1513            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
1514            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
1515            .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
1516            .execute();
1517
1518        final Set<String> versions = new HashSet<>();
1519
1520        for ( Row<String, String, String> row : result.get() )
1521        {
1522            versions.add( getStringValue( row.getColumnSlice(), VERSION.toString() ) );
1523        }
1524
1525        return new ArrayList<>( versions );
1526
1527    }
1528
1529    /*
1530     * iterate over available facets to remove/add from the artifactMetadata
1531     *
1532     * @param facetedMetadata
1533     * @param artifactMetadataModel only use for the key
1534     */
1535    private void updateFacets( final FacetedMetadata facetedMetadata,
1536                               final ArtifactMetadataModel artifactMetadataModel )
1537    {
1538
1539        String cf = cassandraArchivaManager.getMetadataFacetFamilyName();
1540
1541        for ( final String facetId : getSupportedFacets() )
1542        {
1543            MetadataFacet metadataFacet = facetedMetadata.getFacet( facetId );
1544            if ( metadataFacet == null )
1545            {
1546                continue;
1547            }
1548            // clean first
1549
1550            QueryResult<OrderedRows<String, String, String>> result =
1551                HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1552                    .setColumnFamily( cf ) //
1553                    .setColumnNames( REPOSITORY_NAME.toString() ) //
1554                    .addEqualsExpression( REPOSITORY_NAME.toString(), artifactMetadataModel.getRepositoryId() ) //
1555                    .addEqualsExpression( NAMESPACE_ID.toString(), artifactMetadataModel.getNamespace() ) //
1556                    .addEqualsExpression( PROJECT_ID.toString(), artifactMetadataModel.getProject() ) //
1557                    .addEqualsExpression( PROJECT_VERSION.toString(), artifactMetadataModel.getProjectVersion() ) //
1558                    .addEqualsExpression( FACET_ID.toString(), facetId ) //
1559                    .execute();
1560
1561            for ( Row<String, String, String> row : result.get().getList() )
1562            {
1563                this.metadataFacetTemplate.deleteRow( row.getKey() );
1564            }
1565
1566            Map<String, String> properties = metadataFacet.toProperties();
1567
1568            for ( Map.Entry<String, String> entry : properties.entrySet() )
1569            {
1570                String key = new MetadataFacetModel.KeyBuilder().withKey( entry.getKey() ).withArtifactMetadataModel(
1571                    artifactMetadataModel ).withFacetId( facetId ).withName( metadataFacet.getName() ).build();
1572                Mutator<String> mutator = metadataFacetTemplate.createMutator() //
1573                    .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), artifactMetadataModel.getRepositoryId() ) ) //
1574                    .addInsertion( key, cf, column( NAMESPACE_ID.toString(), artifactMetadataModel.getNamespace() ) ) //
1575                    .addInsertion( key, cf, column( PROJECT_ID.toString(), artifactMetadataModel.getProject() ) ) //
1576                    .addInsertion( key, cf, column( PROJECT_VERSION.toString(), artifactMetadataModel.getProjectVersion() ) ) //
1577                    .addInsertion( key, cf, column( FACET_ID.toString(), facetId ) ) //
1578                    .addInsertion( key, cf, column( KEY.toString(), entry.getKey() ) ) //
1579                    .addInsertion( key, cf, column( VALUE.toString(), entry.getValue() ) );
1580
1581                if ( metadataFacet.getName() != null )
1582                {
1583                    mutator.addInsertion( key, cf, column( NAME.toString(), metadataFacet.getName() ) );
1584                }
1585
1586                mutator.execute();
1587            }
1588        }
1589    }
1590
1591
1592    @Override
1593    public List<String> getMetadataFacets( RepositorySession session, final String repositoryId, final String facetId )
1594        throws MetadataRepositoryException
1595    {
1596
1597        QueryResult<OrderedRows<String, String, String>> result = HFactory //
1598            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1599            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1600            .setColumnNames( NAME.toString() ) //
1601            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1602            .addEqualsExpression( FACET_ID.toString(), facetId ) //
1603            .execute();
1604
1605        final List<String> facets = new ArrayList<>();
1606
1607        for ( Row<String, String, String> row : result.get() )
1608        {
1609            facets.add( getStringValue( row.getColumnSlice(), NAME.toString() ) );
1610        }
1611        return facets;
1612    }
1613
1614    private <T> Spliterator<T> createResultSpliterator( QueryResult<OrderedRows<String, String, String>> result, BiFunction<Row<String, String, String>, T, T> converter) throws MetadataRepositoryException
1615    {
1616        final int size = result.get().getCount();
1617        final Iterator<Row<String, String, String>> it = result.get( ).iterator( );
1618
1619        return new Spliterator<T>( )
1620        {
1621            private T lastItem = null;
1622
1623            @Override
1624            public boolean tryAdvance( Consumer<? super T> action )
1625            {
1626                if (size>=1)
1627                {
1628                    if(it.hasNext())
1629                    {
1630                        while ( it.hasNext( ) )
1631                        {
1632                            Row<String, String, String> row = it.next( );
1633                            T item = converter.apply( row, lastItem );
1634                            if ( item != null && lastItem !=null && item != lastItem )
1635                            {
1636                                action.accept( lastItem );
1637                                lastItem = item;
1638                                return true;
1639                            }
1640                            lastItem = item;
1641                        }
1642                        action.accept( lastItem );
1643                        return true;
1644                    } else {
1645                        return false;
1646                    }
1647                }
1648                return false;
1649            }
1650
1651            @Override
1652            public Spliterator<T> trySplit( )
1653            {
1654                return null;
1655            }
1656
1657            @Override
1658            public long estimateSize( )
1659            {
1660                return size;
1661            }
1662
1663            @Override
1664            public int characteristics( )
1665            {
1666                return ORDERED+NONNULL+SIZED;
1667            }
1668        };
1669    }
1670
1671
1672    /**
1673     * Implementation is not very performant, because sorting is part of the stream. I do not know how to specify the sort
1674     * in the query.
1675     * 
1676     * @param <T>
1677     * @param session
1678     * @param repositoryId
1679     * @param facetClazz
1680     * @param queryParameter
1681     * @return
1682     * @throws MetadataRepositoryException
1683     */
1684    @Override
1685    public <T extends MetadataFacet> Stream<T> getMetadataFacetStream(RepositorySession session, String repositoryId, Class<T> facetClazz, QueryParameter queryParameter) throws MetadataRepositoryException
1686    {
1687        final MetadataFacetFactory<T> metadataFacetFactory = getFacetFactory( facetClazz );
1688        final String facetId = metadataFacetFactory.getFacetId( );
1689
1690        QueryResult<OrderedRows<String, String, String>> result = HFactory //
1691            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1692            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName( ) ) //
1693            .setColumnNames( NAME.toString( ), KEY.toString( ), VALUE.toString( ) ) //
1694            .addEqualsExpression( REPOSITORY_NAME.toString( ), repositoryId ) //
1695            .addEqualsExpression( FACET_ID.toString( ), facetId ) //
1696            .setRange( null, null, false, Integer.MAX_VALUE )
1697            .setRowCount( Integer.MAX_VALUE )
1698            .execute( );
1699
1700
1701
1702        return StreamSupport.stream( createResultSpliterator( result, ( Row<String, String, String> row, T lastItem)-> {
1703            ColumnSlice<String, String> columnSlice = row.getColumnSlice();
1704            String name = getStringValue( columnSlice, NAME.toString( ) );
1705            T updateItem;
1706            if (lastItem!=null && lastItem.getName().equals(name))
1707            {
1708                updateItem = lastItem;
1709            } else
1710            {
1711                updateItem = metadataFacetFactory.createMetadataFacet( repositoryId, name );
1712            }
1713            String key = getStringValue( columnSlice, KEY.toString() );
1714            if (StringUtils.isNotEmpty( key ))
1715            {
1716                Map<String, String> map = new HashMap<>( );
1717                map.put( key , getStringValue( columnSlice, VALUE.toString( ) ) );
1718                updateItem.fromProperties( map );
1719            }
1720            return updateItem;
1721
1722        }), false ).sorted( (f1, f2) -> f1.getName()!=null ? f1.getName().compareTo( f2.getName() ) : 1 ).skip( queryParameter.getOffset()).limit( queryParameter.getLimit());
1723    }
1724
1725    @Override
1726    public boolean hasMetadataFacet( RepositorySession session, String repositoryId, String facetId )
1727        throws MetadataRepositoryException
1728    {
1729        return !getMetadataFacets( session, repositoryId, facetId ).isEmpty();
1730    }
1731
1732    @Override
1733    public <T extends MetadataFacet> T getMetadataFacet( RepositorySession session, final String repositoryId, final Class<T> facetClazz, final String name )
1734        throws MetadataRepositoryException
1735    {
1736        final MetadataFacetFactory<T> metadataFacetFactory = getFacetFactory( facetClazz );
1737        if (metadataFacetFactory==null) {
1738            return null;
1739        }
1740        final String facetId = metadataFacetFactory.getFacetId( );
1741        if ( metadataFacetFactory == null )
1742        {
1743            return null;
1744        }
1745
1746        QueryResult<OrderedRows<String, String, String>> result = HFactory //
1747            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1748            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1749            .setColumnNames( KEY.toString(), VALUE.toString() ) //
1750            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1751            .addEqualsExpression( FACET_ID.toString(), facetId ) //
1752            .addEqualsExpression( NAME.toString(), name ) //
1753            .execute();
1754
1755        T metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
1756        int size = result.get().getCount();
1757        if ( size < 1 )
1758        {
1759            return null;
1760        }
1761        Map<String, String> map = new HashMap<>( size );
1762        for ( Row<String, String, String> row : result.get() )
1763        {
1764            ColumnSlice<String, String> columnSlice = row.getColumnSlice();
1765            map.put( getStringValue( columnSlice, KEY.toString() ), getStringValue( columnSlice, VALUE.toString() ) );
1766        }
1767        metadataFacet.fromProperties( map );
1768        return metadataFacet;
1769    }
1770
1771    @Override
1772    public MetadataFacet getMetadataFacet( RepositorySession session, String repositoryId, String facetId, String name ) throws MetadataRepositoryException
1773    {
1774        return getMetadataFacet( session, repositoryId, getFactoryClassForId( facetId ), name );
1775    }
1776
1777    @Override
1778    public void addMetadataFacet( RepositorySession session, String repositoryId, MetadataFacet metadataFacet )
1779        throws MetadataRepositoryException
1780    {
1781
1782        if ( metadataFacet == null )
1783        {
1784            return;
1785        }
1786
1787        if ( metadataFacet.toProperties().isEmpty() )
1788        {
1789            String key = new MetadataFacetModel.KeyBuilder().withRepositoryId( repositoryId ).withFacetId(
1790                metadataFacet.getFacetId() ).withName( metadataFacet.getName() ).build();
1791
1792            boolean exists = this.metadataFacetTemplate.isColumnsExist( key );
1793
1794            if ( exists )
1795            {
1796                ColumnFamilyUpdater<String, String> updater = this.metadataFacetTemplate.createUpdater( key );
1797                addUpdateStringValue( updater, FACET_ID.toString(), metadataFacet.getFacetId() );
1798                addUpdateStringValue( updater, NAME.toString(), metadataFacet.getName() );
1799                this.metadataFacetTemplate.update( updater );
1800            }
1801            else
1802            {
1803                String cf = this.cassandraArchivaManager.getMetadataFacetFamilyName();
1804                this.metadataFacetTemplate.createMutator() //
1805                    .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
1806                    .addInsertion( key, cf, column( FACET_ID.toString(), metadataFacet.getFacetId() ) ) //
1807                    .addInsertion( key, cf, column( NAME.toString(), metadataFacet.getName() ) ) //
1808                    .execute();
1809            }
1810
1811        }
1812        else
1813        {
1814            for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
1815            {
1816                String key = new MetadataFacetModel.KeyBuilder().withRepositoryId( repositoryId ).withFacetId(
1817                    metadataFacet.getFacetId() ).withName( metadataFacet.getName() ).withKey( entry.getKey() ).build();
1818
1819                boolean exists = this.metadataFacetTemplate.isColumnsExist( key );
1820                if ( !exists )
1821                {
1822                    String cf = this.cassandraArchivaManager.getMetadataFacetFamilyName();
1823                    this.metadataFacetTemplate.createMutator() //
1824                        .addInsertion( key, cf, column( REPOSITORY_NAME.toString(), repositoryId ) ) //
1825                        .addInsertion( key, cf, column( FACET_ID.toString(), metadataFacet.getFacetId() ) ) //
1826                        .addInsertion( key, cf, column( NAME.toString(), metadataFacet.getName() ) ) //
1827                        .addInsertion( key, cf, column( KEY.toString(), entry.getKey() ) ) //
1828                        .addInsertion( key, cf, column( VALUE.toString(), entry.getValue() ) ) //
1829                        .execute();
1830                }
1831                else
1832                {
1833                    ColumnFamilyUpdater<String, String> updater = this.metadataFacetTemplate.createUpdater( key );
1834                    addUpdateStringValue( updater, VALUE.toString(), entry.getValue() );
1835                    this.metadataFacetTemplate.update( updater );
1836                }
1837            }
1838        }
1839    }
1840
1841    @Override
1842    public void removeMetadataFacets( RepositorySession session, final String repositoryId, final String facetId )
1843        throws MetadataRepositoryException
1844    {
1845
1846        QueryResult<OrderedRows<String, String, String>> result = HFactory //
1847            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1848            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1849            .setColumnNames( KEY.toString(), VALUE.toString() ) //
1850            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1851            .addEqualsExpression( FACET_ID.toString(), facetId ) //
1852            .execute();
1853
1854        for ( Row<String, String, String> row : result.get() )
1855        {
1856            this.metadataFacetTemplate.deleteRow( row.getKey() );
1857        }
1858
1859    }
1860
1861    @Override
1862    public void removeMetadataFacet( RepositorySession session, final String repositoryId, final String facetId, final String name )
1863        throws MetadataRepositoryException
1864    {
1865
1866        QueryResult<OrderedRows<String, String, String>> result = HFactory //
1867            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1868            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
1869            .setColumnNames( KEY.toString(), VALUE.toString() ) //
1870            .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
1871            .addEqualsExpression( FACET_ID.toString(), facetId ) //
1872            .addEqualsExpression( NAME.toString(), name ) //
1873            .execute();
1874
1875        for ( Row<String, String, String> row : result.get() )
1876        {
1877            this.metadataFacetTemplate.deleteRow( row.getKey() );
1878        }
1879    }
1880
1881    @Override
1882    public List<ArtifactMetadata> getArtifactsByDateRange( RepositorySession session, final String repositoryId, final ZonedDateTime startTime,
1883                                                           final ZonedDateTime endTime, QueryParameter queryParameter )
1884        throws MetadataRepositoryException
1885    {
1886
1887        LongSerializer ls = LongSerializer.get();
1888        RangeSlicesQuery<String, String, Long> query = HFactory //
1889            .createRangeSlicesQuery( keyspace, ss, ss, ls ) //
1890            .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
1891            .setColumnNames( ArtifactMetadataModel.COLUMNS ); //
1892
1893
1894        if ( startTime != null )
1895        {
1896            query = query.addGteExpression( WHEN_GATHERED.toString(), startTime.toInstant().toEpochMilli() );
1897        }
1898        if ( endTime != null )
1899        {
1900            query = query.addLteExpression( WHEN_GATHERED.toString(), endTime.toInstant().toEpochMilli() );
1901        }
1902        QueryResult<OrderedRows<String, String, Long>> result = query.execute();
1903
1904        List<ArtifactMetadata> artifactMetadatas = new ArrayList<>( result.get().getCount() );
1905        Iterator<Row<String, String, Long>> keyIter = result.get().iterator();
1906        if (keyIter.hasNext()) {
1907            String key = keyIter.next().getKey();
1908            for (Row<String, String, Long> row : result.get()) {
1909                ColumnSlice<String, Long> columnSlice = row.getColumnSlice();
1910                String repositoryName = getAsStringValue(columnSlice, REPOSITORY_NAME.toString());
1911                if (StringUtils.equals(repositoryName, repositoryId)) {
1912
1913                    artifactMetadatas.add(mapArtifactMetadataLongColumnSlice(key, columnSlice));
1914                }
1915            }
1916        }
1917
1918        return artifactMetadatas;
1919    }
1920
1921    /**
1922     * For documentation see {@link MetadataRepository#getArtifactByDateRangeStream(RepositorySession, String, ZonedDateTime, ZonedDateTime, QueryParameter)}
1923     *
1924     * This implementation orders the stream. It does not order the query in the backend.
1925     *
1926     * @param session The repository session
1927     * @param repositoryId The repository id
1928     * @param startTime The start time, can be <code>null</code>
1929     * @param endTime The end time, can be <code>null</code>
1930     * @param queryParameter Additional parameters for the query that affect ordering and number of returned results.
1931     * @return
1932     * @throws MetadataRepositoryException
1933     * @see MetadataRepository#getArtifactByDateRangeStream
1934     */
1935    @Override
1936    public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException
1937    {
1938        Comparator<ArtifactMetadata> comp = getArtifactMetadataComparator(queryParameter, "whenGathered");
1939        return getArtifactsByDateRange(session, repositoryId, startTime, endTime, queryParameter).stream().sorted(comp).skip(queryParameter.getOffset()).limit(queryParameter.getLimit());
1940    }
1941
1942
1943    protected ArtifactMetadata mapArtifactMetadataLongColumnSlice( String key, ColumnSlice<String, Long> columnSlice )
1944    {
1945        ArtifactMetadata artifactMetadata = new ArtifactMetadata();
1946        artifactMetadata.setNamespace( getAsStringValue( columnSlice, NAMESPACE_ID.toString() ) );
1947        artifactMetadata.setSize( getLongValue( columnSlice, SIZE.toString() ) );
1948        artifactMetadata.setId( getAsStringValue( columnSlice, ID.toString() ) );
1949        artifactMetadata.setFileLastModified( getLongValue( columnSlice, FILE_LAST_MODIFIED.toString() ) );
1950        artifactMetadata.setMd5( getAsStringValue( columnSlice, MD5.toString() ) );
1951        artifactMetadata.setProject( getAsStringValue( columnSlice, PROJECT.toString() ) );
1952        artifactMetadata.setProjectVersion( getAsStringValue( columnSlice, PROJECT_VERSION.toString() ) );
1953        artifactMetadata.setRepositoryId( getAsStringValue( columnSlice, REPOSITORY_NAME.toString() ) );
1954        artifactMetadata.setSha1( getAsStringValue( columnSlice, SHA1.toString() ) );
1955        artifactMetadata.setVersion( getAsStringValue( columnSlice, VERSION.toString() ) );
1956        Long whenGathered = getLongValue( columnSlice, WHEN_GATHERED.toString() );
1957        if ( whenGathered != null )
1958        {
1959            artifactMetadata.setWhenGathered(ZonedDateTime.ofInstant(Instant.ofEpochMilli(whenGathered), STORAGE_TZ));
1960        }
1961        artifactMetadata.setChecksums(mapChecksumsReverse(getChecksums(key)));
1962        return artifactMetadata;
1963    }
1964
1965    protected ArtifactMetadata mapArtifactMetadataStringColumnSlice( String key, ColumnSlice<String, String> columnSlice )
1966    {
1967        ArtifactMetadata artifactMetadata = new ArtifactMetadata();
1968        artifactMetadata.setNamespace( getStringValue( columnSlice, NAMESPACE_ID.toString() ) );
1969        artifactMetadata.setSize( getAsLongValue( columnSlice, SIZE.toString() ) );
1970        artifactMetadata.setId( getStringValue( columnSlice, ID.toString() ) );
1971        artifactMetadata.setFileLastModified( getAsLongValue( columnSlice, FILE_LAST_MODIFIED.toString() ) );
1972        artifactMetadata.setMd5( getStringValue( columnSlice, MD5.toString() ) );
1973        artifactMetadata.setProject( getStringValue( columnSlice, PROJECT.toString() ) );
1974        artifactMetadata.setProjectVersion( getStringValue( columnSlice, PROJECT_VERSION.toString() ) );
1975        artifactMetadata.setRepositoryId( getStringValue( columnSlice, REPOSITORY_NAME.toString() ) );
1976        artifactMetadata.setSha1( getStringValue( columnSlice, SHA1.toString() ) );
1977        artifactMetadata.setVersion( getStringValue( columnSlice, VERSION.toString() ) );
1978        Long whenGathered = getAsLongValue( columnSlice, WHEN_GATHERED.toString() );
1979        if ( whenGathered != null )
1980        {
1981            artifactMetadata.setWhenGathered(ZonedDateTime.ofInstant(Instant.ofEpochMilli(whenGathered), STORAGE_TZ));
1982        }
1983        artifactMetadata.setChecksums(mapChecksumsReverse(getChecksums(key)));
1984        return artifactMetadata;
1985    }
1986
1987    @Override
1988    public List<ArtifactMetadata> getArtifactsByChecksum(RepositorySession session, final String repositoryId, final String checksum )
1989        throws MetadataRepositoryException
1990    {
1991
1992        // cql cannot run or in queries so running twice the query
1993        Map<String, ArtifactMetadata> artifactMetadataMap = new HashMap<>();
1994
1995        RangeSlicesQuery<String, String, String> query = HFactory //
1996            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
1997            .setColumnFamily( cassandraArchivaManager.getChecksumFamilyName()) //
1998            .setColumnNames(ARTIFACT_METADATA_MODEL_KEY); //
1999
2000        query = query.addEqualsExpression( CHECKSUM_VALUE.toString(), checksum )
2001                .addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId );
2002
2003        QueryResult<OrderedRows<String, String, String>> result = query.execute();
2004
2005        List<String> artifactKeys = new ArrayList<>();
2006        for ( Row<String, String, String> row : result.get() )
2007        {
2008            ColumnSlice<String, String> columnSlice = row.getColumnSlice();
2009
2010            artifactKeys.add(columnSlice.getColumnByName(ARTIFACT_METADATA_MODEL_KEY).getValue());
2011
2012        }
2013
2014        for (String key : artifactKeys) {
2015            query = HFactory //
2016                    .createRangeSlicesQuery(keyspace, ss, ss, ss) //
2017                    .setColumnFamily(cassandraArchivaManager.getArtifactMetadataFamilyName()) //
2018                    .setColumnNames(NAMESPACE_ID.toString(), SIZE.toString(), ID.toString(), FILE_LAST_MODIFIED.toString(), MD5.toString(), PROJECT.toString(), PROJECT_VERSION.toString(),
2019                            REPOSITORY_NAME.toString(), VERSION.toString(), WHEN_GATHERED.toString(), SHA1.toString())
2020                    .setKeys(key, key);
2021            result = query.execute();
2022
2023            for (Row<String, String, String> row : result.get()) {
2024                ColumnSlice<String, String> columnSlice = row.getColumnSlice();
2025
2026                artifactMetadataMap.put(row.getKey(), mapArtifactMetadataStringColumnSlice(key, columnSlice));
2027            }
2028        }
2029
2030        return new ArrayList(artifactMetadataMap.values());
2031    }
2032
2033    /**
2034     * Project version and artifact level metadata are stored in the same place, no distinctions in Cassandra
2035     * implementation, just calls {@link MetadataRepository#getArtifactsByAttribute(RepositorySession, String, String, String)}
2036     */
2037    @Override
2038    public List<ArtifactMetadata> getArtifactsByProjectVersionFacet( RepositorySession session, String key, String value, String repositoryId )
2039        throws MetadataRepositoryException
2040    {
2041        return this.getArtifactsByAttribute( session, key, value, repositoryId );
2042    }
2043
2044    @Override
2045    public List<ArtifactMetadata> getArtifactsByAttribute( RepositorySession session, String key, String value, String repositoryId )
2046        throws MetadataRepositoryException
2047    {
2048        RangeSlicesQuery<String, String, String> query =
2049            HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2050            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
2051            .setColumnNames( MetadataFacetModel.COLUMNS ) //
2052            .addEqualsExpression( VALUE.toString(), value );
2053
2054        if ( key != null )
2055        {
2056            query.addEqualsExpression( KEY.toString(), key ); //
2057        }
2058        if ( repositoryId != null )
2059        {
2060            query.addEqualsExpression( "repositoryName", repositoryId );
2061        }
2062
2063        QueryResult<OrderedRows<String, String, String>> metadataFacetResult = query.execute();
2064        if ( metadataFacetResult.get() == null || metadataFacetResult.get().getCount() < 1 )
2065        {
2066            return Collections.emptyList();
2067        }
2068
2069        List<ArtifactMetadata> artifactMetadatas = new LinkedList<ArtifactMetadata>();
2070
2071        // TODO doing multiple queries, there should be a way to get all the artifactMetadatas for any number of
2072        // projects
2073        for ( Row<String, String, String> row : metadataFacetResult.get() )
2074        {
2075            QueryResult<OrderedRows<String, String, String>> artifactMetadataResult =
2076                HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2077                .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2078                .setColumnNames( ArtifactMetadataModel.COLUMNS ) //
2079                .setRowCount( Integer.MAX_VALUE ) //
2080                .addEqualsExpression( REPOSITORY_NAME.toString(),
2081                                      getStringValue( row.getColumnSlice(), REPOSITORY_NAME ) ) //
2082                .addEqualsExpression( NAMESPACE_ID.toString(), getStringValue( row.getColumnSlice(), NAMESPACE_ID ) ) //
2083                .addEqualsExpression( PROJECT.toString(), getStringValue( row.getColumnSlice(), PROJECT_ID ) ) //
2084                .addEqualsExpression( PROJECT_VERSION.toString(),
2085                                      getStringValue( row.getColumnSlice(), PROJECT_VERSION ) ) //
2086                .execute();
2087
2088            if ( artifactMetadataResult.get() == null || artifactMetadataResult.get().getCount() < 1 )
2089            {
2090                return Collections.emptyList();
2091            }
2092
2093            for ( Row<String, String, String> artifactMetadataRow : artifactMetadataResult.get() )
2094            {
2095                String artifactKey = artifactMetadataRow.getKey();
2096                artifactMetadatas.add( mapArtifactMetadataStringColumnSlice( artifactKey, artifactMetadataRow.getColumnSlice() ) );
2097            }
2098        }
2099
2100        return mapArtifactFacetToArtifact( metadataFacetResult, artifactMetadatas );
2101    }
2102
2103    @Override
2104    public List<ArtifactMetadata> getArtifactsByProjectVersionAttribute( RepositorySession session, String key, String value, String repositoryId )
2105        throws MetadataRepositoryException
2106    {
2107        QueryResult<OrderedRows<String, String, String>> result =
2108            HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2109            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
2110            .setColumnNames( PROJECT_ID.toString(), REPOSITORY_NAME.toString(), NAMESPACE_ID.toString(),
2111                             PROJECT_VERSION.toString() ) //
2112            .addEqualsExpression( key, value ) //
2113            .execute();
2114
2115        int count = result.get().getCount();
2116
2117        if ( count < 1 )
2118        {
2119            return Collections.emptyList();
2120        }
2121
2122        List<ArtifactMetadata> artifacts = new LinkedList<ArtifactMetadata>();
2123
2124        for ( Row<String, String, String> row : result.get() )
2125        {
2126            // TODO doing multiple queries, there should be a way to get all the artifactMetadatas for any number of
2127            // projects
2128            try
2129            {
2130                artifacts.addAll( getArtifacts( session,
2131                    getStringValue( row.getColumnSlice(), REPOSITORY_NAME ),
2132                    getStringValue( row.getColumnSlice(), NAMESPACE_ID ),
2133                    getStringValue( row.getColumnSlice(), PROJECT_ID ), getStringValue( row.getColumnSlice(), PROJECT_VERSION ) ) );
2134            }
2135            catch ( MetadataResolutionException e )
2136            {
2137                // never raised
2138                throw new IllegalStateException( e );
2139            }
2140        }
2141        return artifacts;
2142    }
2143
2144    @Override
2145    public void removeArtifact( RepositorySession session, final String repositoryId, final String namespace, final String project,
2146                                final String version, final String id )
2147        throws MetadataRepositoryException
2148    {
2149        logger.debug( "removeTimestampedArtifact repositoryId: '{}', namespace: '{}', project: '{}', version: '{}', id: '{}'",
2150                      repositoryId, namespace, project, version, id );
2151        String key =
2152            new ArtifactMetadataModel.KeyBuilder().withRepositoryId( repositoryId ).withNamespace( namespace ).withId(
2153                id ).withProjectVersion( version ).withProject( project ).build();
2154
2155        this.artifactMetadataTemplate.deleteRow( key );
2156
2157        key = new ProjectVersionMetadataModel.KeyBuilder() //
2158            .withRepository( repositoryId ) //
2159            .withNamespace( namespace ) //
2160            .withProjectId( project ) //
2161            .withProjectVersion( version ) //
2162            .withId( id ) //
2163            .build();
2164
2165        this.projectVersionMetadataTemplate.deleteRow( key );
2166    }
2167
2168    @Override
2169    public void removeTimestampedArtifact( RepositorySession session, ArtifactMetadata artifactMetadata, String baseVersion )
2170        throws MetadataRepositoryException
2171    {
2172        logger.debug( "removeTimestampedArtifact repositoryId: '{}', namespace: '{}', project: '{}', version: '{}', id: '{}'",
2173                      artifactMetadata.getRepositoryId(), artifactMetadata.getNamespace(),
2174                      artifactMetadata.getProject(), baseVersion, artifactMetadata.getId() );
2175        String key =
2176            new ArtifactMetadataModel.KeyBuilder().withRepositoryId( artifactMetadata.getRepositoryId() ).withNamespace(
2177                artifactMetadata.getNamespace() ).withId( artifactMetadata.getId() ).withProjectVersion(
2178                baseVersion ).withProject( artifactMetadata.getProject() ).build();
2179
2180        this.artifactMetadataTemplate.deleteRow( key );
2181
2182    }
2183
2184    @Override
2185    public void removeFacetFromArtifact( RepositorySession session, final String repositoryId, final String namespace, final String project,
2186                                         final String version, final MetadataFacet metadataFacet )
2187        throws MetadataRepositoryException
2188    {
2189
2190        RangeSlicesQuery<String, String, String> query = HFactory //
2191            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2192            .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2193            .setColumnNames( NAMESPACE_ID.toString() ); //
2194
2195        query = query.addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId ) //
2196            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2197            .addEqualsExpression( PROJECT.toString(), project ) //
2198            .addEqualsExpression( VERSION.toString(), version );
2199
2200        QueryResult<OrderedRows<String, String, String>> result = query.execute();
2201
2202        for ( Row<String, String, String> row : result.get() )
2203        {
2204            this.artifactMetadataTemplate.deleteRow( row.getKey() );
2205        }
2206    }
2207
2208
2209    @Override
2210    public List<ArtifactMetadata> getArtifacts( RepositorySession session, final String repositoryId )
2211        throws MetadataRepositoryException
2212    {
2213
2214        RangeSlicesQuery<String, String, String> query = HFactory //
2215            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2216            .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2217            .setColumnNames( ArtifactMetadataModel.COLUMNS ); //
2218
2219        query = query.addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId );
2220
2221        QueryResult<OrderedRows<String, String, String>> result = query.execute();
2222
2223
2224
2225        List<ArtifactMetadata> artifactMetadatas = new ArrayList<>( result.get().getCount() );
2226
2227        for ( Row<String, String, String> row : result.get() )
2228        {
2229            String key = row.getKey();
2230            ColumnSlice<String, String> columnSlice = row.getColumnSlice();
2231            artifactMetadatas.add( mapArtifactMetadataStringColumnSlice( key, columnSlice ) );
2232
2233        }
2234
2235        return artifactMetadatas;
2236    }
2237
2238
2239    @Override
2240    public List<ProjectVersionReference> getProjectReferences( RepositorySession session, String repoId, String namespace, String projectId,
2241                                                               String projectVersion )
2242        throws MetadataResolutionException
2243    {
2244        QueryResult<OrderedRows<String, String, String>> result = HFactory //
2245            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2246            .setColumnFamily( cassandraArchivaManager.getDependencyFamilyName() ) //
2247            .setColumnNames( "projectVersionMetadataModel.key" ) //
2248            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2249            .addEqualsExpression( GROUP_ID.toString(), namespace ) //
2250            .addEqualsExpression( ARTIFACT_ID.toString(), projectId ) //
2251            .addEqualsExpression( VERSION.toString(), projectVersion ) //
2252            .execute();
2253
2254        List<String> dependenciesIds = new ArrayList<>( result.get().getCount() );
2255
2256        for ( Row<String, String, String> row : result.get().getList() )
2257        {
2258            dependenciesIds.add( getStringValue( row.getColumnSlice(), "projectVersionMetadataModel.key" ) );
2259        }
2260
2261        List<ProjectVersionReference> references = new ArrayList<>( result.get().getCount() );
2262
2263        for ( String key : dependenciesIds )
2264        {
2265            ColumnFamilyResult<String, String> columnFamilyResult =
2266                this.projectVersionMetadataTemplate.queryColumns( key );
2267            references.add( new ProjectVersionReference( ProjectVersionReference.ReferenceType.DEPENDENCY, //
2268                                                         columnFamilyResult.getString( PROJECT_ID.toString() ), //
2269                                                         columnFamilyResult.getString( NAMESPACE_ID.toString() ), //
2270                                                         columnFamilyResult.getString( PROJECT_VERSION.toString() ) ) );
2271        }
2272
2273        return references;
2274    }
2275
2276    @Override
2277    public void removeProjectVersion( RepositorySession session, final String repoId, final String namespace, final String projectId,
2278                                      final String projectVersion )
2279        throws MetadataRepositoryException
2280    {
2281
2282        QueryResult<OrderedRows<String, String, String>> result = HFactory //
2283            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2284            .setColumnFamily( cassandraArchivaManager.getProjectVersionMetadataFamilyName() ) //
2285            .setColumnNames( VERSION.toString() ) //
2286            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2287            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2288            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
2289            .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
2290            .execute();
2291
2292        for ( Row<String, String, String> row : result.get().getList() )
2293        {
2294            this.projectVersionMetadataTemplate.deleteRow( row.getKey() );
2295            removeMailingList( row.getKey() );
2296            removeLicenses( row.getKey() );
2297            removeDependencies( row.getKey() );
2298        }
2299
2300        RangeSlicesQuery<String, String, String> query = HFactory //
2301            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2302            .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2303            .setColumnNames( NAMESPACE_ID.toString() ); //
2304
2305        query = query.addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2306            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2307            .addEqualsExpression( PROJECT.toString(), projectId ) //
2308            .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion );
2309
2310        result = query.execute();
2311
2312        for ( Row<String, String, String> row : result.get() )
2313        {
2314            this.artifactMetadataTemplate.deleteRow( row.getKey() );
2315
2316        }
2317    }
2318
2319    @Override
2320    public List<ArtifactMetadata> getArtifacts( RepositorySession session, final String repoId, final String namespace,
2321                                                final String projectId, final String projectVersion )
2322        throws MetadataResolutionException
2323    {
2324
2325        QueryResult<OrderedRows<String, String, String>> result =
2326            HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2327                .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName() ) //
2328                .setColumnNames( ArtifactMetadataModel.COLUMNS )//
2329                .setRowCount( Integer.MAX_VALUE ) //
2330                .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2331                .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2332                .addEqualsExpression( PROJECT.toString(), projectId ) //
2333                .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
2334                .execute();
2335
2336        if ( result.get() == null || result.get().getCount() < 1 )
2337        {
2338            return Collections.emptyList();
2339        }
2340
2341        List<ArtifactMetadata> artifactMetadatas = new ArrayList<>( result.get().getCount() );
2342
2343        for ( Row<String, String, String> row : result.get() )
2344        {
2345            String key = row.getKey();
2346            artifactMetadatas.add( mapArtifactMetadataStringColumnSlice( key, row.getColumnSlice() ) );
2347        }
2348
2349        result = HFactory.createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2350            .setColumnFamily( cassandraArchivaManager.getMetadataFacetFamilyName() ) //
2351            .setColumnNames( MetadataFacetModel.COLUMNS ) //
2352            .setRowCount( Integer.MAX_VALUE ) //
2353            .addEqualsExpression( REPOSITORY_NAME.toString(), repoId ) //
2354            .addEqualsExpression( NAMESPACE_ID.toString(), namespace ) //
2355            .addEqualsExpression( PROJECT_ID.toString(), projectId ) //
2356            .addEqualsExpression( PROJECT_VERSION.toString(), projectVersion ) //
2357            .execute();
2358
2359        return mapArtifactFacetToArtifact(result, artifactMetadatas);
2360    }
2361
2362    /**
2363     * Attach metadata to each of the  ArtifactMetadata objects
2364     */
2365    private List<ArtifactMetadata> mapArtifactFacetToArtifact( QueryResult<OrderedRows<String, String, String>> result, List<ArtifactMetadata> artifactMetadatas) {
2366        if ( result.get() == null || result.get().getCount() < 1 )
2367        {
2368            return artifactMetadatas;
2369        }
2370
2371        final List<MetadataFacetModel> metadataFacetModels = new ArrayList<>( result.get().getCount() );
2372
2373        for ( Row<String, String, String> row : result.get() )
2374        {
2375            ColumnSlice<String, String> columnSlice = row.getColumnSlice();
2376            MetadataFacetModel metadataFacetModel = new MetadataFacetModel();
2377            metadataFacetModel.setFacetId( getStringValue( columnSlice, FACET_ID.toString() ) );
2378            metadataFacetModel.setName( getStringValue( columnSlice, NAME.toString() ) );
2379            metadataFacetModel.setValue( getStringValue( columnSlice, VALUE.toString() ) );
2380            metadataFacetModel.setKey( getStringValue( columnSlice, KEY.toString() ) );
2381            metadataFacetModel.setProjectVersion( getStringValue( columnSlice, PROJECT_VERSION.toString() ) );
2382            metadataFacetModels.add( metadataFacetModel );
2383        }
2384
2385        // rebuild MetadataFacet for artifacts
2386
2387        for ( final ArtifactMetadata artifactMetadata : artifactMetadatas )
2388        {
2389            Iterable<MetadataFacetModel> metadataFacetModelIterable =
2390                Iterables.filter( metadataFacetModels, new Predicate<MetadataFacetModel>()
2391                {
2392                    @Override
2393                    public boolean apply( MetadataFacetModel metadataFacetModel )
2394                    {
2395                        if ( metadataFacetModel != null )
2396                        {
2397                            return StringUtils.equals( artifactMetadata.getVersion(),
2398                                                       metadataFacetModel.getProjectVersion() );
2399                        }
2400                        return false;
2401                    }
2402                } );
2403            Iterator<MetadataFacetModel> iterator = metadataFacetModelIterable.iterator();
2404            Map<String, List<MetadataFacetModel>> metadataFacetValuesPerFacetId = new HashMap<>();
2405            while ( iterator.hasNext() )
2406            {
2407                MetadataFacetModel metadataFacetModel = iterator.next();
2408                List<MetadataFacetModel> values = metadataFacetValuesPerFacetId.get( metadataFacetModel.getName() );
2409                if ( values == null )
2410                {
2411                    values = new ArrayList<>();
2412                    metadataFacetValuesPerFacetId.put( metadataFacetModel.getFacetId(), values );
2413                }
2414                values.add( metadataFacetModel );
2415
2416            }
2417
2418            for ( Map.Entry<String, List<MetadataFacetModel>> entry : metadataFacetValuesPerFacetId.entrySet() )
2419            {
2420                MetadataFacetFactory metadataFacetFactory = getFacetFactory( entry.getKey() );
2421                if ( metadataFacetFactory != null )
2422                {
2423                    List<MetadataFacetModel> facetModels = entry.getValue();
2424                    if ( !facetModels.isEmpty() )
2425                    {
2426                        MetadataFacet metadataFacet = metadataFacetFactory.createMetadataFacet();
2427                        Map<String, String> props = new HashMap<>( facetModels.size() );
2428                        for ( MetadataFacetModel metadataFacetModel : facetModels )
2429                        {
2430                            props.put( metadataFacetModel.getKey(), metadataFacetModel.getValue() );
2431                        }
2432                        metadataFacet.fromProperties( props );
2433                        artifactMetadata.addFacet( metadataFacet );
2434                    }
2435                }
2436            }
2437        }
2438
2439        return artifactMetadatas;
2440    }
2441
2442    @Override
2443    public void close()
2444        throws MetadataRepositoryException
2445    {
2446        logger.trace( "close" );
2447    }
2448
2449
2450    private static class ModelMapperHolder
2451    {
2452        private static ModelMapper MODEL_MAPPER = new ModelMapper();
2453    }
2454
2455    protected ModelMapper getModelMapper()
2456    {
2457        return ModelMapperHolder.MODEL_MAPPER;
2458    }
2459
2460    /**
2461     * This implementation just calls getArtifactsByAttribute( null, text, repositoryId ). We can't search artifacts by
2462     * any property.
2463     */
2464    @Override
2465    public List<ArtifactMetadata> searchArtifacts( final RepositorySession session, final String repositoryId,
2466                                                   final String text, final boolean exact )
2467        throws MetadataRepositoryException
2468    {
2469        return this.getArtifactsByAttribute( session, null, text, repositoryId );
2470    }
2471
2472    /**
2473     * The exact parameter is ignored as we can't do non exact searches in Cassandra
2474     */
2475    @Override
2476    public List<ArtifactMetadata> searchArtifacts( final RepositorySession session, final String repositoryId,
2477                                                   final String key, final String text, final boolean exact )
2478        throws MetadataRepositoryException
2479    {
2480        // TODO optimize
2481        List<ArtifactMetadata> artifacts = new LinkedList<ArtifactMetadata>();
2482        artifacts.addAll( this.getArtifactsByAttribute( session, key, text, repositoryId ) );
2483        artifacts.addAll( this.getArtifactsByProjectVersionAttribute( session, key, text, repositoryId ) );
2484        return artifacts;
2485    }
2486
2487    @Override
2488    public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId,
2489                                                       final QueryParameter queryParameter ) throws MetadataResolutionException
2490    {
2491        RangeSlicesQuery<String, String, String> query = HFactory //
2492            .createRangeSlicesQuery( keyspace, ss, ss, ss ) //
2493            .setColumnFamily( cassandraArchivaManager.getArtifactMetadataFamilyName( ) ) //
2494            .setColumnNames( ArtifactMetadataModel.COLUMNS ); //
2495
2496        query = query.addEqualsExpression( REPOSITORY_NAME.toString(), repositoryId );
2497
2498        QueryResult<OrderedRows<String, String, String>> result = query.execute();
2499
2500        try
2501        {
2502            return StreamSupport.stream( createResultSpliterator( result, ( Row<String, String, String> row, ArtifactMetadata last ) ->
2503                mapArtifactMetadataStringColumnSlice( row.getKey( ), row.getColumnSlice( ) ) ), false )
2504                .skip( queryParameter.getOffset( ) ).limit( queryParameter.getLimit( ) );
2505        }
2506        catch ( MetadataRepositoryException e )
2507        {
2508            throw new MetadataResolutionException( e.getMessage( ), e );
2509        }
2510    }
2511
2512    @Override
2513    public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repoId,
2514                                                       final String namespace, final String projectId, final String projectVersion,
2515                                                       final QueryParameter queryParameter ) throws MetadataResolutionException
2516    {
2517        // Currently we have to align the facets with the artifacts, which means querying artifacts, querying facets and combining them.
2518        // I so no stream friendly way to do this, so we just use the collection based method and return the stream.
2519        // TODO: Maybe we can query the facets for each artifact separately, but not sure, if this affects performance significantly
2520        //       We need some data to verify this.
2521        return getArtifacts( session, repoId, namespace, projectId, projectVersion ).stream( ).skip( queryParameter.getOffset( ) ).limit( queryParameter.getLimit( ) );
2522    }
2523}