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