2015-05-24 43 views
1
import java.io.IOException; 
import java.lang.ref.SoftReference; 
import java.net.URI; 
import java.security.cert.CRLException; 
import java.security.cert.CertificateException; 
import java.security.cert.X509CRL; 
import java.security.cert.X509Certificate; 
import java.util.Collections; 
import java.util.Date; 
import java.util.HashMap; 
import java.util.Map; 

import javax.naming.NamingException; 

import org.joda.time.DateTime; 
import org.kp.oppr.esb.logger.Logger; 
import org.springframework.beans.factory.annotation.Autowired; 

public class CachedCrlRepository { 

    private static final Logger LOGGER = new Logger("CachedCrlRepository"); 

    private final Map<URI, SoftReference<X509CRL>> crlCache = Collections 
      .synchronizedMap(new HashMap<URI, SoftReference<X509CRL>>());; 

    private static int DEFAULT_CACHE_AGING_HOURS; 

    @Autowired 
    private DgtlSgntrValidator validator; 

    @Autowired 
    private CrlRepository crlRepository; 

    public X509CRL findCrl(URI crlUri, X509Certificate issuerCertificate, 
      Date validationDate) throws DigitalValdiationException, 
      CertificateException, CRLException, IOException, NamingException { 
     SoftReference<X509CRL> crlRef = this.crlCache.get(crlUri); 
     if (null == crlRef) { 
      LOGGER.info("Key CRL URI : " + crlUri + " not found in the cache "); 
      return refreshCrl(crlUri, issuerCertificate, validationDate); 
     } 
     X509CRL crl = crlRef.get(); 
     if (null == crl) { 
      LOGGER.info("CRL Entry garbage collected: " + crlUri); 
      return refreshCrl(crlUri, issuerCertificate, validationDate); 
     } 
     if (validationDate.after(crl.getNextUpdate())) { 
      LOGGER.info("CRL URI no longer valid: " + crlUri); 
      LOGGER.info("CRL validation date: " + validationDate + " is after CRL next update date: " + crl.getNextUpdate()); 
      return refreshCrl(crlUri, issuerCertificate, validationDate); 
     } 

     Date thisUpdate = crl.getThisUpdate(); 
     LOGGER.info("This update " + thisUpdate); 

     /* 
     * The PKI the nextUpdate CRL extension indicates 7 days. The 
     * actual CRL refresh rate is every 3 hours. So it's a bit dangerous to 
     * only base the CRL cache refresh strategy on the nextUpdate field as 
     * indicated by the CRL. 
     */ 

     DateTime cacheMaturityDateTime = new DateTime(thisUpdate) 
       .plusHours(DEFAULT_CACHE_AGING_HOURS); 
     LOGGER.info("Cache maturity Date Time " + cacheMaturityDateTime); 
     if (validationDate.after(cacheMaturityDateTime.toDate())) { 
      LOGGER.info("Validation date: " + validationDate + " is after cache maturity date: " + cacheMaturityDateTime.toDate()); 
      return refreshCrl(crlUri, issuerCertificate, validationDate); 
     } 
     LOGGER.info("using cached CRL: " + crlUri); 
     return crl; 
    } 

    public static int getDEFAULT_CACHE_AGING_HOURS() { 
     return DEFAULT_CACHE_AGING_HOURS; 
    } 

    public static void setDEFAULT_CACHE_AGING_HOURS(int dEFAULT_CACHE_AGING_HOURS) { 
     DEFAULT_CACHE_AGING_HOURS = dEFAULT_CACHE_AGING_HOURS; 
    } 

    private X509CRL refreshCrl(URI crlUri, X509Certificate issuerCertificate, 
      Date validationDate) throws DigitalValdiationException, 
      CertificateException, CRLException, IOException, NamingException { 
     X509CRL crl = crlRepository.downloadCRL(crlUri.toString()); 
     this.crlCache.put(crlUri, new SoftReference<X509CRL>(crl)); 
     return crl; 
    } 




} 

我有這個類CachedCrlrepository存儲來自特定提供者的CRL列表。我想知道我的實現是線程安全的還是我在這裏丟失了某些東西。緩存用於Web服務,因此它是多線程的。線程內存緩存的安全實現

我在這個特殊的方法

private X509CRL refreshCrl(URI crlUri, X509Certificate issuerCertificate, 
       Date validationDate) throws DigitalValdiationException, 
       CertificateException, CRLException, IOException, NamingException { 
      X509CRL crl = crlRepository.downloadCRL(crlUri.toString()); 
      this.crlCache.put(crlUri, new SoftReference<X509CRL>(crl)); 
      return crl; 
     } 

我覺得這一行需要同步

this.crlCache.put(crlUri, new SoftReference<X509CRL>(crl)); 

synchronized(this) 
{ 
this.crlCache.put(crlUri, new SoftReference<X509CRL>(crl)); 

} 

另外一個問題,我看到的是,一個GC運行緩存後仍然有疑問內存中的條目。它從來沒有執行這些行代碼

if (null == crl) { 
      LOGGER.info("CRL Entry garbage collected: " + crlUri); 
      return refreshCrl(crlUri, issuerCertificate, validationDate); 
     } 
+0

我真的很困惑,你想讓它線程安全嗎? –

+0

請考慮使用類似[Caffeine](https://github.com/ben-manes/caffeine/wiki/Cache)的庫。 –

+0

Re。 'put()':不,對'Collections.synchronizedMap()'的調用爲你做。我不喜歡使用'SoftReference',這不是一個好習慣。 – markspace

回答

2

通常你不應該使用在你期待擁擠的交通和你的對象,在這種情況下是crlCache高併發訪問的情況下同步地圖。對於每一個讀取或寫入的線程都會在另一個線程之後等待,並且在負載較重的情況下,線程數量會很高,最終服務器會崩潰。你可以看看ConcurrentHashMap。其目的是在這種情況下有效地工作。

你的第二點:

synchronized(this) 
{ 
this.crlCache.put(crlUri, new SoftReference<X509CRL>(crl)); 

} 

是根本不需要與當前的代碼put方法已經同步。

對於最小的變化更換

private final Map<URI, SoftReference<X509CRL>> crlCache = Collections 
      .synchronizedMap(new HashMap<URI, SoftReference<X509CRL>>());; 

private final ConcurrentHashMap<URI, SoftReference<X509CRL>> crlCache = new ConcurrentHashMap<URI, SoftReference<X509CRL>>(); 

最後,使用SoftReference是好的,但有更好的選擇。來自google的Guava是一個非常強大且高效的緩存構建器。

+0

那麼,不是「必須」,但我同意'如果有任何高需求/線程的機會,'ConcurrentHashMap'可能是一個更好的選擇。 – markspace

+1

「首先,不要爲多線程環境使用同步映射,因爲它們會導致可怕的性能問題。」當然,這是不好的建議。同步集合對於任何競爭將很低的情況都是完全可以接受的解決方案。對象狀態訪問的串行化預計只會在併發訪問高的情況下損害性能,從而產生較高的爭用。此外,在併發訪問不可用的情況下對原子性的要求使得同步成爲一種簡單且通常是乾淨的第一遍解決方案。 – scottb

+0

@scottb:感謝您的建議,相應地進行了編輯,以提出更溫和的聲明。 – ares