org.osgl.storage.impl.SObject.java Source code

Java tutorial

Introduction

Here is the source code for org.osgl.storage.impl.SObject.java

Source

/* 
 * Copyright (C) 2013 The Java Storage project
 * Gelin Luo <greenlaw110(at)gmail.com>
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.osgl.storage.impl;

import org.apache.commons.codec.Charsets;
import org.osgl._;
import org.osgl.exception.UnexpectedIOException;
import org.osgl.storage.ISObject;
import org.osgl.storage.IStorageService;
import org.osgl.util.C;
import org.osgl.util.E;
import org.osgl.util.IO;
import org.osgl.util.S;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * The implementation of {@link ISObject}
 */
public abstract class SObject implements ISObject {
    private String key;
    private Map<String, String> attrs = new HashMap<String, String>();
    private boolean valid = true;
    private Throwable cause = null;

    SObject(String key) {
        if (null == key) {
            throw new NullPointerException();
        }
        this.key = key;
    }

    public static SObject getInvalidObject(String key, Throwable cause) {
        SObject sobj = of(key, "");
        sobj.valid = false;
        sobj.cause = cause;
        return sobj;
    }

    public static ISObject getDumpObject(String key) {
        return of(key, "");
    }

    public static ISObject getDumpObject(String key, Map<String, String> attrs) {
        SObject sobj = of(key, "");
        sobj.setAttrs(attrs);
        return sobj;
    }

    public String getKey() {
        return key;
    }

    protected void setAttrs(Map<String, String> attrs) {
        if (null == attrs)
            return;
        this.attrs.putAll(attrs);
    }

    @Override
    public String getAttribute(String key) {
        return attrs.get(key);
    }

    @Override
    public ISObject setAttribute(String key, String val) {
        attrs.put(key, val);
        return this;
    }

    @Override
    public ISObject setAttributes(Map<String, String> attrs) {
        setAttrs(attrs);
        return this;
    }

    @Override
    public boolean hasAttribute() {
        return !attrs.isEmpty();
    }

    @Override
    public Map<String, String> getAttributes() {
        return C.newMap(attrs);
    }

    @Override
    public boolean isEmpty() {
        String s = asString();
        return null == s || "".equals(s);
    }

    @Override
    public boolean isValid() {
        return valid;
    }

    @Override
    public Throwable getException() {
        return cause;
    }

    @Override
    public void consumeOnce(_.Function<InputStream, ?> consumer) {
        InputStream is = null;
        try {
            is = asInputStream();
            consumer.apply(is);
        } finally {
            IO.close(is);
        }
    }

    /**
     * Construct an SObject with file specified. The key to the
     * sobject is the file's path
     * @param file
     * @return an SObject
     */
    public static SObject of(File file) {
        return of(file.getPath(), file);
    }

    /**
     * Construct an SObject with key and file specified
     * @see #of(String, java.io.File, java.util.Map)
     */
    public static SObject of(String key, File file) {
        if (file.canRead() && file.isFile()) {
            SObject sobj = new FileSObject(key, file);
            sobj.setAttribute(ATTR_FILE_NAME, file.getName());
            sobj.setAttribute(ATTR_CONTENT_TYPE, new MimetypesFileTypeMap().getContentType(file));
            return sobj;
        } else {
            return getInvalidObject(key, new IOException("File is a directory or not readable"));
        }
    }

    /**
     * Deprecated
     * @see #of(String, java.io.File)
     */
    @Deprecated
    public static SObject valueOf(String key, File f) {
        return of(key, f);
    }

    /**
     * Construct an SObject with specified key, file and attributes
     * specified in {@link java.util.Map}
     * @see #of(String, java.io.File, String...)
     */
    public static SObject of(String key, File file, Map<String, String> attributes) {
        SObject sobj = of(key, file);
        sobj.setAttributes(attributes);
        return sobj;
    }

    /**
     * @see #of(String, java.io.File, java.util.Map)
     */
    @Deprecated
    public static SObject valueOf(String key, File file, Map<String, String> conf) {
        return of(key, file, conf);
    }

