Exemple #1
0
		protected virtual void RemoveFromActiveList(MediaCacheRecord record)
		{
			var baseMethod = typeof(MediaCache).GetMethod("RemoveFromActiveList", BindingFlags.Instance | BindingFlags.NonPublic);

			if (baseMethod != null)
				baseMethod.Invoke(this, new object[] { record });
			else Log.Error("Dianoga: Couldn't use malevolent private reflection on RemoveFromActiveList! This may mean Dianoga isn't entirely compatible with this version of Sitecore, though it should only affect a performance optimization.", this);
		}
        // the 'active list' is an internal construct that lets Sitecore stream media to the client at the same time as it's being written to cache
        // unfortunately though the rest of MediaCache is virtual, these methods are inexplicably not
        protected virtual void AddToActiveList(MediaCacheRecord record)
        {
            var baseMethod = typeof(MediaCache).GetMethod("AddToActiveList", BindingFlags.Instance | BindingFlags.NonPublic);

            if (baseMethod != null)
                baseMethod.Invoke(this, new object[] { record });
            else Log.Error("Dianoga: Couldn't use malevolent private reflection on AddToActiveList! This may mean Dianoga isn't entirely compatible with this version of Sitecore, though it should only affect a performance optimization.", this);

            // HEY SITECORE, CAN WE GET THESE VIRTUAL? KTHX.
        }
Exemple #3
0
        // the 'active list' is an internal construct that lets Sitecore stream media to the client at the same time as it's being written to cache
        // unfortunately though the rest of MediaCache is virtual, these methods are inexplicably not
        protected virtual void AddToActiveList(MediaCacheRecord record)
        {
            var baseMethod = typeof(MediaCache).GetMethod("AddToActiveList", BindingFlags.Instance | BindingFlags.NonPublic);

            if (baseMethod != null)
            {
                baseMethod.Invoke(this, new object[] { record });
            }
            else
            {
                Log.Error("Dianoga: Couldn't use malevolent private reflection on AddToActiveList! This may mean Dianoga isn't entirely compatible with this version of Sitecore, though it should only affect a performance optimization.", this);
            }

            // HEY SITECORE, CAN WE GET THESE VIRTUAL? KTHX.
        }
        public override bool AddStream(Media media, MediaOptions options, MediaStream stream, out MediaStream cachedStream)
        {
            /* STOCK METHOD (Decompiled) */
            Assert.ArgumentNotNull(media, "media");
            Assert.ArgumentNotNull(options, "options");
            Assert.ArgumentNotNull(stream, "stream");

            cachedStream = null;

            if (!CanCache(media, options))
            {
                return(false);
            }

            MediaCacheRecord cacheRecord = CreateCacheRecord(media, options, stream);

            if (cacheRecord == null)
            {
                return(false);
            }

            cachedStream = cacheRecord.GetStream();

            if (cachedStream == null)
            {
                return(false);
            }

            AddToActiveList(cacheRecord);
            /* END STOCK */

            // we store the site context because on the background thread: without the Sitecore context saved (on a worker thread), that disables the media cache
            var currentSite = Context.Site;

            cacheRecord.PersistAsync((() => OnAfterPersist(cacheRecord, currentSite)));

            return(true);
        }
		protected virtual void OnAfterPersist(MediaCacheRecord record, SiteContext originalSiteContext)
		{
			string mediaPath = string.Empty;
			try
			{
				RemoveFromActiveList(record);

				// Housekeeping: since we call AddStream() to insert the optimized version, we have to keep AddStream() from calling OnAfterPersist() from that call, causing an optimization loop
				var id = record.Media.MediaData.MediaId;

				if (StreamsInOptimization.Contains(id))
				{
					lock (OptimizeLock)
					{
						if (StreamsInOptimization.Contains(id))
						{
							StreamsInOptimization.Remove(id);
							return;
						}
					}
				}

				lock (OptimizeLock)
				{
					StreamsInOptimization.Add(id);
				}

				// grab the stream from cache and optimize it
				if (!record.HasStream) return;

				var stream = record.GetStream();

				if (!_optimizer.CanOptimize(stream)) return;

				var optimizedStream = _optimizer.Process(stream, record.Options);
				mediaPath = stream.MediaItem.MediaPath;

				if (optimizedStream == null)
				{
					Log.Info("Dianoga: async optimazation result was null, not doing any optimizing for {0}".FormatWith(mediaPath), this);
					return;
				}

				// we have to switch the site context because we're on a background thread, and Sitecore.Context is lost (so the site is always null)
				// if the site is null the media cache ignores all added entries
				using (new SiteContextSwitcher(originalSiteContext))
				{
					for (int i = 0; i < 5; i++)
					{
						try
						{
							// only here to satisfy out param
							MediaStream dgafStream;

							bool success = AddStream(record.Media, record.Options, optimizedStream, out dgafStream);
							if (dgafStream != null) dgafStream.Dispose();

							if (!success)
								Log.Warn("Dianoga: The media cache rejected adding {0}. This is unexpected!".FormatWith(mediaPath), this);
						}
						catch (Exception ex)
						{
							Log.Error("Dianoga: Error occurred adding stream to media cache. Will retry in 10 sec.", ex, this);
							Thread.Sleep(10000);
							continue;
						}

						break;
					}
				}
			}
			catch (Exception ex)
			{
				// this runs in a background thread, and an exception here would cause IIS to terminate the app pool. Bad! So we catch/log, just in case.
				Log.Error("Dianoga: Exception occurred on the background thread when optimizing: {0}".FormatWith(mediaPath), ex, this);
			}
		}
