001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010-2011 ApexIdentity Inc. 015 * Portions Copyright 2011-2015 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.heap; 019 020import static java.lang.String.format; 021import static org.forgerock.http.routing.RouteMatchers.requestUriMatcher; 022import static org.forgerock.http.routing.RoutingMode.EQUALS; 023import static org.forgerock.openig.heap.Keys.ENDPOINT_REGISTRY_HEAP_KEY; 024import static org.forgerock.openig.heap.Keys.LOGSINK_HEAP_KEY; 025import static org.forgerock.openig.heap.Keys.TEMPORARY_STORAGE_HEAP_KEY; 026import static org.forgerock.openig.util.StringUtil.slug; 027 028import java.util.Arrays; 029import java.util.HashSet; 030import java.util.Map; 031import java.util.Set; 032 033import org.forgerock.http.routing.Router; 034import org.forgerock.json.JsonValue; 035import org.forgerock.openig.handler.Handlers; 036import org.forgerock.openig.http.EndpointRegistry; 037import org.forgerock.openig.io.TemporaryStorage; 038import org.forgerock.openig.log.LogSink; 039import org.forgerock.openig.log.Logger; 040 041/** 042 * A generic base class for heaplets with automatically injected fields. 043 * <p> 044 * If the object created is an instance of {@link GenericHeapObject}, it is then 045 * automatically injected with {@code logger} and {@code storage} objects. 046 */ 047public abstract class GenericHeaplet implements Heaplet { 048 049 /** Heap objects to avoid dependency injection (prevents circular dependencies). */ 050 private static final Set<String> SPECIAL_OBJECTS = 051 new HashSet<>(Arrays.asList(LOGSINK_HEAP_KEY, TEMPORARY_STORAGE_HEAP_KEY)); 052 053 /** The name of the object to be created and stored in the heap by this heaplet. */ 054 protected String name; 055 056 /** The fully qualified name of the object to be created. */ 057 protected Name qualified; 058 059 /** The heaplet's object configuration object. */ 060 protected JsonValue config; 061 062 /** Where objects should be put and where object dependencies should be retrieved. */ 063 protected Heap heap; 064 065 /** Provides methods for logging activities. */ 066 protected Logger logger; 067 068 /** Allocates temporary buffers for caching streamed content during processing. */ 069 protected TemporaryStorage storage; 070 071 /** The object created by the heaplet's {@link #create()} method. */ 072 protected Object object; 073 private EndpointRegistry.Registration registration; 074 private EndpointRegistry registry; 075 076 @Override 077 public Object create(Name name, JsonValue config, Heap heap) throws HeapException { 078 this.name = name.getLeaf(); 079 this.qualified = name; 080 this.config = config.required().expect(Map.class); 081 this.heap = heap; 082 if (!SPECIAL_OBJECTS.contains(this.name)) { 083 this.logger = new Logger( 084 heap.resolve( 085 config.get("logSink").defaultTo(LOGSINK_HEAP_KEY), 086 LogSink.class, true), 087 name); 088 this.storage = heap.resolve( 089 config.get("temporaryStorage").defaultTo(TEMPORARY_STORAGE_HEAP_KEY), 090 TemporaryStorage.class); 091 } 092 this.object = create(); 093 if (this.object instanceof GenericHeapObject) { 094 // instrument object if possible 095 GenericHeapObject ghObject = (GenericHeapObject) this.object; 096 ghObject.logger = this.logger; 097 ghObject.storage = this.storage; 098 } 099 start(); 100 return object; 101 } 102 103 /** 104 * Returns this object's {@link EndpointRegistry}, creating it lazily when requested for the first time. 105 * 106 * @return this object's {@link EndpointRegistry} ({@literal /objects/[name]}) 107 * @throws HeapException 108 * should never be thrown 109 */ 110 protected EndpointRegistry endpointRegistry() throws HeapException { 111 if (registry == null) { 112 // Get parent registry (.../objects) 113 EndpointRegistry parent = heap.get(ENDPOINT_REGISTRY_HEAP_KEY, EndpointRegistry.class); 114 Router router = new Router(); 115 router.addRoute(requestUriMatcher(EQUALS, ""), Handlers.NO_CONTENT); 116 String objectName = qualified.getLeaf(); 117 String slug = slug(objectName); 118 if (!slug.equals(objectName)) { 119 logger.warning(format("Heaplet name ('%s') has been converted to " 120 + "a slug ('%s') for URL exposition (REST endpoints).", 121 objectName, 122 slug)); 123 } 124 registration = parent.register(slug, router); 125 registry = new EndpointRegistry(router, registration.getPath()); 126 } 127 return registry; 128 } 129 130 @Override 131 public void destroy() { 132 if (registration != null) { 133 registration.unregister(); 134 } 135 } 136 137 /** 138 * Called to request the heaplet create an object. Called by 139 * {@link Heaplet#create(Name, JsonValue, Heap)} after initializing 140 * the protected field members. Implementations should parse configuration 141 * but not acquire resources, start threads, or log any initialization 142 * messages. These tasks should be performed by the {@link #start()} method. 143 * 144 * @return The created object. 145 * @throws HeapException 146 * if an exception occurred during creation of the heap object 147 * or any of its dependencies. 148 * @throws org.forgerock.json.JsonValueException 149 * if the heaplet (or one of its dependencies) has a malformed 150 * configuration. 151 */ 152 public abstract Object create() throws HeapException; 153 154 /** 155 * Called to request the heaplet start an object. Called by 156 * {@link Heaplet#create(Name, JsonValue, Heap)} after creating and 157 * configuring the object and once the object's logger and storage have been 158 * configured. Implementations should override this method if they need to 159 * acquire resources, start threads, or log any initialization messages. 160 * 161 * @throws HeapException 162 * if an exception occurred while starting the heap object or 163 * any of its dependencies. 164 */ 165 public void start() throws HeapException { 166 // default does nothing 167 } 168}