    /**
     * Construct an SObject with key, file and attributes specified in
     * key1, val1, key2, val2... sequence
     * @see #of(String, java.io.File, java.util.Map)
     */
    public static SObject of(String key, File file, String... attrs) {
        SObject sobj = of(key, file);
        Map<String, String> map = C.map(attrs);
        sobj.setAttributes(map);
        return sobj;
    }

    /**
     * Deprecated
     * @see #of(String, java.io.File, String...)
     */
    @Deprecated
    public static SObject valueOf(String key, File file, String... attrs) {
        return of(key, file, attrs);
    }

    /**
     * Construct an sobject with specified input stream and a randomly
     * generated key.
     *
     * <p>Node the sobject constrcuted from input stream has limits
     * please see the comment to {@link #of(String, java.io.InputStream)}
     * </p>
     *
     * @see #of(String, java.io.InputStream)
     */
    public static SObject of(InputStream is) {
        return of(S.random(), is);
    }

    /**
     * Construct an SObject with key and input stream. Note unlike sobject
     * constructed with String, byte array or file, the sobject constructed
     * with input stream can only be read for one time. If the program
     * tries to access the Sobject the second time, it will encountered an
     * {@link UnexpectedIOException}. Another limit of this sobject is it
     * does not support {@link org.osgl.storage.ISObject#getLength()} method
     *
     * <p>If it needs to construct an SObject without these limits from
     * an input stream, then it shall first read the inputstream into
     * a bytearray, and use the byte array to construct the sobject like
     * following code</p>
     *
     * <pre><code>
     * InputStream is = ...
     * ...
     * ISObject sobj = SObject.of(IO.readContent(is))
     * </code><pre>
     */
    public static SObject of(String key, InputStream is) {
        try {
            return new InputStreamSObject(key, is);
        } catch (Exception e) {
            return getInvalidObject(key, e);
        }
    }

    /**
     * deprecated
     * @see #of(String, java.io.InputStream)
     */
    @Deprecated
    public static SObject valueOf(String key, InputStream is) {
        return of(key, is);
    }

    /**
     * Construct a sobject with key, input stream and attributes specified in a
     * {@link java.util.Map}.
     *
     * <p>Node the sobject constrcuted from input stream has limits
     * please see the comment to {@link #of(String, java.io.InputStream)}
     * </p>
     *
     * @see #of(String, java.io.InputStream)
     */
    public static SObject of(String key, InputStream is, Map<String, String> conf) {
        SObject sobj = of(key, is);
        sobj.setAttributes(conf);
        return sobj;
    }

    /**
     * deprecated
     * @see #of(String, java.io.InputStream, java.util.Map)
     */
    @Deprecated
    public static SObject valueOf(String key, InputStream is, Map<String, String> conf) {
        return of(key, is, conf);
    }

    /**
     * Construct a sobject with key, input stream and attributes specified in a
     * sequence like key1, val1, key2, val2, ...
     *
     * <p>Node the sobject constrcuted from input stream has limits
     * please see the comment to {@link #of(String, java.io.InputStream)}
     * </p>
     *
     * @see #of(String, java.io.InputStream)
     */
    public static SObject of(String key, InputStream is, String... attrs) {
        SObject sobj = of(key, is);
        Map<String, String> map = C.map(attrs);
        sobj.setAttributes(map);
        return sobj;
    }

    /**
     * deprecated
     * @see #of(String, java.io.File, String...)
     */
    @Deprecated
    public static SObject valueOf(String key, InputStream is, String... attrs) {
        return of(key, is, attrs);
    }

    /**
     * Construct an sobject with specified content in String and a randomly
     * generated key
     *
     * @see #of(String, String)
     */
    public static SObject of(String content) {
        return new StringSObject(S.random(), content);
    }

    /**
     * Construct an sobject with content and key specified
     *
     * @see #of(String, String, Map)
     */
    public static SObject of(String key, String content) {
        return new StringSObject(key, content);
    }

    /**
     * Deprecated
     * @see #of(String, String)
     */
    @Deprecated
    public static SObject valueOf(String key, String content) {
        return of(key, content);
    }

