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

com.cedarsoftware.ncube.ContentCmdCell.groovy Maven / Gradle / Ivy

There is a newer version: 5.6.9
Show newest version
package com.cedarsoftware.ncube

import com.cedarsoftware.ncube.util.CdnRouter
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import org.springframework.util.FastByteArrayOutputStream

import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.util.concurrent.ConcurrentHashMap

import static com.cedarsoftware.util.IOUtilities.close
import static com.cedarsoftware.util.IOUtilities.transfer
import static com.cedarsoftware.util.UrlUtilities.getContentFromUrlAsString
import static com.cedarsoftware.util.UrlUtilities.readErrorResponse

/**
 * This class represents any cell that needs to return content from a URL.
 * For example, String or Binary content.
 *
 * @author John DeRegnaucourt ([email protected])
 *         
* Copyright (c) Cedar Software LLC *

* 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. */ @Slf4j @CompileStatic abstract class ContentCmdCell extends UrlCommandCell { private static Map extToMimeType = new ConcurrentHashMap<>() static { extToMimeType['.css'] = 'text/css' extToMimeType['.html'] = 'text/html' extToMimeType['.js'] = 'application/javascript"' extToMimeType['.xml'] = 'application/xml' extToMimeType['.json'] = 'application/json' extToMimeType['.jpg'] = 'image/jpeg' extToMimeType['.png'] = 'image/png' extToMimeType['.gif'] = 'image/gif' extToMimeType['.bmp'] = 'image/bmp' } // constructor only for serialization. ContentCmdCell() {} ContentCmdCell(String cmd, String url, boolean cacheContent) { super(cmd, url, cacheContent) } protected def fetchResult(Map ctx) { Object data if (url == null) { data = cmd } else { data = fetchContentFromUrl(ctx) } return executeInternal(data, ctx) } protected def executeInternal(Object data, Map ctx) { return data } protected def fetchContentFromUrl(Map ctx) { Map input = getInput(ctx) if (input.containsKey(CdnRouter.HTTP_REQUEST) && input.containsKey(CdnRouter.HTTP_RESPONSE)) { return proxyFetch(ctx) } else { return simpleFetch(ctx) } } protected def simpleFetch(Map ctx) { NCube cube = getNCube(ctx) URL u = getActualUrl(ctx) // Try to load twice. for (int i=0; i < 2; i++) { try { return grab(u) } catch (Exception e) { final String className = getClass().simpleName String errorMsg = 'url: ' + url + ', n-cube: ' + cube.name + ', app: ' + cube.applicationID if (i == 1) { // Note: Error is not marked - it will be retried in the future String msg = 'Unable to load content from ' + errorMsg log.warn(className + ': failed 2nd attempt [will retry on future attempts] unable to fetch contents, ' + errorMsg) throw new IllegalStateException(msg, e) } else { log.warn(className + ': retrying fetch, ' + errorMsg) Thread.sleep(150) } } } // Will never happen - loop will throw exception if 2nd attempt fails return null } protected Object grab(URL u) { return getContentFromUrlAsString(u, true) } protected Object proxyFetch(Map ctx) { Map input = getInput(ctx) HttpServletRequest request = (HttpServletRequest) input[CdnRouter.HTTP_REQUEST] HttpServletResponse response = (HttpServletResponse) input[CdnRouter.HTTP_RESPONSE] HttpURLConnection conn = null URL actualUrl = null try { actualUrl = getActualUrl(ctx) HttpURLConnection.followRedirects = true URLConnection connection = actualUrl.openConnection() if (!(connection instanceof HttpURLConnection)) { // Handle a "file://" URL connection.connect() addFileHeader(actualUrl, response) return transferFromServer(connection, response) } conn = (HttpURLConnection) connection conn.allowUserInteraction = false conn.requestMethod = "GET" conn.doOutput = true conn.doInput = true conn.readTimeout = 20000 conn.connectTimeout = 10000 setupRequestHeaders(conn, request) conn.connect() // Note, could transfer content from requestor on (info besides header) // Note, could transfer POST requests int resCode = conn.responseCode if (resCode <= HttpServletResponse.SC_PARTIAL_CONTENT) { transferResponseHeaders(conn, response) return transferFromServer(conn, response) } else { readErrorResponse(conn) response.sendError(resCode, conn.responseMessage) return null } } catch (SocketTimeoutException e) { try { log.warn("Socket time out occurred fetching: " + actualUrl, e) response.sendError(HttpServletResponse.SC_NOT_FOUND, "File not found: " + actualUrl.toString()) } catch (IOException ignore) { } } catch (Exception e) { try { log.error("Error occurred fetching: " + actualUrl, e) readErrorResponse(conn) response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.message) } catch (IOException ignored) { } } return null } private Object transferFromServer(URLConnection conn, HttpServletResponse response) throws IOException { InputStream input = null OutputStream out = null try { input = new BufferedInputStream(conn.inputStream, 32768) if (cacheable) { input = new CachingInputStream(input) } out = response.outputStream transfer(input, out) return cacheable ? ((CachingInputStream) input).streamCache : null // must call .getStreamCache() with CompileStatic } finally { close(input) close(out) } } private static void setupRequestHeaders(URLConnection c, HttpServletRequest request) { Enumeration headerNames = request.headerNames while (headerNames.hasMoreElements()) { String key = (String) headerNames.nextElement() String value = request.getHeader(key) c.setRequestProperty(key, value) } } private static void transferResponseHeaders(URLConnection c, HttpServletResponse response) { Map> headerFields = c.headerFields Set>> entries = headerFields.entrySet() for (Map.Entry> entry : entries) { if (entry.value != null && entry.key != null) { for (String s : entry.value) { if (!"X-Frame-Options".equalsIgnoreCase(s)) { response.addHeader(entry.key, s) } } } } } private static String getExtension(String urlPath) { int index = urlPath == null ? -1 : urlPath.lastIndexOf(EXTENSION_SEPARATOR as int) return index == -1 ? null : urlPath.substring(index).intern() } static void addFileHeader(URL actualUrl, HttpServletResponse response) { if (actualUrl == null) { return } String ext = getExtension(actualUrl.toString().toLowerCase()) String mime = extToMimeType[ext] if (mime == null) { return } response.addHeader("content-type", mime) } static class CachingInputStream extends FilterInputStream { FastByteArrayOutputStream streamCache = new FastByteArrayOutputStream() /** * Creates a {@code FilterInputStream} * by assigning the argument {@code in} * to the field {@code this.in} so as * to remember it for later use. * @param in the underlying input stream, or {@code null} if * this instance is to be created without an underlying stream. */ protected CachingInputStream(InputStream input) { super(input) } int read(byte[] b, int off, int len) throws IOException { int count = super.read(b, off, len) if (count != -1) { streamCache.write(b, off, count) } return count } int read() throws IOException { int result = super.read() if (result != -1) { streamCache.write(result) } return result } byte[] getStreamCache() { return streamCache.toByteArrayUnsafe() } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy