void doPost(final HttpServletRequest req, final HttpServletResponse resp) { try { // Parse out the required API versions. final AcceptAPIVersion acceptVersion = parseAcceptAPIVersion(req); // Prepare response. prepareResponse(req, resp); // Validate request. preprocessRequest(req); rejectIfNoneMatch(req); rejectIfMatch(req); final Map<String, String[]> parameters = req.getParameterMap(); final String action = asSingleValue(PARAM_ACTION, getParameter(req, PARAM_ACTION)); if (action.equalsIgnoreCase(ACTION_ID_CREATE)) { final JsonValue content = getJsonContent(req); final CreateRequest request = Requests.newCreateRequest(getResourceName(req), content); for (final Map.Entry<String, String[]> p : parameters.entrySet()) { final String name = p.getKey(); final String[] values = p.getValue(); if (parseCommonParameter(name, values, request)) { continue; } else if (name.equalsIgnoreCase(PARAM_ACTION)) { // Ignore - already handled. } else if (HttpUtils.isMultiPartRequest(req.getContentType())) { // Ignore - multipart content adds form parts to the parameter set } else { request.setAdditionalParameter(name, asSingleValue(name, values)); } } doRequest(req, resp, acceptVersion, request); } else { // Action request. final JsonValue content = getJsonActionContent(req); final ActionRequest request = Requests.newActionRequest(getResourceName(req), action).setContent(content); for (final Map.Entry<String, String[]> p : parameters.entrySet()) { final String name = p.getKey(); final String[] values = p.getValue(); if (parseCommonParameter(name, values, request)) { continue; } else if (name.equalsIgnoreCase(PARAM_ACTION)) { // Ignore - already handled. } else if (HttpUtils.isMultiPartRequest(req.getContentType())) { // Ignore - multipart content adds form parts to the parameter set } else { request.setAdditionalParameter(name, asSingleValue(name, values)); } } doRequest(req, resp, acceptVersion, request); } } catch (final Exception e) { fail(req, resp, e); } }
void doDelete(final HttpServletRequest req, final HttpServletResponse resp) { try { // Parse out the required API versions. final AcceptAPIVersion acceptVersion = parseAcceptAPIVersion(req); // Prepare response. prepareResponse(req, resp); // Validate request. preprocessRequest(req); rejectIfNoneMatch(req); final Map<String, String[]> parameters = req.getParameterMap(); final DeleteRequest request = Requests.newDeleteRequest(getResourceName(req)).setRevision(getIfMatch(req)); for (final Map.Entry<String, String[]> p : parameters.entrySet()) { final String name = p.getKey(); final String[] values = p.getValue(); if (parseCommonParameter(name, values, request)) { continue; } else { request.setAdditionalParameter(name, asSingleValue(name, values)); } } doRequest(req, resp, acceptVersion, request); } catch (final Exception e) { fail(req, resp, e); } }
/** * Returns <code>true</code> if a persisted <code>Dictionary</code> exists for the given <code>pid * </code>. * * @param pid The identifier for the dictionary to test. */ public boolean exists(String pid) { logger.debug("Config exists call for {}", pid); boolean exists = false; if (isReady(0) && requireRepository) { String id = pidToId(pid); try { ReadRequest readRequest = Requests.newReadRequest(id); Resource existing = repo.read(readRequest); exists = (existing != null); } catch (NotFoundException ex) { exists = false; } catch (ResourceException ex) { throw new RuntimeException( "Failed to check if configuration exists in repository: " + ex.getMessage(), ex); } } if (!exists) { exists = tempStore.containsKey(pid); if (exists) { logger.debug("Entry exists in temporary store for '{}'", pid); } } else { logger.debug("Entry exists for '{}'", pid); } if (!exists) { logger.debug("Entry does not exist for '{}'", pid); } return exists; }
/** * Returns the <code>Dictionary</code> for the given <code>pid</code>. * * @param pid The identifier for the dictionary to load. * @return The dictionary for the identifier. This must not be <code>null</code> but may be empty. * @throws IOException If an error occurrs loading the dictionary. An <code>IOException</code> * must also be thrown if no dictionary exists for the given identifier. */ public Dictionary load(String pid) throws IOException { logger.debug("Config load call for {}", pid); Dictionary result = null; try { if (isReady(0) && requireRepository) { String id = pidToId(pid); ReadRequest readRequest = Requests.newReadRequest(id); Resource existing = repo.read(readRequest); Map<String, Object> existingConfig = existing.getContent().asMap(); Object configMap = existingConfig.get(JSONEnhancedConfig.JSON_CONFIG_PROPERTY); String configString = serializeConfig(configMap); existingConfig.put(JSONEnhancedConfig.JSON_CONFIG_PROPERTY, configString); logger.debug("Config loaded {} {}", pid, existing); result = mapToDict(existingConfig); } else if (!requireRepository) { result = tempStore.get(pid); if (result == null) { throw new IOException("No entry for " + pid + " exists."); } logger.debug("Config loaded from temporary store {} {}", pid, result); } } catch (NotFoundException ex) { result = tempStore.get(pid); if (result == null) { throw new IOException("No entry for " + pid + " exists."); } logger.debug("Config loaded from temporary store {} {}", pid, result); } catch (ResourceException ex) { throw new IOException("Failed to load configuration in repository: " + ex.getMessage(), ex); } return result; }
/** * Creates the underlying backend policies. * * <p>NOTE: if the creation of the underlying policies fails, any successfully created underlying * policies will be attempted to be deleted but if the deletion fails, then the underlying * policies may be in an inconsistent state. * * @param context The request context. * @param policies The underlying policies to create. * @return A promise containing the list of created underlying policies or a {@code * ResourceException} if the creation fails. */ public Promise<List<Resource>, ResourceException> createPolicies( ServerContext context, Set<JsonValue> policies) { final List<String> policyIds = new ArrayList<String>(); List<Promise<Resource, ResourceException>> promises = new ArrayList<Promise<Resource, ResourceException>>(); for (JsonValue policy : policies) { promises.add( policyResource .handleCreate(context, Requests.newCreateRequest("", policy)) .thenOnResult( new ResultHandler<Resource>() { @Override public void handleResult(Resource result) { // Save ids of created policies, in case a latter policy fails to be created, // so we can roll back. policyIds.add(result.getId()); } })); } return Promises.when(promises) .thenAsync( new AsyncFunction<List<Resource>, List<Resource>, ResourceException>() { @Override public Promise<List<Resource>, ResourceException> apply(List<Resource> value) { return Promises.newResultPromise(value); } }, new UmaPolicyCreateFailureHandler(context, policyIds)); }
@Test public void crestActionBlowupIsAllowed() throws SSOException, DelegationException { // Given... final Set<String> actions = new HashSet<>(Arrays.asList("MODIFY")); final DelegationPermission permission = new DelegationPermission( "/abc", "rest", "1.0", "policies", "destroy", actions, EXTENSIONS, DUMB_FUNC); given(factory.newInstance("/abc", "rest", "1.0", "policies", "destroy", actions, EXTENSIONS)) .willReturn(permission); given(subjectContext.getCallerSSOToken()).willReturn(token); given(evaluator.isAllowed(eq(token), eq(permission), eq(ENVIRONMENT))).willReturn(true); JsonValue jsonValue = json(object(field("someKey", "someValue"))); Promise<ActionResponse, ResourceException> promise = Promises.newResultPromise(Responses.newActionResponse(jsonValue)); given(provider.actionCollection(isA(Context.class), isA(ActionRequest.class))) .willReturn(promise); // When... final FilterChain chain = AuthorizationFilters.createAuthorizationFilter(provider, module); final Router router = new Router(); router.addRoute(RoutingMode.STARTS_WITH, Router.uriTemplate("/policies"), chain); final RealmContext context = new RealmContext(subjectContext); context.setSubRealm("abc", "abc"); final ActionRequest request = Requests.newActionRequest("/policies", "blowup"); Promise<ActionResponse, ResourceException> result = router.handleAction(context, request); // Then... assertThat(result).succeeded().withContent().stringAt("someKey").isEqualTo("someValue"); }
/** * Queries relationships, returning the relationship associated with this providers resource path * and the specified relationship field. * * @param context The current context * @param managedObjectId The id of the managed object to find relationships associated with * @return */ private Promise<ResourceResponse, ResourceException> queryRelationship( final Context context, final String managedObjectId) { try { final QueryRequest queryRequest = Requests.newQueryRequest(REPO_RESOURCE_PATH); final List<ResourceResponse> relationships = new ArrayList<>(); queryRequest.setQueryFilter( QueryFilter.and( QueryFilter.equalTo( new JsonPointer(isReverse ? REPO_FIELD_SECOND_ID : REPO_FIELD_FIRST_ID), resourcePath.child(managedObjectId)), QueryFilter.equalTo(new JsonPointer(REPO_FIELD_FIRST_PROPERTY_NAME), propertyName))); connectionFactory.getConnection().query(context, queryRequest, relationships); if (relationships.isEmpty()) { return new NotFoundException().asPromise(); } else { // TODO OPENIDM-4094 - check size and throw illegal state if more than one? return newResultPromise(FORMAT_RESPONSE.apply(relationships.get(0))); } } catch (ResourceException e) { return e.asPromise(); } }
@Test public void crestRequestNotAllowed() throws SSOException, DelegationException { // Given... final Set<String> actions = new HashSet<>(Arrays.asList("MODIFY")); final DelegationPermission permission = new DelegationPermission( "/abc", "rest", "1.0", "policies", "modify", actions, EXTENSIONS, DUMB_FUNC); given(factory.newInstance("/abc", "rest", "1.0", "policies", "modify", actions, EXTENSIONS)) .willReturn(permission); given(subjectContext.getCallerSSOToken()).willReturn(token); given(evaluator.isAllowed(eq(token), eq(permission), eq(ENVIRONMENT))).willReturn(false); // When... final FilterChain chain = AuthorizationFilters.createAuthorizationFilter(provider, module); final Router router = new Router(); router.addRoute(RoutingMode.STARTS_WITH, Router.uriTemplate("/policies"), chain); final RealmContext context = new RealmContext(subjectContext); context.setSubRealm("abc", "abc"); final CreateRequest request = Requests.newCreateRequest("/policies", JsonValue.json(new Object())); Promise<ResourceResponse, ResourceException> promise = router.handleCreate(context, request); // Then... assertThat(promise).failedWithException().isInstanceOf(ForbiddenException.class); }
/** * Deletes the underlying backend policies. * * <p>NOTE: if the deletion of the underlying policies fails, the underlying policies may be in an * inconsistent state. * * @param context The request context. * @param policyIds The list of ids of the underlying backend policies to delete. * @return A promise containing the list of underlying policies that were deleted or a {@code * ResourceException} if the policies were failed to be deleted. */ public Promise<List<Resource>, ResourceException> deletePolicies( ServerContext context, Collection<String> policyIds) { List<Promise<Resource, ResourceException>> promises = new ArrayList<Promise<Resource, ResourceException>>(); for (String policyId : policyIds) { promises.add(policyResource.handleDelete(context, Requests.newDeleteRequest(policyId))); } return Promises.when(promises); }
/** * Persist the supplied {@link JsonValue} {@code value} as the new state of this singleton * relationship on {@code resourceId}. * * <p><em>This is currently the only means of creating an instance of this singleton</em> * * @param context The context of this request * @param resourceId Id of the resource relation fields in value are to be memebers of * @param value A {@link JsonValue} map of relationship fields and their values * @return The persisted instance of {@code value} */ @Override public Promise<JsonValue, ResourceException> setRelationshipValueForResource( Context context, String resourceId, JsonValue value) { if (value.isNotNull()) { try { final JsonValue id = value.get(FIELD_ID); // Update if we got an id, otherwise replace if (id != null && id.isNotNull()) { final UpdateRequest updateRequest = Requests.newUpdateRequest("", value); updateRequest.setAdditionalParameter(PARAM_FIRST_ID, resourceId); return updateInstance(context, value.get(FIELD_ID).asString(), updateRequest) .then( new Function<ResourceResponse, JsonValue, ResourceException>() { @Override public JsonValue apply(ResourceResponse resourceResponse) throws ResourceException { return resourceResponse.getContent(); } }); } else { // no id, replace current instance clear(context, resourceId); final CreateRequest createRequest = Requests.newCreateRequest("", value); createRequest.setAdditionalParameter(PARAM_FIRST_ID, resourceId); return createInstance(context, createRequest) .then( new Function<ResourceResponse, JsonValue, ResourceException>() { @Override public JsonValue apply(ResourceResponse resourceResponse) throws ResourceException { return resourceResponse.getContent(); } }); } } catch (ResourceException e) { return e.asPromise(); } } else { clear(context, resourceId); return newResultPromise(json(null)); } }
/** * Updates the underlying backend policies. * * <p>NOTE: if the update of the underlying policies fails, the underlying policies may be in an * inconsistent state. * * @param context The request context. * @param policies The updated underlying policies to update. * @return A promise containing the list of updated underlying policies or a {@code * ResourceException} if the update failed. */ public Promise<List<Resource>, ResourceException> updatePolicies( ServerContext context, Set<JsonValue> policies) { List<Promise<Resource, ResourceException>> promises = new ArrayList<Promise<Resource, ResourceException>>(); for (JsonValue policy : policies) { String policyName = policy.get("name").asString(); promises.add( policyResource.handleUpdate(context, Requests.newUpdateRequest(policyName, policy))); } return Promises.when(promises); }
@Override public Promise<ActionResponse, ResourceException> handleAction( final Context context, final ActionRequest request) { try { Map<String, String> params = request.getAdditionalParameters(); switch (request.getActionAsEnum(Action.class)) { case updateDbCredentials: String newUser = params.get("user"); String newPassword = params.get("password"); if (newUser == null || newPassword == null) { return adapt(new BadRequestException("Expecting 'user' and 'password' parameters")) .asPromise(); } synchronized (dbLock) { DBHelper.updateDbCredentials(dbURL, user, password, newUser, newPassword); JsonValue config = connectionFactory .getConnection() .read(context, Requests.newReadRequest("config", PID)) .getContent(); config.put("user", newUser); config.put("password", newPassword); UpdateRequest updateRequest = Requests.newUpdateRequest("config/" + PID, config); connectionFactory.getConnection().update(context, updateRequest); return newActionResponse(new JsonValue(params)).asPromise(); } case command: return newActionResponse(new JsonValue(command(request))).asPromise(); default: return adapt(new BadRequestException("Unknown action: " + request.getAction())) .asPromise(); } } catch (IllegalArgumentException e) { return adapt(new BadRequestException("Unknown action: " + request.getAction())).asPromise(); } catch (ResourceException e) { return e.asPromise(); } }
@Test public void testReadSchedule() throws Exception { // given final ReadRequest readRequest = Requests.newReadRequest("test1"); // when Promise<ResourceResponse, ResourceException> promise = schedulerService.handleRead(new RootContext(), readRequest); // then AssertJPromiseAssert.assertThat(promise).isNotNull().succeeded(); ResourceResponse resourceResponse = promise.getOrThrow(); assertThat(resourceResponse.getContent().asMap()).isEqualTo(testScheduleConfig.asMap()); }
@Test public void crestActionNoMappingFails() throws SSOException, DelegationException { // When... final FilterChain chain = AuthorizationFilters.createAuthorizationFilter(provider, module); final Router router = new Router(); router.addRoute(RoutingMode.STARTS_WITH, Router.uriTemplate("/policies"), chain); final Context context = new RealmContext(subjectContext); final ActionRequest request = Requests.newActionRequest("/policies", "unknownAction"); Promise<ActionResponse, ResourceException> promise = router.handleAction(context, request); // Then... assertThat(promise).failedWithException().isInstanceOf(ForbiddenException.class); }
/** * Removes the <code>Dictionary</code> for the given <code>pid</code>. If such a dictionary does * not exist, this method has no effect. * * @param pid The identifier of the dictionary to delet. * @throws IOException If an error occurrs deleting the dictionary. This exception must not be * thrown if no dictionary with the given identifier exists. */ public void delete(String pid) throws IOException { logger.debug("delete call for {}", pid); Object removed = tempStore.remove(pid); if (removed != null) { logger.debug("Deleted {} from temporary store", pid); } try { if (isReady(0) && requireRepository) { String id = pidToId(pid); boolean retry; String rev = null; do { retry = false; try { ReadRequest readRequest = Requests.newReadRequest(id); Map<String, Object> existing = repo.read(readRequest).getContent().asMap(); if (existing != null) { rev = (String) existing.get("_rev"); DeleteRequest r = Requests.newDeleteRequest(id); r.setRevision(rev); repo.delete(r); logger.debug("Deleted {}", pid); } } catch (PreconditionFailedException ex) { logger.debug("Concurrent change during delete, retrying {} {}", pid, rev); retry = true; } catch (NotFoundException ex) { // If it doesn't exists (anymore) that's fine } } while (retry); } } catch (ResourceException ex) { throw new IOException( "Failed to delete configuration + " + pid + " in repository: " + ex.getMessage(), ex); } }
@Test public void testListCurrentlyExecutingJobsAction() throws Exception { // given final ActionRequest readRequest = Requests.newActionRequest("", SchedulerAction.listCurrentlyExecutingJobs.toString()); // when Promise<ActionResponse, ResourceException> promise = schedulerService.handleAction(new RootContext(), readRequest); // then AssertJPromiseAssert.assertThat(promise).isNotNull().succeeded(); ActionResponse resourceResponse = promise.getOrThrow(); assertThat(resourceResponse.getJsonContent().asList().size()).isEqualTo(0); }
@Test public void testResumeJobsAction() throws Exception { // given final ActionRequest readRequest = Requests.newActionRequest("", SchedulerAction.resumeJobs.toString()); // when Promise<ActionResponse, ResourceException> promise = schedulerService.handleAction(new RootContext(), readRequest); // then AssertJPromiseAssert.assertThat(promise).isNotNull().succeeded(); ActionResponse resourceResponse = promise.getOrThrow(); assertThat(resourceResponse.getJsonContent().get("success").getObject()) .isEqualTo(new Boolean(true)); }
/** * TODO: Description. * * @throws SynchronizationException TODO. */ void delete() throws SynchronizationException { if (_id != null) { // forgiving delete try { DeleteRequest r = Requests.newDeleteRequest(linkId(_id)); r.setRevision(_rev); mapping .getService() .getConnectionFactory() .getConnection() .delete(mapping.getService().getServerContext(), r); } catch (ResourceException ose) { LOGGER.warn("Failed to delete link", ose); throw new SynchronizationException(ose); } clear(); } }
/** * Calls buildAuditEvent() and invokes the request to the audit path. * * @param connectionFactory factory used to make crest call to audit service. */ public final Promise<ResourceResponse, ResourceException> log( ConfigAuditState configAuditState, Request request, Context context, ConnectionFactory connectionFactory) { try { JsonValue before = configAuditState.getBefore(); JsonValue after = configAuditState.getAfter(); // Get authenticationId from security context, if it exists. String authenticationId = (context.containsContext(SecurityContext.class)) ? context.asContext(SecurityContext.class).getAuthenticationId() : null; // Build the event utilizing the config builder. AuditEvent auditEvent = ConfigAuditEventBuilder.configEvent() .resourceOperationFromRequest(request) .authenticationFromSecurityContext(context) .runAs(authenticationId) .transactionIdFromRootContext(context) .revision(configAuditState.getRevision()) .timestamp(System.currentTimeMillis()) .eventName(CONFIG_AUDIT_EVENT_NAME) .before(null != before ? before.toString() : "") .after(null != after ? after.toString() : "") .changedFields(getChangedFields(before, after)) .toEvent(); return connectionFactory .getConnection() .create(context, Requests.newCreateRequest(AUDIT_CONFIG_REST_PATH, auditEvent.getValue())) .asPromise(); } catch (ResourceException e) { LOGGER.error("had trouble logging audit event for config changes.", e); return e.asPromise(); } catch (Exception e) { LOGGER.error("had trouble logging audit event for config changes.", e); return new InternalServerErrorException(e.getMessage(), e).asPromise(); } }
/** * TODO: Description. * * @throws SynchronizationException TODO. */ void create() throws SynchronizationException { _id = UUID.randomUUID().toString(); // client-assigned identifier JsonValue jv = toJsonValue(); try { CreateRequest r = Requests.newCreateRequest(linkId(null), _id, jv); Resource resource = mapping .getService() .getConnectionFactory() .getConnection() .create(mapping.getService().getServerContext(), r); this._id = resource.getId(); this._rev = resource.getRevision(); this.initialized = true; } catch (ResourceException ose) { LOGGER.debug("Failed to create link", ose); throw new SynchronizationException(ose); } }
@Test public void testResource() throws Exception { ScriptName scriptName = new ScriptName("resource", getLanguageName()); ScriptEntry scriptEntry = getScriptRegistry().takeScript(scriptName); Assert.assertNotNull(scriptEntry); Script script = scriptEntry.getScript(new RootContext()); // Set RequestLevel Scope script.put("ketto", 2); script.putSafe("callback", mock(Function.class)); JsonValue createContent = new JsonValue(new LinkedHashMap<String, Object>()); createContent.put("externalId", "701984"); createContent.put("userName", "*****@*****.**"); createContent.put( "assignedDashboard", Arrays.asList("Salesforce", "Google", "ConstantContact")); createContent.put("displayName", "Babs Jensen"); createContent.put("nickName", "Babs"); JsonValue updateContent = createContent.copy(); updateContent.put("_id", UUID.randomUUID().toString()); updateContent.put("profileUrl", "https://login.example.com/bjensen"); final Context context = new ApiInfoContext( new SecurityContext(new RootContext(), "*****@*****.**", null), "", ""); script.put("context", context); CreateRequest createRequest = Requests.newCreateRequest("/Users", "701984", createContent); script.put("createRequest", createRequest); ReadRequest readRequest = Requests.newReadRequest("/Users/701984"); script.put("readRequest", readRequest); UpdateRequest updateRequest = Requests.newUpdateRequest("/Users/701984", updateContent); script.put("updateRequest", updateRequest); PatchRequest patchRequest = Requests.newPatchRequest("/Users/701984", PatchOperation.replace("userName", "ddoe")); script.put("patchRequest", patchRequest); QueryRequest queryRequest = Requests.newQueryRequest("/Users/"); script.put("queryRequest", queryRequest); DeleteRequest deleteRequest = Requests.newDeleteRequest("/Users/701984"); script.put("deleteRequest", deleteRequest); ActionRequest actionRequest = Requests.newActionRequest("/Users", "clear"); script.put("actionRequest", actionRequest); script.eval(); }
/** * Update a link. * * @throws SynchronizationException if updating link fails */ void update() throws SynchronizationException { if (_id == null) { throw new SynchronizationException("Attempt to update non-existent link"); } JsonValue jv = toJsonValue(); try { UpdateRequest r = Requests.newUpdateRequest(linkId(_id), jv); r.setRevision(_rev); Resource resource = mapping .getService() .getConnectionFactory() .getConnection() .update(mapping.getService().getServerContext(), r); _rev = resource.getRevision(); } catch (ResourceException ose) { LOGGER.warn("Failed to update link", ose); throw new SynchronizationException(ose); } }
/** {@inheritDoc} */ @Override public Promise<List<Resource>, ResourceException> apply(final ResourceException error) { List<Promise<Resource, ResourceException>> promises = new ArrayList<Promise<Resource, ResourceException>>(); PromiseImpl<Resource, ResourceException> kicker = PromiseImpl.create(); promises.add(kicker); for (String id : policyIds) { promises.add(policyResource.handleDelete(context, Requests.newDeleteRequest(id))); } Promise<List<Resource>, ResourceException> promise = Promises.when(promises) .thenAsync( new AsyncFunction<List<Resource>, List<Resource>, ResourceException>() { @Override public Promise<List<Resource>, ResourceException> apply(List<Resource> value) { // If we succeed in deleting then return the original error return Promises.newExceptionPromise(error); } }); kicker.handleResult(null); return promise; }
@Test public void crestQueryIsAllowed() throws SSOException, DelegationException, ResourceException { // Given... final Set<String> actions = new HashSet<>(Arrays.asList("READ")); final DelegationPermission permission = new DelegationPermission( "/abc", "rest", "1.0", "policies", "read", actions, EXTENSIONS, DUMB_FUNC); given(factory.newInstance("/abc", "rest", "1.0", "policies", "read", actions, EXTENSIONS)) .willReturn(permission); given(subjectContext.getCallerSSOToken()).willReturn(token); given(evaluator.isAllowed(eq(token), eq(permission), eq(ENVIRONMENT))).willReturn(true); QueryResourceHandler handler = mock(QueryResourceHandler.class); Promise<QueryResponse, ResourceException> promise = Promises.newResultPromise(Responses.newQueryResponse("abc-def")); given( provider.queryCollection( isA(Context.class), isA(QueryRequest.class), isA(QueryResourceHandler.class))) .willReturn(promise); // When... final FilterChain chain = AuthorizationFilters.createAuthorizationFilter(provider, module); final Router router = new Router(); router.addRoute(RoutingMode.STARTS_WITH, Router.uriTemplate("/policies"), chain); final RealmContext context = new RealmContext(subjectContext); context.setSubRealm("abc", "abc"); final QueryRequest request = Requests.newQueryRequest("/policies"); Promise<QueryResponse, ResourceException> result = router.handleQuery(context, request, handler); // Then... QueryResponse response = result.getOrThrowUninterruptibly(); assertThat(response.getPagedResultsCookie()).isEqualTo("abc-def"); }
void doPatch(final HttpServletRequest req, final HttpServletResponse resp) { try { // Parse out the required API versions. final AcceptAPIVersion acceptVersion = parseAcceptAPIVersion(req); // Prepare response. prepareResponse(req, resp); // Validate request. preprocessRequest(req); if (req.getHeader(HEADER_IF_NONE_MATCH) != null) { // FIXME: i18n throw new PreconditionFailedException( "Use of If-None-Match not supported for PATCH requests"); } final Map<String, String[]> parameters = req.getParameterMap(); final PatchRequest request = Requests.newPatchRequest(getResourceName(req)).setRevision(getIfMatch(req)); request.getPatchOperations().addAll(getJsonPatchContent(req)); for (final Map.Entry<String, String[]> p : parameters.entrySet()) { final String name = p.getKey(); final String[] values = p.getValue(); if (HttpUtils.isMultiPartRequest(req.getContentType())) { // Ignore - multipart content adds form parts to the parameter set } else if (parseCommonParameter(name, values, request)) { continue; } else { request.setAdditionalParameter(name, asSingleValue(name, values)); } } doRequest(req, resp, acceptVersion, request); } catch (final Exception e) { fail(req, resp, e); } }
/** * Performs the query on the specified object and returns the associated results. * * <p>Queries are parametric; a set of named parameters is provided as the query criteria. The * query result is a JSON object structure composed of basic Java types. * * <p>The returned map is structured as follow: - The top level map contains meta-data about the * query, plus an entry with the actual result records. - The <code>QueryConstants</code> defines * the map keys, including the result records (QUERY_RESULT) * * @param context identifies the object to query. * @param request the parameters of the query to perform. * @return the query results, which includes meta-data and the result records in JSON object * structure format. * @throws NotFoundException if the specified object could not be found. * @throws BadRequestException if the specified params contain invalid arguments, e.g. a query id * that is not configured, a query expression that is invalid, or missing query substitution * tokens. * @throws ForbiddenException if access to the object or specified query is forbidden. */ @Override public Promise<QueryResponse, ResourceException> handleQuery( final Context context, final QueryRequest request, final QueryResourceHandler handler) { // If paged results are requested then decode the cookie in order to determine // the index of the first result to be returned. final int requestPageSize = request.getPageSize(); // Cookie containing offset of last request final String pagedResultsCookie = request.getPagedResultsCookie(); final boolean pagedResultsRequested = requestPageSize > 0; // index of first record (used for SKIP/OFFSET) final int firstResultIndex; if (pagedResultsRequested) { if (StringUtils.isNotEmpty(pagedResultsCookie)) { try { firstResultIndex = Integer.parseInt(pagedResultsCookie); } catch (final NumberFormatException e) { return new BadRequestException("Invalid paged results cookie").asPromise(); } } else { firstResultIndex = Math.max(0, request.getPagedResultsOffset()); } } else { firstResultIndex = 0; } // Once cookie is processed Queries.query() can rely on the offset. request.setPagedResultsOffset(firstResultIndex); try { List<ResourceResponse> results = query(request); for (ResourceResponse result : results) { handler.handleResource(result); } /* * Execute additional -count query if we are paging */ final String nextCookie; // The number of results (if known) final int resultCount; if (pagedResultsRequested) { // count if requested switch (request.getTotalPagedResultsPolicy()) { case ESTIMATE: case EXACT: // Get total if -count query is available final String countQueryId = request.getQueryId() + "-count"; if (queries.queryIdExists(countQueryId)) { QueryRequest countRequest = Requests.copyOfQueryRequest(request); countRequest.setQueryId(countQueryId); // Strip pagination parameters countRequest.setPageSize(0); countRequest.setPagedResultsOffset(0); countRequest.setPagedResultsCookie(null); List<ResourceResponse> countResult = query(countRequest); if (countResult != null && !countResult.isEmpty()) { resultCount = countResult.get(0).getContent().get("total").asInteger(); } else { logger.debug("Count query {} failed.", countQueryId); resultCount = NO_COUNT; } } else { logger.debug("No count query found with id {}", countQueryId); resultCount = NO_COUNT; } break; case NONE: default: resultCount = NO_COUNT; break; } if (results.size() < requestPageSize) { nextCookie = null; } else { final int remainingResults = resultCount - (firstResultIndex + results.size()); if (remainingResults == 0) { nextCookie = null; } else { nextCookie = String.valueOf(firstResultIndex + requestPageSize); } } } else { resultCount = NO_COUNT; nextCookie = null; } if (resultCount == NO_COUNT) { return newQueryResponse(nextCookie).asPromise(); } else { return newQueryResponse(nextCookie, EXACT, resultCount).asPromise(); } } catch (ResourceException e) { return e.asPromise(); } }
/** * Stores the <code>Dictionary</code> under the given <code>pid</code>. * * @param pid The identifier of the dictionary. * @param properties The <code>Dictionary</code> to store. * @throws IOException If an error occurrs storing the dictionary. If this exception is thrown, it * is expected, that {@link #exists(String) exists(pid} returns <code>false</code>. */ public void store(String pid, Dictionary properties) throws IOException { logger.debug("Store call for {} {}", pid, properties); // Store config handling settings in memory if (pid.startsWith("org.apache.felix.fileinstall")) { tempStore.put(pid, properties); return; } try { if (isReady(0) && requireRepository) { String id = pidToId(pid); Map<String, Object> obj = dictToMap(properties); JsonValue content = new JsonValue(obj); String configResourceId = ConfigBootstrapHelper.getId( content.get(ConfigBootstrapHelper.CONFIG_ALIAS).asString(), content.get(ConfigBootstrapHelper.SERVICE_PID).asString(), content.get(ConfigBootstrapHelper.SERVICE_FACTORY_PID).asString()); String configString = (String) obj.get(JSONEnhancedConfig.JSON_CONFIG_PROPERTY); Map<Object, Object> configMap = deserializeConfig(configString); if (configMap != null) { configMap.put("_id", configResourceId); } obj.put(JSONEnhancedConfig.JSON_CONFIG_PROPERTY, configMap); Map<String, Object> existing = null; try { ReadRequest readRequest = Requests.newReadRequest(id); existing = repo.read(readRequest).getContent().asMap(); } catch (NotFoundException ex) { // Just detect that it doesn't exist } if (existing != null) { String rev = (String) existing.get("_rev"); existing.remove("_rev"); existing.remove("_id"); obj.remove("_rev"); // beware, this means _id and _rev should not be in config file obj.remove("_id"); // beware, this means _id and _rev should not be in config file obj.remove(RepoPersistenceManager.BUNDLE_LOCATION); obj.remove(RepoPersistenceManager.FELIX_FILEINSTALL_FILENAME); if (!existing.equals(obj)) { logger.trace("Not matching {} {}", existing, obj); boolean retry; do { retry = false; try { UpdateRequest r = Requests.newUpdateRequest(id, new JsonValue(obj)); r.setRevision(rev); repo.update(r); } catch (PreconditionFailedException ex) { logger.debug("Concurrent change during update, retrying {} {}", pid, rev); ReadRequest readRequest = Requests.newReadRequest(id); existing = repo.read(readRequest).getContent().asMap(); retry = true; } } while (retry); logger.debug("Updated existing config {} {} {}", new Object[] {pid, rev, obj}); } else { logger.debug( "Existing config same as store request, ignoring {} {} {}", new Object[] {pid, rev, obj}); } } else { logger.trace("Creating: {} {} ", id, obj); // This may create a new (empty) configuration, which felix marks with // _felix___cm__newConfiguration=true String newResourceId = id.substring(CONFIG_CONTEXT_PREFIX.length()); CreateRequest createRequest = Requests.newCreateRequest(CONFIG_CONTEXT_PREFIX, new JsonValue(obj)); createRequest.setNewResourceId(newResourceId); obj = repo.create(createRequest).getContent().asMap(); logger.debug("Stored new config in repository {} {}", pid, obj); } } else { tempStore.put(pid, properties); logger.debug("Stored in memory {} {}", pid, properties); } } catch (ResourceException ex) { throw new IOException("Failed to store configuration in repository: " + ex.getMessage(), ex); } }
void doPut(final HttpServletRequest req, final HttpServletResponse resp) { try { // Parse out the required API versions. final AcceptAPIVersion acceptVersion = parseAcceptAPIVersion(req); // Prepare response. prepareResponse(req, resp); // Validate request. preprocessRequest(req); if (req.getHeader(HEADER_IF_MATCH) != null && req.getHeader(HEADER_IF_NONE_MATCH) != null) { // FIXME: i18n throw new PreconditionFailedException( "Simultaneous use of If-Match and If-None-Match not " + "supported for PUT requests"); } final Map<String, String[]> parameters = req.getParameterMap(); final JsonValue content = getJsonContent(req); final String rev = getIfNoneMatch(req); if (ETAG_ANY.equals(rev)) { // This is a create with a user provided resource ID: split the // path into the parent resource name and resource ID. final String resourceName = getResourceName(req); final int i = resourceName.lastIndexOf('/'); final CreateRequest request; if (resourceName.isEmpty()) { // FIXME: i18n. throw new BadRequestException("No new resource ID in HTTP PUT request"); } else if (i < 0) { // We have a pathInfo of the form "{id}" request = Requests.newCreateRequest(EMPTY_STRING, content); request.setNewResourceId(resourceName); } else { // We have a pathInfo of the form "{container}/{id}" request = Requests.newCreateRequest(resourceName.substring(0, i), content); request.setNewResourceId(resourceName.substring(i + 1)); } for (final Map.Entry<String, String[]> p : parameters.entrySet()) { final String name = p.getKey(); final String[] values = p.getValue(); if (HttpUtils.isMultiPartRequest(req.getContentType())) { // Ignore - multipart content adds form parts to the parameter set } else if (parseCommonParameter(name, values, request)) { continue; } else { request.setAdditionalParameter(name, asSingleValue(name, values)); } } doRequest(req, resp, acceptVersion, request); } else { final UpdateRequest request = Requests.newUpdateRequest(getResourceName(req), content).setRevision(getIfMatch(req)); for (final Map.Entry<String, String[]> p : parameters.entrySet()) { final String name = p.getKey(); final String[] values = p.getValue(); if (HttpUtils.isMultiPartRequest(req.getContentType())) { // Ignore - multipart content adds form parts to the parameter set } else if (parseCommonParameter(name, values, request)) { continue; } else { request.setAdditionalParameter(name, asSingleValue(name, values)); } } doRequest(req, resp, acceptVersion, request); } } catch (final Exception e) { fail(req, resp, e); } }
void doGet(final HttpServletRequest req, final HttpServletResponse resp) { try { // Parse out the required API versions. final AcceptAPIVersion acceptVersion = parseAcceptAPIVersion(req); // Prepare response. prepareResponse(req, resp); // Validate request. preprocessRequest(req); rejectIfMatch(req); final Map<String, String[]> parameters = req.getParameterMap(); if (hasParameter(req, PARAM_QUERY_ID) || hasParameter(req, PARAM_QUERY_EXPRESSION) || hasParameter(req, PARAM_QUERY_FILTER)) { // Additional pre-validation for queries. rejectIfNoneMatch(req); // Query against collection. final QueryRequest request = Requests.newQueryRequest(getResourceName(req)); for (final Map.Entry<String, String[]> p : parameters.entrySet()) { final String name = p.getKey(); final String[] values = p.getValue(); if (parseCommonParameter(name, values, request)) { continue; } else if (name.equalsIgnoreCase(PARAM_SORT_KEYS)) { for (final String s : values) { try { request.addSortKey(s.split(SORT_KEYS_DELIMITER)); } catch (final IllegalArgumentException e) { // FIXME: i18n. throw new BadRequestException( "The value '" + s + "' for parameter '" + name + "' could not be parsed as a comma " + "separated list of sort keys"); } } } else if (name.equalsIgnoreCase(PARAM_QUERY_ID)) { request.setQueryId(asSingleValue(name, values)); } else if (name.equalsIgnoreCase(PARAM_QUERY_EXPRESSION)) { request.setQueryExpression(asSingleValue(name, values)); } else if (name.equalsIgnoreCase(PARAM_PAGED_RESULTS_COOKIE)) { request.setPagedResultsCookie(asSingleValue(name, values)); } else if (name.equalsIgnoreCase(PARAM_PAGED_RESULTS_OFFSET)) { request.setPagedResultsOffset(asIntValue(name, values)); } else if (name.equalsIgnoreCase(PARAM_PAGE_SIZE)) { request.setPageSize(asIntValue(name, values)); } else if (name.equalsIgnoreCase(PARAM_QUERY_FILTER)) { final String s = asSingleValue(name, values); try { request.setQueryFilter(QueryFilter.valueOf(s)); } catch (final IllegalArgumentException e) { // FIXME: i18n. throw new BadRequestException( "The value '" + s + "' for parameter '" + name + "' could not be parsed as a valid query filter"); } } else { request.setAdditionalParameter(name, asSingleValue(name, values)); } } // Check for incompatible arguments. if (request.getQueryId() != null && request.getQueryFilter() != null) { // FIXME: i18n. throw new BadRequestException( "The parameters " + PARAM_QUERY_ID + " and " + PARAM_QUERY_FILTER + " are mutually exclusive"); } if (request.getQueryId() != null && request.getQueryExpression() != null) { // FIXME: i18n. throw new BadRequestException( "The parameters " + PARAM_QUERY_ID + " and " + PARAM_QUERY_EXPRESSION + " are mutually exclusive"); } if (request.getQueryFilter() != null && request.getQueryExpression() != null) { // FIXME: i18n. throw new BadRequestException( "The parameters " + PARAM_QUERY_FILTER + " and " + PARAM_QUERY_EXPRESSION + " are mutually exclusive"); } doRequest(req, resp, acceptVersion, request); } else { // Read of instance within collection or singleton. final String rev = getIfNoneMatch(req); if (ETAG_ANY.equals(rev)) { // FIXME: i18n throw new PreconditionFailedException( "If-None-Match * not appropriate for " + getMethod(req) + " requests"); } final ReadRequest request = Requests.newReadRequest(getResourceName(req)); for (final Map.Entry<String, String[]> p : parameters.entrySet()) { final String name = p.getKey(); final String[] values = p.getValue(); if (parseCommonParameter(name, values, request)) { continue; } else if (PARAM_MIME_TYPE.equalsIgnoreCase(name)) { if (values.length != 1 || values[0].split(FIELDS_DELIMITER).length > 1) { // FIXME: i18n. throw new BadRequestException("Only one mime type value allowed"); } if (parameters.get(PARAM_FIELDS).length != 1) { // FIXME: i18n. throw new BadRequestException( "The mime type parameter requires only 1 field to be specified"); } } else { request.setAdditionalParameter(name, asSingleValue(name, values)); } } doRequest(req, resp, acceptVersion, request); } } catch (final Exception e) { fail(req, resp, e); } }