    /**
     * Construct an sobject with content, key and attributes specified in
     * {@link java.util.Map}
     */
    public static SObject of(String key, String content, Map<String, String> attrs) {
        SObject sobj = of(key, content);
        sobj.setAttributes(attrs);
        return sobj;
    }

    /**
     * Deprecated
     * @see #of(String, String, java.util.Map)
     */
    @Deprecated
    public static SObject valueOf(String key, String content, Map<String, String> attrs) {
        return of(key, content, attrs);
    }

    /**
     * Construct an sobject with key, content and attributes specified in sequence
     * key1, val1, key2, val2, ...
     *
     * @see #of(String, String, java.util.Map)
     */
    public static SObject of(String key, String content, String... attrs) {
        SObject sobj = of(key, content);
        Map<String, String> map = C.map(attrs);
        sobj.setAttributes(map);
        return sobj;
    }

    /**
     * Deprecated
     * @see #of(String, String, String...)
     */
    @Deprecated
    public static SObject valueOf(String key, String content, String... attrs) {
        return of(key, content, attrs);
    }

    /**
     * Construct an sobject with content in byte array and
     * a random generated key
     *
     * @see #of(String, byte[])
     */
    public static SObject of(byte[] buf) {
        return of(S.random(), buf);
    }

    /**
     * Construct an sobject with specified key and content
     * in byte array
     * @see #of(String, byte[], java.util.Map)
     */
    public static SObject of(String key, byte[] buf) {
        return new ByteArraySObject(key, buf);
    }

    /**
     * Deprecated
     * @see #of(String, byte[])
     */
    @Deprecated
    public static SObject valueOf(String key, byte[] buf) {
        return of(key, buf);
    }

    /**
     * Construct an sobject with specified key, content as byte array
     * and attributes in {@link java.util.Map}
     *
     * @see #of(String, byte[], String...)
     */
    public static SObject of(String key, byte[] buf, Map<String, String> attrs) {
        SObject sobj = valueOf(key, buf);
        sobj.setAttributes(attrs);
        return sobj;
    }

    /**
     * Deprecated
     * @see #of(String, byte[], java.util.Map)
     */
    @Deprecated
    public static SObject valueOf(String key, byte[] buf, Map<String, String> attrs) {
        return of(key, buf, attrs);
    }

    /**
     * Construct an sobject with specified key, content in byte array and
     * attributes in sequence of key1, val1, key1, val2, ...
     * @see #of(String, byte[], java.util.Map)
     */
    public static SObject of(String key, byte[] buf, String... attrs) {
        SObject sobj = valueOf(key, buf);
        Map<String, String> map = C.map(attrs);
        sobj.setAttributes(map);
        return sobj;
    }

    /**
     * Deprecated
     * @see #of(String, byte[], String...)
     */
    @Deprecated
    public static SObject valueOf(String key, byte[] buf, String... attrs) {
        return of(key, buf, attrs);
    }

    public static SObject valueOf(String key, ISObject copy) {
        SObject sobj = of(key, copy.asByteArray());
        sobj.setAttrs(copy.getAttributes());
        return sobj;
    }

    public static SObject lazyLoad(String key, IStorageService ss) {
        return new LazyLoadSObject(key, ss);
    }

    public static SObject lazyLoad(String key, IStorageService ss, Map<String, String> conf) {
        SObject sobj = lazyLoad(key, ss);
        sobj.setAttributes(conf);
        return sobj;
    }

    public static SObject lazyLoad(String key, IStorageService ss, String... attrs) {
        SObject sobj = lazyLoad(key, ss);
        Map<String, String> map = C.map(attrs);
        sobj.setAttributes(map);
        return sobj;
    }

    private static File createTempFile() {
        try {
            return File.createTempFile("sobj_", ".tmp");
        } catch (IOException e) {
            throw E.ioException(e);
        }
    }

    private static class StringSObject extends SObject {
        private String s_ = null;

        StringSObject(String key, String s) {
            super(key);
            s_ = null == s ? "" : s;
        }

        @Override
        public byte[] asByteArray() {
            return s_.getBytes();
        }

