AbstractScriptedService.java revision 21dcdac963f79c098a5ea1a2c5c5e109429c9786
2N/A/*
2N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2N/A *
2N/A * Copyright (c) 2013 ForgeRock AS. All Rights Reserved
2N/A *
2N/A * The contents of this file are subject to the terms
2N/A * of the Common Development and Distribution License
2N/A * (the License). You may not use this file except in
2N/A * compliance with the License.
2N/A *
2N/A * You can obtain a copy of the License at
2N/A * http://forgerock.org/license/CDDLv1.0.html
2N/A * See the License for the specific language governing
2N/A * permission and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL
2N/A * Header Notice in each file and include the License file
2N/A * at http://forgerock.org/license/CDDLv1.0.html
2N/A * If applicable, add the following below the CDDL Header,
2N/A * with the fields enclosed by brackets [] replaced by
2N/A * your own identifying information:
2N/A * "Portions Copyrighted [year] [name of copyright owner]"
2N/A */
2N/A
2N/Apackage org.forgerock.openidm.script;
2N/A
2N/Aimport java.util.Dictionary;
2N/Aimport java.util.EnumSet;
2N/A
2N/Aimport javax.script.Bindings;
2N/Aimport javax.script.ScriptException;
2N/A
2N/Aimport org.apache.felix.scr.annotations.Component;
2N/Aimport org.apache.felix.scr.annotations.Reference;
2N/Aimport org.apache.felix.scr.annotations.ReferencePolicy;
2N/Aimport org.forgerock.services.context.Context;
2N/Aimport org.forgerock.json.JsonValue;
2N/Aimport org.forgerock.json.resource.ActionRequest;
2N/Aimport org.forgerock.json.resource.CreateRequest;
2N/Aimport org.forgerock.json.resource.DeleteRequest;
2N/Aimport org.forgerock.json.resource.NotSupportedException;
2N/Aimport org.forgerock.json.resource.PatchRequest;
2N/Aimport org.forgerock.json.resource.QueryRequest;
2N/Aimport org.forgerock.json.resource.ReadRequest;
2N/Aimport org.forgerock.json.resource.Request;
2N/Aimport org.forgerock.json.resource.RequestHandler;
2N/Aimport org.forgerock.json.resource.RequestType;
2N/Aimport org.forgerock.json.resource.ResourceException;
2N/Aimport org.forgerock.json.resource.UpdateRequest;
2N/Aimport org.forgerock.openidm.core.ServerConstants;
2N/Aimport org.forgerock.script.Scope;
2N/Aimport org.forgerock.script.ScriptEntry;
2N/Aimport org.forgerock.script.ScriptEvent;
2N/Aimport org.forgerock.script.ScriptListener;
2N/Aimport org.forgerock.script.ScriptName;
2N/Aimport org.forgerock.script.ScriptRegistry;
2N/Aimport org.forgerock.script.source.SourceUnit;
2N/Aimport org.osgi.framework.BundleContext;
2N/Aimport org.osgi.framework.ServiceRegistration;
2N/Aimport org.osgi.service.component.ComponentException;
2N/Aimport org.slf4j.Logger;
2N/Aimport org.slf4j.LoggerFactory;
/**
* An AbstractScriptedService does ...
*
*/
@Component(componentAbstract = true)
public abstract class AbstractScriptedService implements ScriptCustomizer, ScriptListener {
/**
* Setup logging for the {@link AbstractScriptedService}.
*/
private static final Logger logger = LoggerFactory.getLogger(AbstractScriptedService.class);
/** Script Registry service. */
@Reference(policy = ReferencePolicy.DYNAMIC)
private ScriptRegistry scriptRegistry;
protected void bindScriptRegistry(final ScriptRegistry service) {
scriptRegistry = service;
}
protected void unbindScriptRegistry(final ScriptRegistry service) {
scriptRegistry = null;
}
private ScriptedRequestHandler embeddedHandler = null;
private ServiceRegistration<RequestHandler> selfRegistration = null;
private Dictionary<String, Object> properties = null;
protected Bindings bindings = null;
protected ScriptName scriptName = null;
/**
* Operation mask for supported operation.
*/
protected final EnumSet<RequestType> mask;
protected AbstractScriptedService() {
mask = EnumSet.allOf(RequestType.class);
}
protected AbstractScriptedService(EnumSet<RequestType> mask) {
this.mask = mask;
}
/**
* Get the {@link ServerConstants#ROUTER_PREFIX} value.
* <p/>
* If it return null then the {@link ServerConstants#ROUTER_PREFIX} won't be
* changed in the service registration properties.
*
* @param factoryPid
* @param configuration
* @return null or String or String[]
*/
protected abstract Object getRouterPrefixes(String factoryPid, JsonValue configuration);
protected abstract BundleContext getBundleContext();
protected ScriptCustomizer getScriptCustomizer() {
return this;
}
protected Dictionary<String, Object> getProperties() {
return properties;
}
protected void setProperties(Dictionary<String, Object> properties) {
this.properties = properties;
}
protected Scope activate(final BundleContext context, final String factoryPid,
final JsonValue configuration) {
Dictionary<String, Object> prop = getProperties();
if (null != prop) {
Object o = getRouterPrefixes(factoryPid, configuration);
if (null != o) {
prop.put(ServerConstants.ROUTER_PREFIX, o);
}
}
try {
ScriptEntry scriptEntry = scriptRegistry.takeScript(configuration);
if (null == scriptEntry) {
logger.error("Failed to get the script {}:{}", configuration
.get(SourceUnit.ATTR_NAME), configuration.get(SourceUnit.ATTR_TYPE));
throw new NullPointerException();
}
scriptEntry.addScriptListener(this);
scriptName = scriptEntry.getName();
embeddedHandler = new ScriptedRequestHandler(scriptEntry, getScriptCustomizer());
selfRegistration = context.registerService(RequestHandler.class, embeddedHandler, prop);
return embeddedHandler;
} catch (ScriptException e) {
throw new ComponentException("Failed to take script: " + factoryPid, e);
}
}
protected Scope modified(final String factoryPid, final JsonValue configuration) {
try {
ScriptEntry scriptEntry = scriptRegistry.takeScript(configuration);
if (null == scriptEntry) {
logger.error("Failed to get the script {}:{}", configuration
.get(SourceUnit.ATTR_NAME), configuration.get(SourceUnit.ATTR_TYPE));
throw new NullPointerException();
}
if (null != scriptName) {
scriptRegistry.deleteScriptListener(scriptName, this);
}
scriptEntry.addScriptListener(this);
scriptName = scriptEntry.getName();
embeddedHandler.setScriptEntry(scriptEntry);
return embeddedHandler;
} catch (ScriptException e) {
logger.error("Failed to modify the ScriptedService", e);
throw new ComponentException("Failed to take script: " + factoryPid, e);
}
}
protected void deactivate() {
try {
if (null != selfRegistration) {
selfRegistration.unregister();
selfRegistration = null;
}
} catch (IllegalStateException e) {
/* Catch if the service was already removed */
selfRegistration = null;
} finally {
if (null != scriptName) {
scriptRegistry.deleteScriptListener(scriptName, this);
}
}
logger.info("OpenIDM Info Service component is deactivated.");
}
// ----- Implementation of ScriptListener interface
public void scriptChanged(ScriptEvent event) throws ScriptException {
if (ScriptEvent.REGISTERED == event.getType()) {
if (null == selfRegistration) {
synchronized (selfRegistration) {
if (null == selfRegistration) {
final ScriptEntry scriptEntry = event.getScriptLibraryEntry();
scriptEntry.setBindings(bindings);
selfRegistration =
getBundleContext().registerService(
RequestHandler.class,
new ScriptedRequestHandler(scriptEntry,
getScriptCustomizer()), getProperties());
}
}
}
} else if (ScriptEvent.UNREGISTERING == event.getType()) {
if (null != selfRegistration) {
synchronized (selfRegistration) {
if (null != selfRegistration) {
selfRegistration.unregister();
}
}
}
} else if (ScriptEvent.MODIFIED == event.getType()) {
/*
* if (null != selfRegistration) { selfRegistration.unregister(); }
* if (null == selfRegistration) { selfRegistration =
* getBundleContext().registerService(RequestHandler.class, new
* ScriptedRequestHandler( event.getScriptLibraryEntry(), this),
* properties); }
*/
}
}
// ----- Implementation of ScriptCustomizer interface
public void handleAction(final Context context, final ActionRequest request, final Bindings bindings)
throws ResourceException {
if (!mask.contains(RequestType.ACTION)) {
throw new NotSupportedException("Actions are not supported for resource instances");
}
handleRequest(context, request, bindings);
}
public void handleCreate(final Context context, final CreateRequest request, final Bindings bindings)
throws ResourceException {
if (!mask.contains(RequestType.CREATE)) {
throw new NotSupportedException("Create operations are not supported");
}
handleRequest(context, request, bindings);
}
public void handleDelete(final Context context, final DeleteRequest request, final Bindings bindings)
throws ResourceException {
if (!mask.contains(RequestType.DELETE)) {
throw new NotSupportedException("Delete operations are not supported");
}
handleRequest(context, request, bindings);
}
public void handlePatch(final Context context, final PatchRequest request, final Bindings bindings)
throws ResourceException {
if (!mask.contains(RequestType.PATCH)) {
throw new NotSupportedException("Patch operations are not supported");
}
handleRequest(context, request, bindings);
}
public void handleQuery(final Context context, final QueryRequest request, final Bindings bindings)
throws ResourceException {
if (!mask.contains(RequestType.QUERY)) {
throw new NotSupportedException("Query operations are not supported");
}
handleRequest(context, request, bindings);
}
public void handleRead(final Context context, final ReadRequest request, final Bindings bindings)
throws ResourceException {
if (!mask.contains(RequestType.READ)) {
throw new NotSupportedException("Read operations are not supported");
}
handleRequest(context, request, bindings);
}
public void handleUpdate(final Context context, final UpdateRequest request, final Bindings bindings)
throws ResourceException {
if (!mask.contains(RequestType.UPDATE)) {
throw new NotSupportedException("Update operations are not supported");
}
handleRequest(context, request, bindings);
}
protected void handleRequest(final Context context, final Request request, final Bindings bindings) {
bindings.put("request", request);
// TODO-crest3 deprecate legacy scripts that use resourceName
bindings.put("resourceName", request.getResourcePathObject());
bindings.put("resourcePath", request.getResourcePathObject());
bindings.put("context", context);
}
}