Exemple #6
0
		private void Optimize(SiteContext currentSite, Media media, MediaOptions options)
		{
			var mediaItem = media.MediaData.MediaItem;

			MediaStream originalMediaStream = null;
			MediaStream backupMediaStream = null;
			MediaStream optimizedMediaStream = null;

			try
			{
				// switch to the right site context (see above)
				using (new SiteContextSwitcher(currentSite))
				{
					//if the image is already optimized, abort task
					if (this.Contains(media, options))
					{
						return;
					}

					//get stream from mediaItem to reduce memory usage
					using (var stream = media.GetStream(options))
					{
						// make a copy of the stream to use
						var originalStream = new MemoryStream();
						stream.CopyTo(originalStream);
						originalStream.Seek(0, SeekOrigin.Begin);

						originalMediaStream = new MediaStream(originalStream, media.Extension, mediaItem);

						// make a stream backup we can use to persist in the event of an optimization failure
						// (which will dispose of originalStream)
						var backupStream = new MemoryStream();
						originalStream.CopyTo(backupStream);
						backupStream.Seek(0, SeekOrigin.Begin);

						backupMediaStream = new MediaStream(backupStream, media.Extension, mediaItem);
					}

					MediaCacheRecord cacheRecord = null;

					optimizedMediaStream = _optimizer.Process(originalMediaStream, options);

					if (optimizedMediaStream == null)
					{
						Log.Info($"Dianoga: {mediaItem.MediaPath} cannot be optimized due to media type or path exclusion", this);
						cacheRecord = CreateCacheRecord(media, options, backupMediaStream);
					}

					if (cacheRecord == null)
					{
						cacheRecord = CreateCacheRecord(media, options, optimizedMediaStream);
					}

					AddToActiveList(cacheRecord);

					cacheRecord.Persist();

					RemoveFromActiveList(cacheRecord);
				}
			}
			catch (Exception ex)
			{
				// this runs in a background thread, and an exception here would cause IIS to terminate the app pool. Bad! So we catch/log, just in case.
				Log.Error($"Dianoga: Exception occurred on the background thread when optimizing: {mediaItem.MediaPath}", ex, this);
			}
			finally
			{
				// release resources used by the optimization task
				originalMediaStream?.Dispose();
				backupMediaStream?.Dispose();
				optimizedMediaStream?.Dispose();
			}
		}
