@Override public String getBootstrapFallbackUrl(AuraContext context, Map<String, Object> attributes) { String contextPath = context.getContextPath(); String nonce = context.getFrameworkUID(); return String.format( "%s/auraFW/resources/%s/aura/fallback/fallback.bootstrap.js", contextPath, nonce); }
/** * check the top level component/app and get dependencies. * * <p>This routine checks to see that we have a valid top level component. If our top level * component is out of sync, we have to ignore it here, but we _must_ force the client to not * cache the response. * * <p>If there is a QFE, we substitute the QFE descriptor for the one given us, and continue. * Again, we cannot allow caching. * * <p>Finally, if there is no descriptor given, we simply ignore the request and give them an * empty response. Which is done here by returning null. * * <p>Also note that this handles the 'if-modified-since' header, as we want to tell the browser * that nothing changed in that case. * * @param request the request (for exception handling) * @param response the response (for exception handling) * @param context the context to get the definition. * @return the set of descriptors we are sending back, or null in the case that we handled the * response. * @throws IOException if there was an IO exception handling a client out of sync exception * @throws ServletException if there was a problem handling the out of sync */ @Override public Set<DefDescriptor<?>> verifyTopLevel( HttpServletRequest request, HttpServletResponse response, AuraContext context) throws IOException { DefDescriptor<? extends BaseComponentDef> appDesc = context.getApplicationDescriptor(); MasterDefRegistry mdr = context.getDefRegistry(); context.setPreloading(true); if (appDesc == null) { // // This means we have nothing to say to the client, so the response is // left completely empty. // return null; } long ifModifiedSince = request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE); String uid = context.getUid(appDesc); try { try { definitionService.updateLoaded(appDesc); if (uid != null && ifModifiedSince != -1) { // // In this case, we have an unmodified descriptor, so just tell // the client that. // response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return null; } } catch (ClientOutOfSyncException coose) { // // We can't actually handle an out of sync here, since we are doing a // resource load. We have to ignore it, and continue as if nothing happened. // But in the process, we make sure to set 'no-cache' so that the result // is thrown away. This may actually not give the right result in bizarre // corner cases... beware cache inconsistencies on revert after a QFE. // // We actually probably should do something different, like send a minimalist // set of stuff to make the client re-try. // this.setNoCache(response); String oosUid = mdr.getUid(null, appDesc); return mdr.getDependencies(oosUid); } } catch (QuickFixException qfe) { // // A quickfix exception means that we couldn't compile something. // In this case, we still want to preload things, but we want to preload // quick fix values, note that we force NoCache here. // this.setNoCache(response); this.handleServletException(qfe, true, context, request, response, true); return null; } this.setLongCache(response); if (uid == null) { uid = context.getUid(appDesc); } return mdr.getDependencies(uid); }
@Override public String getAppCssUrl(AuraContext context) { String contextPath = context.getContextPath(); StringBuilder defs = new StringBuilder(contextPath).append("/l/"); defs.append(context.getEncodedURL(AuraContext.EncodingStyle.Css)); defs.append("/app.css"); return defs.toString(); }
private String commonJsUrl(String filepath, AuraContext context, Map<String, Object> attributes) { StringBuilder url = new StringBuilder(context.getContextPath()).append("/l/"); url.append(context.getEncodedURL(AuraContext.EncodingStyle.Normal)); url.append(filepath); if (attributes != null) { addAttributes(url, attributes); } return url.toString(); }
/** Start a context and set up default values. */ protected AuraContext setupContext( Mode mode, Format format, DefDescriptor<? extends BaseComponentDef> desc) throws QuickFixException { ContextService contextService = Aura.getContextService(); AuraContext ctxt = contextService.startContext(mode, format, Authentication.AUTHENTICATED, desc); ctxt.setFrameworkUID(Aura.getConfigAdapter().getAuraFrameworkNonce()); String uid = ctxt.getDefRegistry().getUid(null, desc); ctxt.addLoaded(desc, uid); return ctxt; }
private Set<String> getClientLibraryUrls( DefDescriptor<? extends BaseComponentDef> desc, Type libraryType) throws Exception { AuraContext context = Aura.getContextService().getCurrentContext(); context.setApplicationDescriptor(desc); // TODO: Why this extra step, should the Client Library service take care of loading the appDesc // def and // returning the urls? Aura.getDefinitionService().updateLoaded(desc); Set<String> urls = clientLibraryService.getUrls(context, libraryType); return urls; }
/** * get the manifest URL. * * <p>This routine will simply return the string, it does not check to see if the manifest is * enabled first. * * @return a string for the manifest URL. */ @Override public String getManifestUrl(AuraContext context, Map<String, Object> attributes) { String contextPath = context.getContextPath(); String ret = ""; StringBuilder defs = new StringBuilder(contextPath).append("/l/"); defs.append(context.getEncodedURL(AuraContext.EncodingStyle.Bare)); defs.append("/app.manifest"); addAttributes(defs, attributes); ret = defs.toString(); return ret; }
/** Get the set of base scripts for a context. */ @Override public List<String> getNamespacesScripts(AuraContext context) throws QuickFixException { String contextPath = context.getContextPath(); List<String> ret = Lists.newArrayList(); StringBuilder defs = new StringBuilder(contextPath).append("/l/"); defs.append(context.getEncodedURL(AuraContext.EncodingStyle.Normal)); defs.append("/app.js"); ret.add(defs.toString()); return ret; }
private DefDescriptor<ApplicationDef> startAppContext(String markup) { DefDescriptor<ApplicationDef> app = addSourceAutoCleanup(ApplicationDef.class, markup); if (Aura.getContextService().isEstablished()) { Aura.getContextService().endContext(); } AuraContext ctx = Aura.getContextService() .startContext( AuraContext.Mode.UTEST, AuraContext.Format.JSON, AuraContext.Authentication.UNAUTHENTICATED, app); ctx.setFrameworkUID("#FAKEUID#"); return app; }
@Override public void serialize(Json json) throws IOException { json.writeMapBegin(); json.writeMapEntry("descriptor", descriptor); AuraContext context = Aura.getContextService().getCurrentContext(); if (!context.isPreloading() && !context.isPreloaded(getDescriptor())) { // TODONM: revisit this after removing theme from aura context if (context.getThemeList().isEmpty()) { context.addAppThemeDescriptors(); } json.writeMapEntry("code", getCode()); } json.writeMapEnd(); }
@Override public List<String> getStyles(AuraContext context) throws QuickFixException { String contextPath = context.getContextPath(); Set<String> ret = Sets.newLinkedHashSet(); // add css client libraries ret.addAll(getClientLibraryUrls(context, ClientLibraryDef.Type.CSS)); StringBuilder defs = new StringBuilder(contextPath).append("/l/"); defs.append(context.getEncodedURL(AuraContext.EncodingStyle.Css)); defs.append("/app.css"); ret.add(defs.toString()); return new ArrayList<>(ret); }
/** * finish up the validation of a set of compiling defs. * * @param context only needed to do setCurrentNamspace. */ private void finishValidation(AuraContext context, Collection<CompilingDef<?>> compiling) throws QuickFixException { // // Now validate our references. // for (CompilingDef<?> cd : compiling) { // FIXME: setting the current namespace on the context seems extremely hackish context.setCurrentNamespace(cd.descriptor.getNamespace()); if (cd.built) { // FIXME: this may be incorrect, we may need to validate references even when we did // not actually build this, and we may not need to if the registry is pre-compiled. cd.def.validateReferences(); } } // // And finally, mark everything as happily compiled. // for (CompilingDef<?> cd : compiling) { defs.put(cd.descriptor, cd.def); if (cd.built) { if (cd.cacheable) { defsCache.put(cd.descriptor, Optional.of(cd.def)); } cd.def.markValid(); } } }
@SuppressWarnings("unchecked") private List<DefDescriptor<FlavoredStyleDef>> getDefs() { AuraContext context = Aura.getContextService().getCurrentContext(); MasterDefRegistry mdr = context.getDefRegistry(); List<DefDescriptor<FlavoredStyleDef>> defs = new ArrayList<>(); for (DefDescriptor<?> dd : mdr.find(filter)) { // currently getBundle will only work with file-based, unless the other loaders also set the // bundle if (dd.getDefType() == DefType.FLAVORED_STYLE && dd.getBundle().getName().equals(bundle)) { defs.add((DefDescriptor<FlavoredStyleDef>) dd); } } return defs; }
public DefOverviewModel() throws QuickFixException { AuraContext context = Aura.getContextService().getCurrentContext(); BaseComponent<?, ?> component = context.getCurrentComponent(); String desc = (String) component.getAttributes().getValue("descriptor"); DefType defType = DefType.valueOf(((String) component.getAttributes().getValue("defType")).toUpperCase()); DefinitionService definitionService = Aura.getDefinitionService(); DefDescriptor<?> descriptor = definitionService.getDefDescriptor(desc, defType.getPrimaryInterface()); Definition def = descriptor.getDef(); ReferenceTreeModel.assertAccess(def); Map<DefType, List<DefModel>> depsMap = Maps.newEnumMap(DefType.class); Set<DefDescriptor<?>> deps = Sets.newHashSet(); def.appendDependencies(deps); for (DefDescriptor<?> dep : deps) { DefType type = dep.getDefType(); List<DefModel> depsList = depsMap.get(type); if (depsList == null) { depsList = Lists.newArrayList(); depsMap.put(type, depsList); } depsList.add(new DefModel(dep)); } for (Entry<DefType, List<DefModel>> entry : depsMap.entrySet()) { List<DefModel> list = entry.getValue(); Collections.sort(list); Map<String, Object> group = Maps.newHashMap(); group.put("type", AuraTextUtil.initCap(entry.getKey().toString().toLowerCase())); group.put("list", list); dependencies.add(group); } }
public void testWriteResourcesJS() throws Exception { AuraContext context = Aura.getContextService().getCurrentContext(); DefDescriptor<ApplicationDef> appDesc = Aura.getDefinitionService() .getDefDescriptor("clientLibraryTest:clientLibraryTest", ApplicationDef.class); context.setApplicationDescriptor(appDesc); Aura.getDefinitionService().updateLoaded(appDesc); StringBuilder sb = new StringBuilder(); clientLibraryService.writeJs(context, sb); String libraryContent = sb.toString(); assertTrue("Missing resource JS", libraryContent.contains("clientLibraryTest")); try { clientLibraryService.writeJs(context, null); fail("Should not be able to write to null stream"); } catch (Exception e) { checkExceptionFull(e, AuraRuntimeException.class, "Output cannot be null"); } }
/** * get the manifest URL. * * <p>This routine will simply return the string, it does not check to see if the manifest is * enabled first. * * @return a string for the manifest URL. */ public static String getManifestUrl() throws QuickFixException { AuraContext context = Aura.getContextService().getCurrentContext(); String contextPath = context.getContextPath(); String ret = ""; boolean serLastMod = context.getSerializeLastMod(); StringBuilder defs = new StringBuilder(contextPath).append("/l/"); StringBuilder sb = new StringBuilder(); context.setSerializeLastMod(false); try { Aura.getSerializationService().write(context, null, AuraContext.class, sb, "HTML"); } catch (IOException e) { throw new AuraRuntimeException(e); } context.setSerializeLastMod(serLastMod); String contextJson = AuraTextUtil.urlencode(sb.toString()); defs.append(contextJson); defs.append("/app.manifest"); ret = defs.toString(); return ret; }
/** Is AppCache allowed by the current configuration? */ public static boolean isManifestEnabled() { if (!Aura.getConfigAdapter().isClientAppcacheEnabled()) { return false; } AuraContext context = Aura.getContextService().getCurrentContext(); DefDescriptor<? extends BaseComponentDef> appDefDesc = context.getApplicationDescriptor(); if (appDefDesc != null && appDefDesc.getDefType().equals(DefType.APPLICATION)) { try { Boolean useAppcache = ((ApplicationDef) appDefDesc.getDef()).isAppcacheEnabled(); if (useAppcache != null) { return useAppcache.booleanValue(); } return false; } catch (QuickFixException e) { return false; } } return false; }
public void testContextPath() throws Exception { AuraContext context = Aura.getContextService().getCurrentContext(); DefDescriptor<ApplicationDef> appDesc = Aura.getDefinitionService() .getDefDescriptor("clientLibraryTest:clientLibraryTest", ApplicationDef.class); context.setApplicationDescriptor(appDesc); String coolContext = "/cool"; context.setContextPath(coolContext); Aura.getDefinitionService().updateLoaded(appDesc); Set<String> urlSet = clientLibraryService.getUrls(context, Type.JS); Pattern pattern = Pattern.compile("/auraFW|/l/"); for (String url : urlSet) { Matcher matcher = pattern.matcher(url); while (matcher.find()) { int start = matcher.start(); String cool = url.substring(start - 5, start); if (!cool.equals(coolContext)) { fail("Context path was not prepended to Aura urls"); } } } }
/** * Gets the UID for the application descriptor of the current context, or {@code null} if there is * no application (probably because of a compile error). */ public String getContextAppUid(AuraContext context) { DefDescriptor<? extends BaseComponentDef> app = context.getApplicationDescriptor(); if (app != null) { try { return definitionService.getDefRegistry().getUid(null, app); } catch (QuickFixException e) { // This is perfectly possible, but the error is handled in more // contextually-sensible places. For here, we know there's no // meaningful uid, so we fall through and return null. } } return null; }
@Override public String getCode(List<Plugin> plugins) { List<Plugin> augmented = Lists.newArrayList(plugins); try { AuraContext ctx = Aura.getContextService().getCurrentContext(); DefDescriptor<? extends BaseComponentDef> top = ctx.getLoadingApplicationDescriptor(); if (top != null && top.getDefType() == DefType.APPLICATION) { DefDescriptor<FlavorAssortmentDef> flavors = ((ApplicationDef) top.getDef()).getAppFlavors(); if (flavors != null) { FlavorMapping mapping = flavors.getDef().computeOverrides(); if (!mapping.isEmpty()) { boolean devMode = Aura.getContextService().getCurrentContext().isDevMode(); augmented.add(new FlavorMappingEnforcerPlugin(getDescriptor(), mapping, devMode)); } } } } catch (QuickFixException e) { throw new AuraRuntimeException(e); } return super.getCode(augmented); }
/** * Get the expected name for the manifest cookie. * * @return the name (null if none) */ private static String getManifestCookieName() { AuraContext context = Aura.getContextService().getCurrentContext(); if (context.getApplicationDescriptor() != null) { StringBuilder sb = new StringBuilder(); if (context.getMode() != Mode.PROD) { sb.append(context.getMode()); sb.append("_"); } sb.append(context.getApplicationDescriptor().getNamespace()); sb.append("_"); sb.append(context.getApplicationDescriptor().getName()); sb.append(MANIFEST_COOKIE_TAIL); return sb.toString(); } return null; }
private void loadTestMocks(AuraContext context, boolean doReset, Collection<Definition> mocks) { // TODO: fix error handling if (mocks == null || mocks.isEmpty()) { return; } boolean error = false; MasterDefRegistry registry = context.getDefRegistry(); for (Definition def : mocks) { try { if (doReset && def instanceof Resettable) { ((Resettable) def).reset(); } registry.addLocalDef(def); } catch (Throwable t) { LOG.error("Failed to add mock " + def, t); error = true; } } if (error) { Aura.get(TestContextAdapter.class).release(); } }
/** * Fill a compiling def for a descriptor. * * <p>This makes sure that we can get a registry for a given def, then tries to get the def from * the global cache, if that fails, it retrieves from the registry, and marks the def as locally * built. * * @param compiling the current compiling def (if there is one). * @throws QuickFixException if validateDefinition caused a quickfix. */ private <D extends Definition> boolean fillCompilingDef( CompilingDef<D> compiling, AuraContext context) throws QuickFixException { assert compiling.def == null; { // // First, check our local cached defs to see if we have a fully compiled version. // in this case, we don't care about caching, since we are done. // @SuppressWarnings("unchecked") D localDef = (D) defs.get(compiling.descriptor); if (localDef != null) { compiling.def = localDef; compiling.built = !localDef.isValid(); if (compiling.built) { localDef.validateDefinition(); } return true; } } // // If there is no local cache, we must first check to see if there is a registry, as we may not // have // a registry (depending on configuration). In the case that we don't find one, we are done // here. // DefRegistry<D> registry = getRegistryFor(compiling.descriptor); if (registry == null) { defs.put(compiling.descriptor, null); return false; } // // Now, check if we can cache the def later, as we won't have the registry to check at a later // time. // If we can cache, look it up in the cache. If we find it, we have a built definition. // if (isCacheable(registry)) { compiling.cacheable = true; @SuppressWarnings("unchecked") Optional<D> opt = (Optional<D>) defsCache.getIfPresent(compiling.descriptor); if (opt != null) { D cachedDef = opt.orNull(); if (cachedDef != null) { @SuppressWarnings("unchecked") DefDescriptor<D> canonical = (DefDescriptor<D>) cachedDef.getDescriptor(); compiling.def = cachedDef; compiling.descriptor = canonical; compiling.built = false; defs.put(canonical, cachedDef); return true; } else { return false; } } } // // The last case. This is our first compile or the def is uncacheable. // In this case, we make sure that the initial validation is called, and put // the def in the non-validated set. // compiling.def = registry.getDef(compiling.descriptor); if (compiling.def == null) { return false; } @SuppressWarnings("unchecked") DefDescriptor<D> canonical = (DefDescriptor<D>) compiling.def.getDescriptor(); compiling.descriptor = canonical; // cc.loggingService.incrementNum(LoggingService.DEF_COUNT); // FIXME: setting the current namespace on the context seems // extremely hackish context.setCurrentNamespace(canonical.getNamespace()); compiling.def.validateDefinition(); compiling.built = true; defs.put(canonical, compiling.def); return true; }
@Override public void assertAccess(DefDescriptor<?> desc) throws QuickFixException { if (!accessCache.contains(desc)) { Aura.getLoggingService().incrementNum("SecurityProviderCheck"); DefType defType = desc.getDefType(); String ns = desc.getNamespace(); AuraContext context = Aura.getContextService().getCurrentContext(); Mode mode = context.getMode(); String prefix = desc.getPrefix(); // // This breaks encapsulation! -gordon // boolean isTopLevel = desc.equals(context.getApplicationDescriptor()); if (isTopLevel) { // // If we are trying to access the top level component, we need to ensure // that it is _not_ abstract. // BaseComponentDef def = getDef(context.getApplicationDescriptor()); if (def != null && def.isAbstract() && def.getProviderDescriptor() == null) { throw new NoAccessException( String.format("Access to %s disallowed. Abstract definition.", desc)); } } // // If this is _not_ the top level, we allow circumventing the security provider. // This means that certain things will short-circuit, hopefully making checks faster... // Not sure if this is premature optimization or not. // if (!isTopLevel || desc.getDefType().equals(DefType.COMPONENT)) { if (!securedDefTypes.contains(defType) || unsecuredPrefixes.contains(prefix) || unsecuredNamespaces.contains(ns) || (mode != Mode.PROD && (!Aura.getConfigAdapter().isProduction()) && unsecuredNonProductionNamespaces.contains(ns))) { accessCache.add(desc); return; } if (ns != null && DefDescriptor.JAVA_PREFIX.equals(prefix)) { // handle java packages that have namespaces like aura.impl.blah for (String okNs : unsecuredNamespaces) { if (ns.startsWith(okNs)) { accessCache.add(desc); return; } } } } SecurityProviderDef securityProviderDef = getSecurityProvider(); if (securityProviderDef == null) { if (mode != Mode.PROD && !Aura.getConfigAdapter().isProduction()) { accessCache.add(desc); return; } else { throw new NoAccessException( String.format("Access to %s disallowed. No Security Provider found.", desc)); } } else { if (!securityProviderDef.isAllowed(desc)) { throw new NoAccessException( String.format( "Access to %s disallowed by %s", desc, securityProviderDef.getDescriptor().getName())); } } accessCache.add(desc); } }
/** * Write out the manifest. * * <p>This writes out the full manifest for an application so that we can use the AppCache. * * <p>The manifest contains CSS and JavaScript URLs. These specified resources are copied into the * AppCache with the HTML template. When the page is reloaded, the existing manifest is compared * to the new manifest. If they are identical, the resources are served from the AppCache. * Otherwise, the resources are requested from the server and the AppCache is updated. * * @param request the request * @param response the response * @param context the context * @throws IOException if unable to write out the response */ @Override public void write(HttpServletRequest request, HttpServletResponse response, AuraContext context) throws IOException { servletUtilAdapter.setNoCache(response); try { Map<String, Object> attributes = getComponentAttributes(request); // // First, we make sure that the manifest is enabled. // if (!manifestUtil.isManifestEnabled(request)) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } // // Now we validate the cookie, which includes loop detection. // this routine sets the response code. // if (!manifestUtil.checkManifestCookie(request, response)) { return; } boolean appOk = false; DefDescriptor<? extends BaseComponentDef> descr = null; try { descr = context.getApplicationDescriptor(); if (descr != null) { definitionService.updateLoaded(descr); appOk = true; } } catch (QuickFixException qfe) { // // ignore qfe, since we really don't care... the manifest will be 404ed. // This will eventually cause the browser to give up. Note that this case // should almost never occur, as it requires the qfe to be introduced between // the initial request (which will not set a manifest if it gets a qfe) and // the manifest request. // } catch (ClientOutOfSyncException coose) { // // In this case, we want to force a reload... A 404 on the manifest is // supposed to handle this. we hope that the client will do the right // thing, and reload everything. Note that this case really should only // happen if the client already has content, and thus should be refreshing // However, there are very odd edge cases that we probably can't detect // without keeping server side state, such as the case that something // is updated between the initial HTML request and the manifest request. // Not sure what browsers will do in this case. // } if (!appOk) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } // // This writes both the app and framework signatures into // the manifest, so that if either one changes, the // manifest will change. Note that in most cases, we will // write these signatures in multiple places, but we just // need to make sure that they are in at least one place. // Map<String, Object> attribs = Maps.newHashMap(); String appUid = getContextAppUid(context); String nonce = configAdapter.getAuraFrameworkNonce(); // Since we don't get the UID from our URL, we set it here. context.setFrameworkUID(nonce); attribs.put(LAST_MOD, String.format("app=%s, FW=%s", appUid, nonce)); attribs.put(UID, appUid); StringWriter sw = new StringWriter(); String resetCssUrl = configAdapter.getResetCssURL(); if (resetCssUrl != null) { sw.write(resetCssUrl); sw.write('\n'); } for (String s : servletUtilAdapter.getStyles(context)) { sw.write(s); sw.write('\n'); } for (String s : servletUtilAdapter.getScripts(context, true, true, attributes)) { sw.write(s); sw.write('\n'); } // Add locker service safe eval worker url String lockerWorkerURL = configAdapter.getLockerWorkerURL(); if (lockerWorkerURL != null) { sw.write(lockerWorkerURL); sw.write('\n'); } // Add in any application specific resources if (descr != null && descr.getDefType().equals(DefType.APPLICATION)) { ApplicationDef def = (ApplicationDef) descr.getDef(); for (String s : def.getAdditionalAppCacheURLs()) { if (s != null) { sw.write(s); sw.write('\n'); } } } attribs.put(RESOURCE_URLS, sw.toString()); DefDescriptor<ComponentDef> tmplDesc = definitionService.getDefDescriptor("ui:manifest", ComponentDef.class); Component tmpl = instanceService.getInstance(tmplDesc, attribs); renderingService.render(tmpl, response.getWriter()); } catch (Exception e) { Aura.getExceptionAdapter().handleException(e); // Can't throw exception here: to set manifest OBSOLETE response.setStatus(HttpServletResponse.SC_NOT_FOUND); } }
@Override public void serialize(Json json, AuraContext ctx) throws IOException { json.writeMapBegin(); json.writeMapEntry("mode", ctx.getMode()); DefDescriptor<? extends BaseComponentDef> appDesc = ctx.getApplicationDescriptor(); if (appDesc != null) { if (appDesc.getDefType().equals(DefType.APPLICATION)) { json.writeMapEntry( "app", String.format("%s:%s", appDesc.getNamespace(), appDesc.getName())); } else { json.writeMapEntry( "cmp", String.format("%s:%s", appDesc.getNamespace(), appDesc.getName())); } } if (ctx.getSerializeThemes()) { ThemeList themes = ctx.getThemeList(); if (!themes.isEmpty()) { List<String> stringed = Lists.newArrayList(); for (DefDescriptor<ThemeDef> theme : themes) { stringed.add(theme.getQualifiedName()); } json.writeMapEntry("themes", stringed); } Optional<String> dynamicVarsUid = themes.getActiveDynamicVarsUid(); if (dynamicVarsUid.isPresent()) { json.writeMapEntry("dynamicVarsUid", dynamicVarsUid.get()); } } if (ctx.getRequestedLocales() != null) { List<String> locales = new ArrayList<>(); for (Locale locale : ctx.getRequestedLocales()) { locales.add(locale.toString()); } json.writeMapEntry("requestedLocales", locales); } Map<String, String> loadedStrings = Maps.newHashMap(); Map<DefDescriptor<?>, String> clientLoaded = Maps.newHashMap(); clientLoaded.putAll(ctx.getClientLoaded()); for (Map.Entry<DefDescriptor<?>, String> entry : ctx.getLoaded().entrySet()) { loadedStrings.put( String.format( "%s@%s", entry.getKey().getDefType().toString(), entry.getKey().getQualifiedName()), entry.getValue()); clientLoaded.remove(entry.getKey()); } if (forClient) { for (DefDescriptor<?> deleted : clientLoaded.keySet()) { loadedStrings.put( String.format("%s@%s", deleted.getDefType().toString(), deleted.getQualifiedName()), DELETED); } } if (loadedStrings.size() > 0) { json.writeMapKey("loaded"); json.writeMap(loadedStrings); } if (ctx.getSerializeLastMod()) { json.writeMapEntry("lastmod", Long.toString(AuraBaseServlet.getLastMod())); } TestContextAdapter testContextAdapter = Aura.get(TestContextAdapter.class); if (testContextAdapter != null) { TestContext testContext = testContextAdapter.getTestContext(); if (testContext != null) { json.writeMapEntry("test", testContext.getName()); } } if (ctx.getFrameworkUID() != null) { json.writeMapEntry("fwuid", ctx.getFrameworkUID()); } if (forClient) { // client needs value providers, urls don't boolean started = false; for (GlobalValueProvider valueProvider : ctx.getGlobalProviders().values()) { if (!valueProvider.isEmpty()) { if (!started) { json.writeMapKey("globalValueProviders"); json.writeArrayBegin(); started = true; } json.writeComma(); json.writeIndent(); json.writeMapBegin(); json.writeMapEntry("type", valueProvider.getValueProviderKey().getPrefix()); json.writeMapEntry("values", valueProvider.getData()); json.writeMapEnd(); } } if (started) { json.writeArrayEnd(); } // // Now comes the tricky part, we have to serialize all of the definitions that are // required on the client side, and, of all types. This way, we won't have to handle // ugly cases of actual definitions nested inside our configs, and, we ensure that // all dependencies actually get sent to the client. Note that the 'loaded' set needs // to be updated as well, but that needs to happen prior to this. // Map<DefDescriptor<? extends Definition>, Definition> defMap; defMap = ctx.getDefRegistry().filterRegistry(ctx.getPreloadedDefinitions()); if (defMap.size() > 0) { List<Definition> componentDefs = Lists.newArrayList(); List<Definition> eventDefs = Lists.newArrayList(); List<Definition> libraryDefs = Lists.newArrayList(); for (Map.Entry<DefDescriptor<? extends Definition>, Definition> entry : defMap.entrySet()) { DefDescriptor<? extends Definition> desc = entry.getKey(); DefType dt = desc.getDefType(); Definition d = entry.getValue(); // // Ignore defs that ended up not being valid. This is arguably something // that the MDR should have done when filtering. // if (d != null) { if (DefType.COMPONENT.equals(dt) || DefType.APPLICATION.equals(dt)) { componentDefs.add(d); } else if (DefType.EVENT.equals(dt)) { eventDefs.add(d); } else if (DefType.LIBRARY.equals(dt)) { libraryDefs.add(d); } } } writeDefs(json, "componentDefs", componentDefs); writeDefs(json, "eventDefs", eventDefs); writeDefs(json, "libraryDefs", libraryDefs); } ctx.serializeAsPart(json); } json.writeMapEnd(); }
/** * Handle an exception in the servlet. * * <p>This routine should be called whenever an exception has surfaced to the top level of the * servlet. It should not be overridden unless Aura is entirely subsumed. Most special cases can * be handled by the Aura user by implementing {@link ExceptionAdapter ExceptionAdapter}. * * @param t the throwable to write out. * @param quickfix is this exception a valid quick-fix * @param context the aura context. * @param request the request. * @param response the response. * @param written true if we have started writing to the output stream. * @throws IOException if the output stream does. * @throws ServletException if send404 does (should not generally happen). */ @Override public void handleServletException( Throwable t, boolean quickfix, AuraContext context, HttpServletRequest request, HttpServletResponse response, boolean written) throws IOException { try { Throwable mappedEx = t; boolean map = !quickfix; Format format = context.getFormat(); // // This seems to fail, though the documentation implies that you can do // it. // // if (written && !response.isCommitted()) { // response.resetBuffer(); // written = false; // } if (!written) { // Should we only delete for JSON? setNoCache(response); } if (mappedEx instanceof IOException) { // // Just re-throw IOExceptions. // throw (IOException) mappedEx; } else if (mappedEx instanceof NoAccessException) { Throwable cause = mappedEx.getCause(); String denyMessage = mappedEx.getMessage(); map = false; if (cause != null) { // // Note that the exception handler can remap the cause here. // cause = exceptionAdapter.handleException(cause); denyMessage += ": cause = " + cause.getMessage(); } // // Is this correct?!?!?! // if (format != Format.JSON) { this.send404(request.getServletContext(), request, response); if (!isProductionMode(context.getMode())) { // Preserve new lines and tabs in the stacktrace since this is directly being written on // to the // page denyMessage = "<pre>" + AuraTextUtil.escapeForHTML(denyMessage) + "</pre>"; response.getWriter().println(denyMessage); } return; } } else if (mappedEx instanceof QuickFixException) { if (isProductionMode(context.getMode())) { // // In production environments, we want wrap the quick-fix. But be a little careful here. // We should never mark the top level as a quick-fix, because that means that we gack // on every mis-spelled app. In this case we simply send a 404 and bolt. // if (mappedEx instanceof DefinitionNotFoundException) { DefinitionNotFoundException dnfe = (DefinitionNotFoundException) mappedEx; if (dnfe.getDescriptor() != null && dnfe.getDescriptor().equals(context.getApplicationDescriptor())) { // We're in production and tried to hit an aura app that doesn't exist. // just show the standard 404 page. this.send404(request.getServletContext(), request, response); return; } } map = true; mappedEx = new AuraUnhandledException("404 Not Found (Application Error)", mappedEx); } } if (map) { mappedEx = exceptionAdapter.handleException(mappedEx); } PrintWriter out = response.getWriter(); // // If we have written out data, We are kinda toast in this case. // We really want to roll it all back, but we can't, so we opt // for the best we can do. For HTML we can do nothing at all. // if (format == Format.JSON) { if (!written) { out.write(CSRF_PROTECT); } // // If an exception happened while we were emitting JSON, we want the // client to ignore the now-corrupt data structure. 404s and 500s // cause the client to prepend /*, so we can effectively erase the // bad data by appending a */ here and then serializing the exception // info. // out.write("*/"); // // Unfortunately we can't do the following now. It might be possible // in some cases, but we don't want to go there unless we have to. // } if (format == Format.JS || format == Format.CSS) { // Make sure js and css doesn't get cached in browser, appcache, etc response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); } if (format == Format.JSON || format == Format.HTML || format == Format.JS || format == Format.CSS) { // // We only write out exceptions for HTML or JSON. // Seems bogus, but here it is. // // Start out by cleaning out some settings to ensure we don't // check too many things, leading to a circular failure. Note // that this is still a bit dangerous, as we seem to have a lot // of magic in the serializer. // // Clear the InstanceStack before trying to serialize the exception since the Throwable has // likely // rendered the stack inaccurate, and may falsely trigger NoAccessExceptions. InstanceStack stack = this.contextService.getCurrentContext().getInstanceStack(); List<String> list = stack.getStackInfo(); for (int count = list.size(); count > 0; count--) { stack.popInstance(stack.peek()); } serializationService.write(mappedEx, null, out); if (format == Format.JSON) { out.write("/*ERROR*/"); } } } catch (IOException ioe) { throw ioe; } catch (Throwable death) { // // Catch any other exception and log it. This is actually kinda bad, because something has // gone horribly wrong. We should write out some sort of generic page other than a 404, // but at this point, it is unclear what we can do, as stuff is breaking right and left. // try { response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); exceptionAdapter.handleException(death); if (!isProductionMode(context.getMode())) { response.getWriter().println(death.getMessage()); } } catch (IOException ioe) { throw ioe; } catch (Throwable doubleDeath) { // we are totally hosed. if (!isProductionMode(context.getMode())) { response.getWriter().println(doubleDeath.getMessage()); } } } finally { this.contextService.endContext(); } }
@Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException { if (!Aura.getConfigAdapter().isTestAllowed()) { chain.doFilter(req, res); return; } TestContextAdapter testContextAdapter = Aura.get(TestContextAdapter.class); if (testContextAdapter == null) { chain.doFilter(req, res); return; } // Check for requests to execute a JSTest, i.e. initial component GETs with particular // parameters. HttpServletRequest request = (HttpServletRequest) req; if ("GET".equals(request.getMethod())) { String contextPath = request.getContextPath(); String uri = request.getRequestURI(); String browserType = request.getParameter("aura.browserType"); if (browserType == null) { // read it from request header String ua = request.getHeader(HttpHeaders.USER_AGENT); if (ua != null) { ua = ua.toLowerCase(); if (ua.contains("chrome")) { browserType = "GOOGLECHROME"; } else if (ua.contains("safari")) { browserType = "SAFARI"; } else if (ua.contains("firefox")) { browserType = "FIREFOX"; } else if (ua.contains("ipad")) { browserType = "IPAD"; } else if (ua.contains("iphone")) { browserType = "IPHONE"; } else if (ua.contains("msie 10")) { browserType = "IE10"; } else if (ua.contains("msie 9")) { browserType = "IE9"; } else if (ua.contains("msie 8")) { browserType = "IE8"; } else if (ua.contains("msie 7")) { browserType = "IE7"; } else if (ua.contains("msie 6")) { browserType = "IE6"; } else if (ua.contains("trident/7.0")) { browserType = "IE11"; } else if (ua.contains("edge/12")) { browserType = "IE12"; } else { browserType = "OTHER"; } } } String path; if (uri.startsWith(contextPath)) { path = uri.substring(contextPath.length()); } else { path = uri; } Matcher matcher = AuraRewriteFilter.DESCRIPTOR_PATTERN.matcher(path); if (matcher.matches()) { // Extract the target component since AuraContext usually does not have the app descriptor // set yet. DefType type = "app".equals(matcher.group(3)) ? DefType.APPLICATION : DefType.COMPONENT; String namespace = matcher.group(1); String name = matcher.group(2); DefDescriptor<?> targetDescriptor = Aura.getDefinitionService() .getDefDescriptor( String.format("%s:%s", namespace, name), type.getPrimaryInterface()); // Check if a single jstest is being requested. String testToRun = jstestToRun.get(request); if (testToRun != null && !testToRun.isEmpty()) { AuraContext context = Aura.getContextService().getCurrentContext(); Format format = context.getFormat(); switch (format) { case HTML: TestCaseDef testDef; TestContext testContext; String targetUri; try { TestSuiteDef suiteDef = getTestSuite(targetDescriptor); testDef = getTestCase(suiteDef, testToRun); testDef.validateDefinition(); testDef.setCurrentBrowser(browserType); testContextAdapter.getTestContext(testDef.getQualifiedName()); testContextAdapter.release(); testContext = testContextAdapter.getTestContext(testDef.getQualifiedName()); targetUri = buildJsTestTargetUri(targetDescriptor, testDef); } catch (QuickFixException e) { ((HttpServletResponse) res).setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR); res.setCharacterEncoding(AuraBaseServlet.UTF_ENCODING); res.getWriter().append(e.getMessage()); Aura.getExceptionAdapter().handleException(e); return; } // Load any test mocks. Collection<Definition> mocks = testDef.getLocalDefs(); testContext.getLocalDefs().addAll(mocks); loadTestMocks(context, true, testContext.getLocalDefs()); // Capture the response and inject tags to load jstest. String capturedResponse = captureResponse(req, res, targetUri); if (capturedResponse != null) { res.setCharacterEncoding(AuraBaseServlet.UTF_ENCODING); if (!Aura.getContextService().isEstablished()) { // There was an error in the original response, so just write the response out. res.getWriter().write(capturedResponse); } else { String testTag = buildJsTestScriptTag(targetDescriptor, testToRun, capturedResponse); injectScriptTags(res.getWriter(), capturedResponse, testTag); } return; } case JS: res.setCharacterEncoding(AuraBaseServlet.UTF_ENCODING); writeJsTestScript(res.getWriter(), targetDescriptor, testToRun); return; default: // Pass it on. } } // aurajstest:jstest app is invokable in the following ways: // ?aura.mode=JSTEST - run all tests // ?aura.mode JSTEST&test=XXX - run single test // ?aura.jstest - run all tests // ?aura.jstest=XXX - run single test // ?aura.jstestrun - run all tests // TODO: delete JSTEST mode String jstestAppRequest = jstestAppFlag.get(request); Mode mode = AuraContextFilter.mode.get(request, Mode.PROD); if (mode == Mode.JSTEST || mode == Mode.JSTESTDEBUG || jstestAppRequest != null || testToRun != null) { mode = mode.toString().endsWith("DEBUG") ? Mode.AUTOJSTESTDEBUG : Mode.AUTOJSTEST; String qs = String.format("descriptor=%s:%s&defType=%s", namespace, name, type.name()); String testName = null; if (jstestAppRequest != null && !jstestAppRequest.isEmpty()) { testName = jstestAppRequest; } else if (testToRun != null && !testToRun.isEmpty()) { testName = testToRun; } if (testName != null) { qs = qs + "&test=" + testName; } String newUri = createURI( "aurajstest", "jstest", DefType.APPLICATION, mode, Authentication.AUTHENTICATED.name(), qs); RequestDispatcher dispatcher = servletContext.getContext(newUri).getRequestDispatcher(newUri); if (dispatcher != null) { dispatcher.forward(req, res); return; } } } } // Handle mock definitions specified in the tests. TestContext testContext = getTestContext(request); if (testContext == null) { // During manual testing, the test context adapter may not always get cleared. testContextAdapter.clear(); } else { ContextService contextService = Aura.getContextService(); if (!contextService.isEstablished()) { LOG.error("Aura context is not established! New context will NOT be created."); chain.doFilter(req, res); return; } AuraContext context = contextService.getCurrentContext(); // Reset mocks if requested, or for the initial GET. boolean doResetMocks = testReset.get(request, Format.HTML.equals(context.getFormat())); loadTestMocks(context, doResetMocks, testContext.getLocalDefs()); } chain.doFilter(req, res); }
public ComponentDefModel() throws QuickFixException { AuraContext context = Aura.getContextService().getCurrentContext(); BaseComponent<?, ?> component = context.getCurrentComponent(); String desc = (String) component.getAttributes().getValue("descriptor"); DefType defType = DefType.valueOf(((String) component.getAttributes().getValue("defType")).toUpperCase()); DefinitionService definitionService = Aura.getDefinitionService(); descriptor = definitionService.getDefDescriptor(desc, defType.getPrimaryInterface()); definition = descriptor.getDef(); ReferenceTreeModel.assertAccess(definition); String type = null; if (definition instanceof RootDefinition) { RootDefinition rootDef = (RootDefinition) definition; for (AttributeDef attribute : rootDef.getAttributeDefs().values()) { if (ReferenceTreeModel.hasAccess(attribute)) { attributes.add(new AttributeModel(attribute)); } } DocumentationDef docDef = rootDef.getDocumentationDef(); doc = docDef != null ? new DocumentationDefModel(docDef) : null; if (definition instanceof BaseComponentDef) { BaseComponentDef cmpDef = (BaseComponentDef) definition; for (RegisterEventDef reg : cmpDef.getRegisterEventDefs().values()) { if (ReferenceTreeModel.hasAccess(reg)) { events.add(new AttributeModel(reg)); } } for (EventHandlerDef handler : cmpDef.getHandlerDefs()) { handledEvents.add(new AttributeModel(handler)); } for (DefDescriptor<InterfaceDef> intf : cmpDef.getInterfaces()) { if (ReferenceTreeModel.hasAccess(intf.getDef())) { interfaces.add(intf.getNamespace() + ":" + intf.getName()); } } DefDescriptor<?> superDesc = cmpDef.getExtendsDescriptor(); if (superDesc != null) { theSuper = superDesc.getNamespace() + ":" + superDesc.getName(); } else { theSuper = null; } isAbstract = cmpDef.isAbstract(); isExtensible = cmpDef.isExtensible(); } else if (definition instanceof EventDef) { EventDef eventDef = (EventDef) definition; DefDescriptor<?> superDesc = eventDef.getExtendsDescriptor(); if (superDesc != null) { theSuper = superDesc.getNamespace() + ":" + superDesc.getName(); } else { theSuper = null; } type = eventDef.getEventType().name(); isExtensible = true; isAbstract = false; } else { theSuper = null; isExtensible = true; isAbstract = false; } support = rootDef.getSupport().name(); if (definition instanceof RootDefinition) { List<DefDescriptor<?>> deps = ((RootDefinition) definition).getBundle(); for (DefDescriptor<?> dep : deps) { // we already surface the documentation--users don't need to see the source for it. if (dep.getDefType() != DefType.DOCUMENTATION) { Definition def = dep.getDef(); if (ReferenceTreeModel.hasAccess(def)) { defs.add(new DefModel(dep)); } } } } } else { support = null; theSuper = null; isExtensible = false; isAbstract = false; doc = null; } this.type = type; }
@Override public void write(T value, Map<String, Object> componentAttributes, Appendable out) throws IOException { try { AuraContext context = Aura.getContextService().getCurrentContext(); InstanceService instanceService = Aura.getInstanceService(); RenderingService renderingService = Aura.getRenderingService(); BaseComponentDef def = value.getDescriptor().getDef(); ComponentDef templateDef = def.getTemplateDef(); Map<String, Object> attributes = Maps.newHashMap(); StringBuilder sb = new StringBuilder(); writeHtmlStyles(new ArrayList<>(Arrays.asList(Aura.getConfigAdapter().getResetCssURL())), sb); attributes.put("auraResetCss", sb.toString()); sb.setLength(0); writeHtmlStyles(AuraServlet.getStyles(), sb); attributes.put("auraStyleTags", sb.toString()); sb.setLength(0); writeHtmlScripts(AuraServlet.getScripts(), sb); DefDescriptor<StyleDef> styleDefDesc = templateDef.getStyleDescriptor(); if (styleDefDesc != null) { attributes.put("auraInlineStyle", styleDefDesc.getDef().getCode()); } String contextPath = context.getContextPath(); Mode mode = context.getMode(); if (mode.allowLocalRendering() && def.isLocallyRenderable()) { BaseComponent<?, ?> cmp = (BaseComponent<?, ?>) instanceService.getInstance(def, componentAttributes); attributes.put("body", Lists.<BaseComponent<?, ?>>newArrayList(cmp)); attributes.put("bodyClass", ""); attributes.put("autoInitialize", "false"); Component template = instanceService.getInstance(templateDef.getDescriptor(), attributes); renderingService.render(template, out); } else { attributes.put("auraScriptTags", sb.toString()); Map<String, Object> auraInit = Maps.newHashMap(); if (componentAttributes != null && !componentAttributes.isEmpty()) { auraInit.put("attributes", componentAttributes); } auraInit.put("descriptor", def.getDescriptor()); auraInit.put("deftype", def.getDescriptor().getDefType()); auraInit.put("host", contextPath); attributes.put("autoInitialize", "false"); attributes.put("autoInitializeSync", "true"); auraInit.put("instance", value); auraInit.put("token", AuraBaseServlet.getToken()); StringBuilder contextWriter = new StringBuilder(); Aura.getSerializationService() .write(context, null, AuraContext.class, contextWriter, "JSON"); auraInit.put("context", new Literal(contextWriter.toString())); attributes.put("auraInitSync", JsonEncoder.serialize(auraInit)); Component template = instanceService.getInstance(templateDef.getDescriptor(), attributes); renderingService.render(template, out); } } catch (QuickFixException e) { throw new AuraRuntimeException(e); } }