/* * Copyright 2008-2014 the original author or authors * * 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. */ package org.kaleidofoundry.core.store; import static org.kaleidofoundry.core.i18n.InternalBundleHelper.StoreMessageBundle; import static org.kaleidofoundry.core.store.FileStoreConstants.DEFAULT_CHARSET; import static org.kaleidofoundry.core.store.FileStoreContextBuilder.Charset; import static org.kaleidofoundry.core.util.ObjectHelper.firstNonNull; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; import java.io.UnsupportedEncodingException; import org.kaleidofoundry.core.io.IOHelper; import org.kaleidofoundry.core.lang.annotation.Immutable; import org.kaleidofoundry.core.lang.annotation.NotNull; import org.kaleidofoundry.core.lang.annotation.NotThreadSafe; /** * Default {@link ResourceHandler} implementation <br/> * If the class is built using constructor with a {@link Reader} or an {@link InputStream}, the instance will not be thread safe ! <br/> * If the class is built with the raw data of the resource, there is no problem. * * @author jraduget */ @Immutable @NotThreadSafe(comment = "for the instances built by calling constructor with an inputstream or a reader") class ResourceHandlerBean implements ResourceHandler, Serializable { private static final long serialVersionUID = 1L; private final transient AbstractFileStore store; private final String uri; private long lastModified; private String mimeType; private String charset; private boolean closed; private byte[] bytes; private String text; private long length; private transient InputStream input; private transient Reader reader; /** * needed by REST javax.ws.rs controller */ ResourceHandlerBean() { this(null, null, (InputStream)null); } /** * @param store * @param uri * @param content */ ResourceHandlerBean(final AbstractFileStore store, final String uri, @NotNull final byte[] content) { this.store = store; this.uri = uri; input = null; text = null; reader = null; bytes = content; closed = false; lastModified = -1; length = -1; } /** * @param store * @param uri * @param content * @throws UnsupportedEncodingException default text encoding is UTF-8 */ ResourceHandlerBean(final AbstractFileStore store, final String uri, @NotNull final String content) { this.store = store; this.uri = uri; input = null; text = content; reader = null; bytes = null; closed = false; lastModified = -1; length = -1; } /** * @param store * @param uri * @param content * @param charset * @throws UnsupportedEncodingException default text encoding is UTF-8 */ ResourceHandlerBean(final AbstractFileStore store, final String uri, @NotNull final String content, final String charset) { this.store = store; this.uri = uri; input = null; text = content; this.charset = charset; reader = null; bytes = null; closed = false; lastModified = -1; length = -1; } /** * @param store * @param uri * @param reader * @param charset * @throws UnsupportedEncodingException default text encoding is UTF-8 */ ResourceHandlerBean(final AbstractFileStore store, final String uri, @NotNull final Reader reader, final String charset) { this.store = store; this.uri = uri; input = null; text = null; this.charset = charset; this.reader = reader; bytes = null; closed = false; lastModified = -1; length = -1; } /** * @param store * @param uri * @param input */ ResourceHandlerBean(final AbstractFileStore store, final String uri, final InputStream input) { this.store = store; this.uri = uri; this.input = input; text = null; reader = null; bytes = null; closed = false; lastModified = -1; length = -1; } @Override public String getUri() { return uri; } @Override public String getPath() { if (store != null && store.getBaseUri() != null) { return uri.substring(store.getBaseUri().length()); } else { return uri; } } @Override public boolean isEmpty() { return input == null && reader == null && text == null && bytes == null; } @Override public boolean isClosed() { return closed; } @Override public InputStream getInputStream() throws ResourceException { if (input != null) { return input; } else if (bytes != null) { input = new ByteArrayInputStream(bytes); return input; } else { String charset = firstNonNull(this.charset, store.context.getString(Charset, DEFAULT_CHARSET.getCode())); try { input = new ByteArrayInputStream(getText().getBytes(charset)); return input; } catch (UnsupportedEncodingException uee) { throw new IllegalStateException("Invalid charset encoding " + charset, uee); } } } @Override public byte[] getBytes() throws ResourceException { if (bytes != null) { return bytes; } else if (text != null) { String charset = firstNonNull(this.charset, store.context.getString(Charset, DEFAULT_CHARSET.getCode())); try { return text.getBytes(charset); } catch (UnsupportedEncodingException uee) { throw new IllegalStateException("Invalid charset encoding " + charset, uee); } } else { try { bytes = IOHelper.toByteArray(input); return bytes; } catch (final IOException ioe) { throw new ResourceException(ioe, uri); } finally { // free resource handler close(); } } } @Override public Reader getReader() throws ResourceException { String charset = firstNonNull(this.charset, store.context.getString(Charset, DEFAULT_CHARSET.getCode())); return getReader(charset); } @Override public Reader getReader(final String charset) throws ResourceException { if (reader != null) { return reader; } else if (text != null) { reader = new StringReader(text); return reader; } else { try { reader = new InputStreamReader(getInputStream(), charset); return reader; } catch (final IOException ioe) { throw new ResourceException(ioe, uri); } } } @Override public String getText() throws ResourceException { if (text != null) { return text; } else { String charset = firstNonNull(this.charset, store.context.getString(Charset, DEFAULT_CHARSET.getCode())); return getText(charset); } } @Override public String getText(final String charset) throws ResourceException { if (text != null) { return text; } else { BufferedReader buffReader = null; String inputLine; try { final StringBuilder stb = new StringBuilder(); buffReader = new BufferedReader(getReader(charset)); while ((inputLine = buffReader.readLine()) != null) { stb.append(inputLine.trim()).append("\n"); } if (stb.length() <= 0) { text = ""; } else { text = stb.toString().substring(0, stb.length() - 1); } return text; } catch (final IOException ioe) { throw new ResourceException(ioe, uri); } finally { // free resource handler close(); } } } @Override public void close() { if (input != null && !closed) { try { input.close(); closed = true; if (store != null) { store.unregisterOpenedResource(uri); } } catch (final IOException ioe) { throw new IllegalStateException(StoreMessageBundle.getMessage("store.resource.close.error", ioe.getMessage(), ioe), ioe); } } if (reader != null && !closed) { try { reader.close(); closed = true; if (store != null) { store.unregisterOpenedResource(uri); } } catch (final IOException ioe) { throw new IllegalStateException(StoreMessageBundle.getMessage("store.resource.close.error", ioe.getMessage(), ioe), ioe); } } } @Override public long getLastModified() { return lastModified; } void setLastModified(final long lastModified) { this.lastModified = lastModified; } @Override public String getMimeType() { return mimeType; } void setMimeType(final String mimeType) { this.mimeType = mimeType; } @Override public String getCharset() { return charset; } void setCharset(final String charset) { this.charset = charset; } @Override public long getLength() { return length; } void setLength(long length) { this.length = length; } /** * this method is only used by for caching resource handler (if caching is enable) * * @param inputStream */ void setInputStream(final InputStream inputStream) { input = inputStream; } }