All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bndly.rest.controller.impl.ControllerResourceRenderer Maven / Gradle / Ivy

The newest version!
package org.bndly.rest.controller.impl;

/*-
 * #%L
 * REST Controller Impl
 * %%
 * Copyright (C) 2013 - 2020 Cybercon GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import org.bndly.common.data.io.IOUtils;
import org.bndly.rest.api.ContentType;
import org.bndly.rest.api.Context;
import org.bndly.rest.api.Resource;
import org.bndly.rest.api.ResourceRenderer;
import org.bndly.rest.api.ResourceURI;
import org.bndly.rest.api.StatusWriter.Code;
import org.bndly.rest.controller.api.EntityRenderer;
import org.bndly.rest.controller.api.Response;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;

/**
 *
 * @author cybercon <[email protected]>
 */
@Component(service = ResourceRenderer.class, immediate = true)
public class ControllerResourceRenderer implements ResourceRenderer {

	private final List listOfAllEntityRenderers = new ArrayList<>();
	private final ReadWriteLock entityRenderersLock = new ReentrantReadWriteLock();
	
	private EntityRenderer xmlEntityRenderer;
	private EntityRenderer jsonEntityRenderer;
	
	@Activate
	public void activate() {
	}
	@Deactivate
	public void deactivate() {
		entityRenderersLock.writeLock().lock();
		try {
			listOfAllEntityRenderers.clear();
			xmlEntityRenderer = null;
			jsonEntityRenderer = null;
		} finally {
			entityRenderersLock.writeLock().unlock();
		}
	}
	
	@Reference(
			bind = "addEntityRenderer",
			unbind = "removeEntityRenderer",
			cardinality = ReferenceCardinality.MULTIPLE,
			policy = ReferencePolicy.DYNAMIC,
			service = EntityRenderer.class
	)
	public void addEntityRenderer(EntityRenderer entityRenderer) {
		if (entityRenderer != null) {
			entityRenderersLock.writeLock().lock();
			try {
				listOfAllEntityRenderers.add(entityRenderer);
				if (entityRenderer.getSupportedContentType().getName().equals(ContentType.XML.getName())) {
					xmlEntityRenderer = entityRenderer;
				}
				if (entityRenderer.getSupportedContentType().getName().equals(ContentType.JSON.getName())) {
					jsonEntityRenderer = entityRenderer;
				}
			} finally {
				entityRenderersLock.writeLock().unlock();
			}
		}
	}

	public void removeEntityRenderer(EntityRenderer entityRenderer) {
		if (entityRenderer != null) {
			// this is not thread safe, but this should only be critical when there is some deployment going on.
			// hence i leave it unsafe regarding multithreading.
			entityRenderersLock.writeLock().lock();
			try {
				if (entityRenderer == xmlEntityRenderer) {
					xmlEntityRenderer = null;
				}
				if (entityRenderer == jsonEntityRenderer) {
					jsonEntityRenderer = null;
				}
				Iterator iterator = listOfAllEntityRenderers.iterator();
				while (iterator.hasNext()) {
					EntityRenderer next = iterator.next();
					if (next == entityRenderer) {
						iterator.remove();
					}
				}
			} finally {
				entityRenderersLock.writeLock().unlock();
			}
		}
	}
	
	@Override
	public boolean supports(Resource resource, Context context) {
		return ControllerResource.class.isInstance(resource);
	}
	
	@Override
	public void render(Resource resource, Context context) throws IOException {
		ControllerResource cr = (ControllerResource) resource;
		Object result = cr.getResult();
		if (result != null) {
			if (Response.class.isInstance(result)) {
				Response r = (Response) result;
				renderResponse(r, context);
			} else {
				throw new IllegalStateException("unsupported result for controller resource");
			}
		}
	}

	private void renderResponse(final Response response, Context context) throws IOException {
		Code code = Code.fromHttpCode(response.getStatus());
		if (code != null) {
			context.getStatusWriter().write(code);
		}
		String l = response.getLocation();
		if (l != null) {
			context.setLocation(context.createURIBuilder().replace(l).build());
		}
		Map headers = response.getHeaders();
		if (headers != null) {
			for (Map.Entry entry : headers.entrySet()) {
				String name = entry.getKey();
				final String value = entry.getValue();
				if ("Content-Type".equalsIgnoreCase(name)) {
					// ignore
				} else {
					context.setOutputHeader(name, value);
				}
			}
		}

		Object entity = response.getEntity();
		if (entity != null) {
			// render the entity to the output stream
			OutputStream os = context.getOutputStream();
			// render the entity to the output stream
			if (InputStream.class.isInstance(entity)) {
				IOUtils.copy((InputStream) entity, os);
			} else if (String.class.isInstance(entity)) {
				IOUtils.copy(((String) entity).getBytes("UTF-8"), os);
			} else {
				// dependending on the requested content type, we will render
				// use a custom renderer for xml, json or anything else
				final EntityRenderer usedRenderer = findMatchingEntityRenderer(context);
				context.setOutputContentType(usedRenderer.getSupportedContentType(), usedRenderer.getSupportedEncoding());
				usedRenderer.render(entity, os);
			}
		}
	}

	private EntityRenderer findMatchingEntityRenderer(Context context) {
		ContentType dct = context.getDesiredContentType();
		entityRenderersLock.readLock().lock();
		try {
			Iterator iter = listOfAllEntityRenderers.iterator();
			ResourceURI.Extension ext = context.getURI().getExtension();
			String contextExtension = ext == null ? null : ext.getName();
			while (iter.hasNext()) {
				EntityRenderer next = iter.next();
				if (next.getSupportedContentType() == dct
						|| next.getSupportedContentType().getExtension().equals(contextExtension)
						|| dct != null
						&& (next.getSupportedContentType().getExtension().equals(dct.getExtension())
						|| next.getSupportedContentType().getName().equals(dct.getName()))) {
					return next;
				}
			}
			if (xmlEntityRenderer != null) {
				return xmlEntityRenderer;
			}
			if (jsonEntityRenderer != null) {
				return jsonEntityRenderer;
			}
			return null;
		} finally {
		entityRenderersLock.readLock().unlock();
		}
	}

	public void setXmlEntityRenderer(EntityRenderer xmlEntityRenderer) {
		entityRenderersLock.writeLock().lock();
		try {
			this.xmlEntityRenderer = xmlEntityRenderer;
		} finally {
			entityRenderersLock.writeLock().unlock();
		}
	}

	public void setJsonEntityRenderer(EntityRenderer jsonEntityRenderer) {
		entityRenderersLock.writeLock().lock();
		try {
			this.jsonEntityRenderer = jsonEntityRenderer;
		} finally {
			entityRenderersLock.writeLock().unlock();
		}
	}
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy