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