View Javadoc
1   /*
2    * Copyright (c) 2002-2024, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.identitystore.cache;
35  
36  import fr.paris.lutece.plugins.identitystore.business.contract.ServiceContract;
37  import fr.paris.lutece.plugins.identitystore.business.identity.Identity;
38  import fr.paris.lutece.plugins.identitystore.business.identity.IdentityHome;
39  import fr.paris.lutece.plugins.identitystore.service.identity.IdentityQualityService;
40  import fr.paris.lutece.plugins.identitystore.v3.web.rs.DtoConverter;
41  import fr.paris.lutece.plugins.identitystore.v3.web.rs.dto.common.IdentityDto;
42  import fr.paris.lutece.portal.service.cache.AbstractCacheableService;
43  import fr.paris.lutece.portal.service.util.AppLogService;
44  import org.apache.commons.lang3.StringUtils;
45  
46  import java.sql.Timestamp;
47  import java.util.Objects;
48  
49  /**
50   * cache sur les requêtes d'identité (par CUID ou GUID), qui cache :<br/>
51   * <ul>
52   * <li>Les attributs autorisés en lecture défini dans le contrat de service</li>
53   * <li>Le score de qualité</li>
54   * <li>Le score de couverture</li>
55   * <li>Les données de suspicions de doublons si l'identité est marquée comme suspecte</li>
56   * <li>Les données d'exclusion si l'identité a été exclue d'une ou plusieurs suspicions de doublon avec d'autres identités</li>
57   * </ul>
58   * l'identité elle-même sera récupérée de la DB à chaque fois, afin de vérifier la date de dernière modification :<br/>
59   * <ul>
60   * <li>Si la date est la même dans l'objet caché et en DB, l'objet caché est retourné</li>
61   * <li>Sinon, on supprime l'objet caché du cache, on récupère toutes les infos de la DB et on recache.</li>
62   * </ul>
63   * La clé de cache combine CUID + ID contrat de service
64   */
65  public class IdentityDtoCache extends AbstractCacheableService
66  {
67      public static final String SERVICE_NAME = "IdentityCache";
68  
69      public IdentityDtoCache( )
70      {
71          this.initCache( );
72      }
73  
74      public void refresh( )
75      {
76          AppLogService.debug( "Init Identity cache" );
77          this.resetCache( );
78      }
79  
80      public void put( final IdentityDto identity, final int serviceContractId )
81      {
82          final String cacheKey = computeCacheKey( identity.getCustomerId( ), serviceContractId );
83          if ( this.getKeys( ).contains( cacheKey ) )
84          {
85              this.removeKey( cacheKey );
86          }
87          this.putInCache( cacheKey, identity );
88          AppLogService.debug( "Identity added to cache: " + cacheKey );
89      }
90  
91      public void remove( final String cacheKey )
92      {
93          if ( this.getKeys( ).contains( cacheKey ) )
94          {
95              this.removeKey( cacheKey );
96          }
97          AppLogService.debug( "Identity removed from cache: " + cacheKey );
98      }
99  
100     public IdentityDto getByConnectionId( final String connectionId, final ServiceContract serviceContract )
101     {
102         Identity identity = IdentityHome.findByConnectionId( connectionId );
103         if ( identity != null && identity.isMerged( ) && identity.getMasterIdentityId( ) != null )
104         {
105             identity = IdentityHome.findByPrimaryKey( identity.getMasterIdentityId( ) );
106         }
107         if ( identity == null || StringUtils.isBlank( identity.getCustomerId( ) ) || identity.getLastUpdateDate( ) == null )
108         {
109             return null;
110         }
111         return get( identity.getCustomerId( ), identity.getLastUpdateDate( ), serviceContract );
112     }
113 
114     public IdentityDto getByCustomerId( final String customerId, final ServiceContract serviceContract )
115     {
116         Identity identity = IdentityHome.findByCustomerIdNoAttributes( customerId );
117         if ( identity != null && identity.isMerged( ) && identity.getMasterIdentityId( ) != null )
118         {
119             identity = IdentityHome.findByPrimaryKey( identity.getMasterIdentityId( ) );
120         }
121         if ( identity == null || StringUtils.isBlank( identity.getCustomerId( ) ) || identity.getLastUpdateDate( ) == null )
122         {
123             return null;
124         }
125         return get( identity.getCustomerId( ), identity.getLastUpdateDate( ), serviceContract );
126     }
127 
128     private IdentityDto get( final String customerId, final Timestamp lastUpdateDateFromDb, final ServiceContract serviceContract )
129     {
130         final String cacheKey = computeCacheKey( customerId, serviceContract.getId( ) );
131         final IdentityDto identityDtoFromCache = (IdentityDto) this.getFromCache( cacheKey );
132         if ( identityDtoFromCache == null || !Objects.equals( identityDtoFromCache.getLastUpdateDate( ), lastUpdateDateFromDb ) )
133         {
134             this.remove( cacheKey );
135             final IdentityDto enrichedIdentity = this.getFromDatabaseAndEnrich( customerId, serviceContract );
136             if ( enrichedIdentity == null )
137             {
138                 return null;
139             }
140             this.put( enrichedIdentity, serviceContract.getId( ) );
141             return enrichedIdentity;
142         }
143         return identityDtoFromCache;
144     }
145 
146     public IdentityDto getFromDatabaseAndEnrich( final String customerId, final ServiceContract serviceContract )
147     {
148         final Identity identity = IdentityHome.findMasterIdentityByCustomerId( customerId );
149         if ( identity == null )
150         {
151             return null;
152         }
153         return convertAndEnrich( identity, serviceContract );
154     }
155 
156     private IdentityDto convertAndEnrich( final Identity identity, final ServiceContract serviceContract )
157     {
158         final IdentityDto identityDto = DtoConverter.convertIdentityToDto( identity );
159         IdentityQualityService.instance( ).enrich( null, identityDto, serviceContract, identity );
160         return identityDto;
161     }
162 
163     private String computeCacheKey( final String cuid, final int serviceContractId )
164     {
165         return cuid + "|" + serviceContractId;
166     }
167 
168     @Override
169     public String getName( )
170     {
171         return SERVICE_NAME;
172     }
173 }