Exemple #7
0
        public override bool AddStream(Media media, MediaOptions options, MediaStream stream, out MediaStream cachedStream)
        {
            /* STOCK METHOD (Decompiled) */
            Assert.ArgumentNotNull(media, "media");
            Assert.ArgumentNotNull(options, "options");
            Assert.ArgumentNotNull(stream, "stream");

            cachedStream = null;

            if (!CanCache(media, options))
            {
                return(false);
            }

            if (string.IsNullOrEmpty(media.MediaData.MediaId))
            {
                return(false);
            }

            if (!stream.Stream.CanRead)
            {
                Log.Warn($"Cannot optimize {media.MediaData.MediaItem.MediaPath} because cache was passed a non readable stream.", this);
                return(false);
            }

            // buffer the stream if it's say a SQL stream
            stream.MakeStreamSeekable();
            stream.Stream.Seek(0, SeekOrigin.Begin);

            // Sitecore will use this to stream the media while we persist
            cachedStream = stream;

            // make a copy of the stream to use temporarily while we async persist
            var copyStream = new MemoryStream();

            stream.CopyTo(copyStream);
            copyStream.Seek(0, SeekOrigin.Begin);

            var copiedMediaStream = new MediaStream(copyStream, stream.Extension, stream.MediaItem);

            // we store the site context because on the background thread: without the Sitecore context saved (on a worker thread), that disables the media cache
            var currentSite = Context.Site;

            ThreadPool.QueueUserWorkItem(state =>
            {
                var mediaPath = stream.MediaItem.MediaPath;

                try
                {
                    // make a stream backup we can use to persist in the event of an optimization failure
                    // (which will dispose of copyStream)
                    var backupStream = new MemoryStream();
                    copyStream.CopyTo(backupStream);
                    backupStream.Seek(0, SeekOrigin.Begin);

                    var backupMediaStream = new MediaStream(backupStream, stream.Extension, stream.MediaItem);

                    // switch to the right site context (see above)
                    using (new SiteContextSwitcher(currentSite))
                    {
                        MediaCacheRecord cacheRecord = null;

                        var optimizedStream = _optimizer.Process(copiedMediaStream, options);

                        if (optimizedStream == null)
                        {
                            Log.Info($"Dianoga: {mediaPath} cannot be optimized due to media type or path exclusion", this);
                            cacheRecord = CreateCacheRecord(media, options, backupMediaStream);
                        }

                        if (cacheRecord == null)
                        {
                            cacheRecord = CreateCacheRecord(media, options, optimizedStream);
                            backupMediaStream.Dispose();
                        }

                        AddToActiveList(cacheRecord);

                        cacheRecord.Persist();

                        RemoveFromActiveList(cacheRecord);
                    }
                }
                catch (Exception ex)
                {
                    // this runs in a background thread, and an exception here would cause IIS to terminate the app pool. Bad! So we catch/log, just in case.
                    Log.Error($"Dianoga: Exception occurred on the background thread when optimizing: {mediaPath}", ex, this);
                }
            });

            return(true);
        }
        protected virtual void OnAfterPersist(MediaCacheRecord record, SiteContext originalSiteContext)
        {
            string mediaPath = string.Empty;

            try
            {
                RemoveFromActiveList(record);

                // Housekeeping: since we call AddStream() to insert the optimized version, we have to keep AddStream() from calling OnAfterPersist() from that call, causing an optimization loop
                var id = record.Media.MediaData.MediaId;

                if (StreamsInOptimization.Contains(id))
                {
                    lock (OptimizeLock)
                    {
                        if (StreamsInOptimization.Contains(id))
                        {
                            StreamsInOptimization.Remove(id);
                            return;
                        }
                    }
                }

                lock (OptimizeLock)
                {
                    StreamsInOptimization.Add(id);
                }

                // grab the stream from cache and optimize it
                if (!record.HasStream)
                {
                    return;
                }

                var stream = record.GetStream();

                if (!_optimizer.CanOptimize(stream))
                {
                    return;
                }

                var optimizedStream = _optimizer.Process(stream, record.Options);
                mediaPath = stream.MediaItem.MediaPath;

                if (optimizedStream == null)
                {
                    Log.Info("Dianoga: async optimazation result was null, not doing any optimizing for {0}".FormatWith(mediaPath), this);
                    return;
                }

                // we have to switch the site context because we're on a background thread, and Sitecore.Context is lost (so the site is always null)
                // if the site is null the media cache ignores all added entries
                using (new SiteContextSwitcher(originalSiteContext))
                {
                    for (int i = 0; i < 5; i++)
                    {
                        try
                        {
                            // only here to satisfy out param
                            MediaStream dgafStream;

                            bool success = AddStream(record.Media, record.Options, optimizedStream, out dgafStream);
                            if (dgafStream != null)
                            {
                                dgafStream.Dispose();
                            }

                            if (!success)
                            {
                                Log.Warn("Dianoga: The media cache rejected adding {0}. This is unexpected!".FormatWith(mediaPath), this);
                            }
                        }
                        catch (Exception ex)
                        {
                            Log.Error("Dianoga: Error occurred adding stream to media cache. Will retry in 10 sec.", ex, this);
                            Thread.Sleep(10000);
                            continue;
                        }

                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                // this runs in a background thread, and an exception here would cause IIS to terminate the app pool. Bad! So we catch/log, just in case.
                Log.Error("Dianoga: Exception occurred on the background thread when optimizing: {0}".FormatWith(mediaPath), ex, this);
            }
        }