1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import posixpath 7 8from custom_logger import CustomLogger 9from extensions_paths import EXAMPLES 10from file_system_util import CreateURLsFromPaths 11from future import Future 12from render_servlet import RenderServlet 13from special_paths import SITE_VERIFICATION_FILE 14from timer import Timer 15 16 17_SUPPORTED_TARGETS = { 18 'examples': (EXAMPLES, 'extensions/examples'), 19} 20 21 22_log = CustomLogger('render_refresher') 23 24 25class _SingletonRenderServletDelegate(RenderServlet.Delegate): 26 def __init__(self, server_instance): 27 self._server_instance = server_instance 28 29 def CreateServerInstance(self): 30 return self._server_instance 31 32 33def _RequestEachItem(title, items, request_callback): 34 '''Runs a task |request_callback| named |title| for each item in |items|. 35 |request_callback| must take an item and return a servlet response. 36 Returns true if every item was successfully run, false if any return a 37 non-200 response or raise an exception. 38 ''' 39 _log.info('%s: starting', title) 40 success_count, failure_count = 0, 0 41 timer = Timer() 42 try: 43 for i, item in enumerate(items): 44 def error_message(detail): 45 return '%s: error rendering %s (%s of %s): %s' % ( 46 title, item, i + 1, len(items), detail) 47 try: 48 response = request_callback(item) 49 if response.status == 200: 50 success_count += 1 51 else: 52 _log.error(error_message('response status %s' % response.status)) 53 failure_count += 1 54 except Exception as e: 55 _log.error(error_message(traceback.format_exc())) 56 failure_count += 1 57 if IsDeadlineExceededError(e): raise 58 finally: 59 _log.info('%s: rendered %s of %s with %s failures in %s', 60 title, success_count, len(items), failure_count, 61 timer.Stop().FormatElapsed()) 62 return success_count == len(items) 63 64 65class RenderRefresher(object): 66 '''Used to refresh any set of renderable resources. Currently only supports 67 assets related to extensions examples.''' 68 def __init__(self, server_instance, request): 69 self._server_instance = server_instance 70 self._request = request 71 72 def GetRefreshPaths(self): 73 return _SUPPORTED_TARGETS.keys() 74 75 def Refresh(self, path): 76 def render(path): 77 request = Request(path, self._request.host, self._request.headers) 78 delegate = _SingletonRenderServletDelegate(self._server_instance) 79 return RenderServlet(request, delegate).Get() 80 81 def request_files_in_dir(path, prefix='', strip_ext=None): 82 '''Requests every file found under |path| in this host file system, with 83 a request prefix of |prefix|. |strip_ext| is an optional list of file 84 extensions that should be stripped from paths before requesting. 85 ''' 86 def maybe_strip_ext(name): 87 if name == SITE_VERIFICATION_FILE or not strip_ext: 88 return name 89 base, ext = posixpath.splitext(name) 90 return base if ext in strip_ext else name 91 files = [maybe_strip_ext(name) 92 for name, _ in CreateURLsFromPaths(master_fs, path, prefix)] 93 return _RequestEachItem(path, files, render) 94 95 # Only support examples for now. 96 if path not in _SUPPORTED_TARGETS: 97 return Future(callback=lambda: False) 98 99 dir = _SUPPORTED_TARGETS[path][0] 100 prefix = _SUPPORTED_TARGETS[path][1] 101 return request_files_in_dir(dir, prefix=prefix) 102