        @Override
        public File asFile() {
            File tmpFile = createTempFile();
            IO.writeContent(s_, tmpFile);
            return tmpFile;
        }

        @Override
        public InputStream asInputStream() {
            return IO.is(asByteArray());
        }

        @Override
        public String asString() {
            return s_;
        }

        @Override
        public String asString(Charset charset) throws UnexpectedIOException {
            return s_;
        }

        @Override
        public long getLength() {
            return s_.length();
        }

    }

    private static class FileSObject extends SObject {
        private File file_;
        private byte[] ba_;

        FileSObject(String key, File file) {
            super(key);
            E.NPE(file);
            file_ = file;
        }

        private void readToCache() {
            if (null != ba_)
                return;
            ba_ = IO.readContent(file_);
        }

        @Override
        public long getLength() {
            return file_.length();
        }

        @Override
        public File asFile() throws UnexpectedIOException {
            return file_;
        }

        @Override
        public String asString() throws UnexpectedIOException {
            return asString(Charsets.UTF_8);
        }

        @Override
        public String asString(Charset charset) throws UnexpectedIOException {
            readToCache();
            return new String(ba_, charset);
        }

        @Override
        public byte[] asByteArray() throws UnexpectedIOException {
            readToCache();
            return ba_;
        }

        @Override
        public InputStream asInputStream() throws UnexpectedIOException {
            return IO.is(file_);
        }

    }

    private static class ByteArraySObject extends SObject {
        protected byte[] buf_;

        ByteArraySObject(String key, byte[] buf) {
            super(key);
            E.NPE(buf);
            buf_ = buf;
        }

        @Override
        public byte[] asByteArray() {
            int len = buf_.length;
            byte[] ba = new byte[len];
            System.arraycopy(buf_, 0, ba, 0, len);
            return ba;
        }

        @Override
        public File asFile() {
            File tmpFile = createTempFile();
            IO.write(buf_, tmpFile);
            return tmpFile;
        }

        @Override
        public InputStream asInputStream() {
            return IO.is(buf_);
        }

        @Override
        public String asString() {
            return asString(Charsets.UTF_8);
        }

        @Override
        public String asString(Charset charset) throws UnexpectedIOException {
            return new String(buf_, charset);
        }

        @Override
        public long getLength() {
            return buf_.length;
        }
    }

    private static class InputStreamSObject extends SObject {
        private final InputStream is_;

        InputStreamSObject(String key, InputStream is) {
            super(key);
            E.NPE(is);
            this.is_ = is;
        }

        @Override
        public byte[] asByteArray() {
            return IO.readContent(is_);
        }

        @Override
        public File asFile() {
            File tmpFile = createTempFile();
            IO.write(is_, tmpFile);
            return tmpFile;
        }

        @Override
        public InputStream asInputStream() {
            return is_;
        }

        @Override
        public String asString() {
            return asString(Charsets.UTF_8);
        }

        @Override
        public String asString(Charset charset) throws UnexpectedIOException {
            return new String(asByteArray(), charset);
        }

        @Override
        public long getLength() {
            throw E.unsupport();
        }
    }

    private static class LazyLoadSObject extends SObject {
        private volatile ISObject sobj_;
        private IStorageService ss_;

        LazyLoadSObject(String key, IStorageService ss) {
            super(key);
            E.NPE(ss);
            ss_ = ss;
        }

        private ISObject force() {
            if (null == sobj_) {
                synchronized (this) {
                    if (null == sobj_)
                        sobj_ = ss_.get(getKey());
                }
            }
            return sobj_;
        }

        @Override
        public long getLength() {
            return null == sobj_ ? -1 : sobj_.getLength();
        }

        @Override
        public File asFile() throws UnexpectedIOException {
            return force().asFile();
        }

        @Override
        public String asString() throws UnexpectedIOException {
            return force().asString();
        }

        @Override
        public String asString(Charset charset) throws UnexpectedIOException {
            return force().asString(charset);
        }

        @Override
        public byte[] asByteArray() throws UnexpectedIOException {
            return force().asByteArray();
        }

        @Override
        public InputStream asInputStream() throws UnexpectedIOException {
            return force().asInputStream();
        }
    }

}