| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | 5 import logging |
| 6 import os | 6 import os |
| 7 from StringIO import StringIO |
| 7 import sys | 8 import sys |
| 8 | 9 |
| 9 from appengine_wrappers import webapp | 10 from appengine_wrappers import webapp |
| 10 from appengine_wrappers import memcache | 11 from appengine_wrappers import memcache |
| 11 from appengine_wrappers import urlfetch | 12 from appengine_wrappers import urlfetch |
| 12 | 13 |
| 13 from api_data_source import APIDataSource | 14 from api_data_source import APIDataSource |
| 14 from api_list_data_source import APIListDataSource | 15 from api_list_data_source import APIListDataSource |
| 15 from appengine_blobstore import AppEngineBlobstore | 16 from appengine_blobstore import AppEngineBlobstore |
| 16 from appengine_memcache import AppEngineMemcache | 17 from in_memory_object_store import InMemoryObjectStore |
| 17 from appengine_url_fetcher import AppEngineUrlFetcher | 18 from appengine_url_fetcher import AppEngineUrlFetcher |
| 18 from branch_utility import BranchUtility | 19 from branch_utility import BranchUtility |
| 19 from example_zipper import ExampleZipper | 20 from example_zipper import ExampleZipper |
| 20 from file_system_cache import FileSystemCache | 21 from file_system_cache import FileSystemCache |
| 22 import file_system_cache as fs_cache |
| 21 from github_file_system import GithubFileSystem | 23 from github_file_system import GithubFileSystem |
| 22 from intro_data_source import IntroDataSource | 24 from intro_data_source import IntroDataSource |
| 23 from local_file_system import LocalFileSystem | 25 from local_file_system import LocalFileSystem |
| 24 from memcache_file_system import MemcacheFileSystem | 26 from memcache_file_system import MemcacheFileSystem |
| 25 from samples_data_source import SamplesDataSource | 27 from samples_data_source import SamplesDataSource |
| 26 from server_instance import ServerInstance | 28 from server_instance import ServerInstance |
| 27 from subversion_file_system import SubversionFileSystem | 29 from subversion_file_system import SubversionFileSystem |
| 28 from template_data_source import TemplateDataSource | 30 from template_data_source import TemplateDataSource |
| 29 import url_constants | 31 import url_constants |
| 30 | 32 |
| 31 # The branch that the server will default to when no branch is specified in the | 33 # The branch that the server will default to when no branch is specified in the |
| 32 # URL. This is necessary because it is not possible to pass flags to the script | 34 # URL. This is necessary because it is not possible to pass flags to the script |
| 33 # handler. | 35 # handler. |
| 34 DEFAULT_BRANCH = 'local' | 36 DEFAULT_BRANCH = 'local' |
| 35 | 37 |
| 36 BRANCH_UTILITY_MEMCACHE = AppEngineMemcache('branch_utility') | 38 BRANCH_UTILITY_MEMCACHE = InMemoryObjectStore('branch_utility') |
| 37 BRANCH_UTILITY = BranchUtility(url_constants.OMAHA_PROXY_URL, | 39 BRANCH_UTILITY = BranchUtility(url_constants.OMAHA_PROXY_URL, |
| 38 DEFAULT_BRANCH, | 40 DEFAULT_BRANCH, |
| 39 AppEngineUrlFetcher(None), | 41 AppEngineUrlFetcher(None), |
| 40 BRANCH_UTILITY_MEMCACHE) | 42 BRANCH_UTILITY_MEMCACHE) |
| 41 | 43 |
| 44 GITHUB_MEMCACHE = InMemoryObjectStore('github') |
| 42 GITHUB_FILE_SYSTEM = GithubFileSystem( | 45 GITHUB_FILE_SYSTEM = GithubFileSystem( |
| 43 AppEngineUrlFetcher(url_constants.GITHUB_URL), | 46 AppEngineUrlFetcher(url_constants.GITHUB_URL), |
| 44 AppEngineMemcache('github'), | 47 GITHUB_MEMCACHE, |
| 45 AppEngineBlobstore()) | 48 AppEngineBlobstore()) |
| 46 GITHUB_CACHE_BUILDER = FileSystemCache.Builder(GITHUB_FILE_SYSTEM) | 49 GITHUB_CACHE_BUILDER = FileSystemCache.Builder(GITHUB_FILE_SYSTEM, |
| 50 GITHUB_MEMCACHE) |
| 47 | 51 |
| 48 STATIC_DIR_PREFIX = 'docs/server2' | 52 STATIC_DIR_PREFIX = 'docs/server2' |
| 49 EXTENSIONS_PATH = 'chrome/common/extensions' | 53 EXTENSIONS_PATH = 'chrome/common/extensions' |
| 50 DOCS_PATH = 'docs' | 54 DOCS_PATH = 'docs' |
| 51 API_PATH = 'api' | 55 API_PATH = 'api' |
| 52 INTRO_PATH = DOCS_PATH + '/server2/templates/intros' | 56 TEMPLATE_PATH = DOCS_PATH + '/server2/templates' |
| 53 ARTICLE_PATH = DOCS_PATH + '/server2/templates/articles' | 57 INTRO_PATH = TEMPLATE_PATH + '/intros' |
| 54 PUBLIC_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/public' | 58 ARTICLE_PATH = TEMPLATE_PATH + '/articles' |
| 55 PRIVATE_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/private' | 59 PUBLIC_TEMPLATE_PATH = TEMPLATE_PATH + '/public' |
| 60 PRIVATE_TEMPLATE_PATH = TEMPLATE_PATH + '/private' |
| 56 EXAMPLES_PATH = DOCS_PATH + '/examples' | 61 EXAMPLES_PATH = DOCS_PATH + '/examples' |
| 57 FULL_EXAMPLES_PATH = DOCS_PATH + '/' + EXAMPLES_PATH | |
| 58 | 62 |
| 59 # Global cache of instances because Handler is recreated for every request. | 63 # Global cache of instances because Handler is recreated for every request. |
| 60 SERVER_INSTANCES = {} | 64 SERVER_INSTANCES = {} |
| 61 | 65 |
| 66 def _CreateMemcacheFileSystem(branch, branch_memcache): |
| 67 svn_url = _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH |
| 68 stat_fetcher = AppEngineUrlFetcher( |
| 69 svn_url.replace(url_constants.SVN_URL, url_constants.VIEWVC_URL)) |
| 70 fetcher = AppEngineUrlFetcher(svn_url) |
| 71 return MemcacheFileSystem(SubversionFileSystem(fetcher, stat_fetcher), |
| 72 branch_memcache) |
| 73 |
| 62 def _GetInstanceForBranch(channel_name, local_path): | 74 def _GetInstanceForBranch(channel_name, local_path): |
| 63 branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name) | 75 branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name) |
| 64 if branch in SERVER_INSTANCES: | 76 if branch in SERVER_INSTANCES: |
| 65 return SERVER_INSTANCES[branch] | 77 return SERVER_INSTANCES[branch] |
| 78 branch_memcache = InMemoryObjectStore(branch) |
| 66 if branch == 'local': | 79 if branch == 'local': |
| 67 file_system = LocalFileSystem(local_path) | 80 file_system = LocalFileSystem(local_path) |
| 68 else: | 81 else: |
| 69 svn_url = _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH | 82 file_system = _CreateMemcacheFileSystem(branch, branch_memcache) |
| 70 stat_fetcher = AppEngineUrlFetcher( | |
| 71 svn_url.replace(url_constants.SVN_URL, url_constants.VIEWVC_URL)) | |
| 72 fetcher = AppEngineUrlFetcher(svn_url) | |
| 73 file_system = MemcacheFileSystem( | |
| 74 SubversionFileSystem(fetcher, stat_fetcher), | |
| 75 AppEngineMemcache(branch)) | |
| 76 | 83 |
| 77 cache_builder = FileSystemCache.Builder(file_system) | 84 cache_builder = FileSystemCache.Builder(file_system, branch_memcache) |
| 78 api_list_data_source = APIListDataSource(cache_builder, | 85 api_list_data_source_factory = APIListDataSource.Factory(cache_builder, |
| 79 file_system, | 86 file_system, |
| 80 API_PATH, | 87 API_PATH, |
| 81 PUBLIC_TEMPLATE_PATH) | 88 PUBLIC_TEMPLATE_PATH) |
| 82 intro_data_source = IntroDataSource(cache_builder, | 89 intro_data_source_factory = IntroDataSource.Factory( |
| 83 [INTRO_PATH, ARTICLE_PATH]) | 90 cache_builder, |
| 91 [INTRO_PATH, ARTICLE_PATH]) |
| 84 samples_data_source_factory = SamplesDataSource.Factory(branch, | 92 samples_data_source_factory = SamplesDataSource.Factory(branch, |
| 85 file_system, | 93 file_system, |
| 86 GITHUB_FILE_SYSTEM, | 94 GITHUB_FILE_SYSTEM, |
| 87 cache_builder, | 95 cache_builder, |
| 88 GITHUB_CACHE_BUILDER, | 96 GITHUB_CACHE_BUILDER, |
| 89 EXAMPLES_PATH) | 97 EXAMPLES_PATH) |
| 90 api_data_source_factory = APIDataSource.Factory(cache_builder, | 98 api_data_source_factory = APIDataSource.Factory(cache_builder, |
| 91 API_PATH, | 99 API_PATH, |
| 92 samples_data_source_factory) | 100 samples_data_source_factory) |
| 93 template_data_source_factory = TemplateDataSource.Factory( | 101 template_data_source_factory = TemplateDataSource.Factory( |
| 94 channel_name, | 102 channel_name, |
| 95 api_data_source_factory, | 103 api_data_source_factory, |
| 96 api_list_data_source, | 104 api_list_data_source_factory, |
| 97 intro_data_source, | 105 intro_data_source_factory, |
| 98 samples_data_source_factory, | 106 samples_data_source_factory, |
| 99 cache_builder, | 107 cache_builder, |
| 100 PUBLIC_TEMPLATE_PATH, | 108 PUBLIC_TEMPLATE_PATH, |
| 101 PRIVATE_TEMPLATE_PATH) | 109 PRIVATE_TEMPLATE_PATH) |
| 102 example_zipper = ExampleZipper(file_system, | 110 example_zipper = ExampleZipper(file_system, |
| 103 cache_builder, | 111 cache_builder, |
| 104 DOCS_PATH, | 112 DOCS_PATH, |
| 105 EXAMPLES_PATH) | 113 EXAMPLES_PATH) |
| 106 SERVER_INSTANCES[branch] = ServerInstance( | 114 SERVER_INSTANCES[branch] = ServerInstance( |
| 107 template_data_source_factory, | 115 template_data_source_factory, |
| 108 example_zipper, | 116 example_zipper, |
| 109 cache_builder) | 117 cache_builder) |
| 110 return SERVER_INSTANCES[branch] | 118 return SERVER_INSTANCES[branch] |
| 111 | 119 |
| 112 def _GetURLFromBranch(branch): | 120 def _GetURLFromBranch(branch): |
| 113 if branch == 'trunk': | 121 if branch == 'trunk': |
| 114 return url_constants.SVN_TRUNK_URL + '/src' | 122 return url_constants.SVN_TRUNK_URL + '/src' |
| 115 return url_constants.SVN_BRANCH_URL + '/' + branch + '/src' | 123 return url_constants.SVN_BRANCH_URL + '/' + branch + '/src' |
| 116 | 124 |
| 117 def _CleanBranches(): | 125 def _CleanBranches(): |
| 118 numbers = BRANCH_UTILITY.GetAllBranchNumbers() | 126 numbers = BRANCH_UTILITY.GetAllBranchNumbers() |
| 119 for key in SERVER_INSTANCES.keys(): | 127 for key in SERVER_INSTANCES.keys(): |
| 120 if key not in numbers: | 128 if key not in numbers: |
| 121 SERVER_INSTANCES.pop(key) | 129 SERVER_INSTANCES.pop(key) |
| 122 | 130 |
| 131 class _MockResponse(object): |
| 132 def __init__(self): |
| 133 self.status = 200 |
| 134 self.out = StringIO() |
| 135 |
| 136 def set_status(self, status): |
| 137 self.status = status |
| 138 |
| 139 class _MockRequest(object): |
| 140 def __init__(self, path): |
| 141 self.headers = {} |
| 142 self.path = path |
| 143 |
| 123 class Handler(webapp.RequestHandler): | 144 class Handler(webapp.RequestHandler): |
| 124 def __init__(self, request, response, local_path=EXTENSIONS_PATH): | 145 def __init__(self, request, response, local_path=EXTENSIONS_PATH): |
| 125 self._local_path = local_path | 146 self._local_path = local_path |
| 126 super(Handler, self).__init__(request, response) | 147 super(Handler, self).__init__(request, response) |
| 127 | 148 |
| 128 def _NavigateToPath(self, path): | 149 def _HandleGet(self, path): |
| 129 channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path) | 150 channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path) |
| 130 # TODO: Detect that these are directories and serve index.html out of them. | 151 # TODO: Detect that these are directories and serve index.html out of them. |
| 131 if real_path.strip('/') == 'apps': | 152 if real_path.strip('/') == 'apps': |
| 132 real_path = 'apps/index.html' | 153 real_path = 'apps/index.html' |
| 133 if real_path.strip('/') == 'extensions': | 154 if real_path.strip('/') == 'extensions': |
| 134 real_path = 'extensions/index.html' | 155 real_path = 'extensions/index.html' |
| 135 _CleanBranches() | 156 _CleanBranches() |
| 136 _GetInstanceForBranch(channel_name, self._local_path).Get(real_path, | 157 _GetInstanceForBranch(channel_name, self._local_path).Get(real_path, |
| 137 self.request, | 158 self.request, |
| 138 self.response) | 159 self.response) |
| 139 | 160 |
| 161 def _Render(self, files, branch): |
| 162 for f in files: |
| 163 path = branch + f.split(PUBLIC_TEMPLATE_PATH)[-1] |
| 164 self.request = _MockRequest(path) |
| 165 self.response = _MockResponse() |
| 166 self._HandleGet(path) |
| 167 |
| 168 def _HandleCron(self, path): |
| 169 branch = path.split('/')[-1] |
| 170 logging.info('Running cron job for %s.' % branch) |
| 171 branch_memcache = InMemoryObjectStore(branch) |
| 172 file_system = _CreateMemcacheFileSystem(branch, branch_memcache) |
| 173 builder = FileSystemCache.Builder(file_system, branch_memcache) |
| 174 render_cache = builder.build(lambda x: self._Render(x, branch), |
| 175 fs_cache.RENDER) |
| 176 render_cache.GetFromFileListing(PUBLIC_TEMPLATE_PATH) |
| 177 |
| 140 def get(self): | 178 def get(self): |
| 141 path = self.request.path | 179 path = self.request.path |
| 142 if '_ah/warmup' in path: | 180 if path.startswith('/cron'): |
| 143 logging.info('Warmup request.') | 181 self._HandleCron(path) |
| 144 self._NavigateToPath('trunk/extensions/samples.html') | 182 else: |
| 145 self._NavigateToPath('dev/extensions/samples.html') | 183 # Redirect paths like "directory" to "directory/". This is so relative |
| 146 self._NavigateToPath('beta/extensions/samples.html') | 184 # file paths will know to treat this as a directory. |
| 147 self._NavigateToPath('stable/extensions/samples.html') | 185 if os.path.splitext(path)[1] == '' and path[-1] != '/': |
| 148 # Only do this request if we are on the deployed server. | 186 self.redirect(path + '/') |
| 149 # Bug: http://crbug.com/141910 | 187 path = path.replace('/chrome/', '') |
| 150 if DEFAULT_BRANCH != 'local': | 188 path = path.strip('/') |
| 151 self._NavigateToPath('apps/samples.html') | 189 self._HandleGet(path) |
| 152 return | |
| 153 | |
| 154 # Redirect paths like "directory" to "directory/". This is so relative file | |
| 155 # paths will know to treat this as a directory. | |
| 156 if os.path.splitext(path)[1] == '' and path[-1] != '/': | |
| 157 self.redirect(path + '/') | |
| 158 path = path.replace('/chrome/', '') | |
| 159 path = path.strip('/') | |
| 160 self._NavigateToPath(path) | |
| OLD | NEW |