001package gu.sql2java.store;
002
003import static gu.sql2java.store.BinaryUtils.*;
004import static gu.sql2java.store.URLInfo.wrap;
005
006import java.io.IOException;
007import java.net.URL;
008import java.net.URLStreamHandler;
009import java.net.URLStreamHandlerFactory;
010import java.util.Collections;
011import java.util.HashMap;
012import java.util.Hashtable;
013import java.util.Map;
014
015import ref.org.apache.commons.jnet.Installer;
016
017/**
018 * 基于{@link URLStreamHandler}实现二进制存储接口{@link URLStore}的抽象类<br>
019 * 实现{@link URLStreamHandlerFactory}接口,应用启动时应调用{@link #intall()}将当前实例安装到JVM
020 * @author guyadong
021 *
022 */
023public abstract class BaseURLStore implements URLStreamHandlerFactory, URLStore {
024
025        private volatile boolean installed = false;
026        private final Map<String, Class<?>> optionalParamTypes = new Hashtable<>();
027        protected final ThreadLocal<Map<String, Object>> additionalParams = new ThreadLocal<>();
028        protected BaseURLStore() {
029                this(null);
030        }
031        
032        protected BaseURLStore(Map<String, Class<?>> optionalParamTypes) {
033                super();
034                if(optionalParamTypes == null){
035                        optionalParamTypes = Collections.emptyMap();
036                }
037                this.optionalParamTypes.putAll(optionalParamTypes);
038        }
039
040        /**
041         * 存储图像数据
042         * @param binary 二进制数据字节数组
043         * @param md5 imageBytes的MD5校验码
044         * @param extension 文件后缀,可为{@code null} 
045         * @param makeURLOnly 为{@code true}时不存储数据只返回存储URL
046         * @return 存储URL
047         * @throws IOException
048         */
049        protected abstract URL doStore(byte[] binary,String md5, String extension, boolean makeURLOnly) throws IOException;
050
051        /**
052         * 判断存储 URl 是否存在 
053         * @param storedURL 存储URL
054         * @return
055         */
056        protected abstract boolean doExists(URL storedURL);
057        
058        /**
059         * 查找指定MD5的二进制数据
060         * @param md5 MD5校验码
061         * @return 数据存储URL,找不到返回{@code null}
062         */
063        protected abstract URL doFind(String md5);
064        /**
065         * 指定指定的二进制数据
066         * @param storedURL 存储的URL
067         * @return 删除成功返回{@code true},否则返回{@code false}
068         * @throws IOException
069         */
070        protected abstract boolean doDelete(URL storedURL) throws IOException;
071
072        protected abstract URLStreamHandler doGetURLStreamHandler();
073        
074        protected final URL find(URLInfo binary){
075                if(isStored(binary.location)){
076                        return doExists(binary.location) ? binary.location : null;
077                }
078                String md5 = binary.getMD5();
079                return md5 == null ? null : doFind(md5);
080        }
081
082        @Override
083        public final boolean isStored(URL url){
084                return null != url && url.getProtocol().equals(getProtocol());
085        }
086        
087        @Override
088        public final boolean exists(URL url){
089                return null != find(wrap(url));
090        }
091        
092        @Override
093        public final <T>URL store(T input,String md5,String extension, boolean overwrite, boolean makeURLOnly) throws IOException {
094                byte[] binary;
095                if(makeURLOnly){
096                        binary = getBytes(input);
097                        if(!validMd5(md5)){
098                                throw new IOException("VALID md5 required while make URL only");
099                        }
100                }else{
101                        binary = getBytesNotEmpty(input);
102                        if(!validMd5(md5)){
103                                md5 = getMD5String(binary);
104                        }
105                }
106                return doStore(binary,md5,extension, makeURLOnly);
107        }
108
109        @Override
110        public final boolean delete(String md5) throws IOException{
111                if(validMd5(md5)){
112                        URL storedURL;
113                        if(null != (storedURL = doFind(md5))){
114                                return doDelete(storedURL);     
115                        }
116                }
117                return false;
118        }
119
120        @Override
121        public final URL store(URL url, boolean overwrite, boolean makeURLOnly) throws IOException {
122                URLInfo data = wrap(url);
123                URL storedURL;
124                if(null == (storedURL = find(data)) || overwrite){
125                        storedURL = doStore(
126                                        getBytesNotEmpty(data.location), 
127                                        data.getMD5(), 
128                                        data.extension, 
129                                        makeURLOnly);
130                }
131                return storedURL;
132        }
133
134        @Override
135        public final boolean delete(URL url) throws IOException{
136                URL storedURL = url;
137                if(!isStored(storedURL)){
138                        if(null == (storedURL = find(wrap(url)))){
139                                return false;
140                        }
141                }
142                return doDelete(storedURL);
143        }
144        
145        @Override
146        public final BaseURLStore setAdditionalParam(String name,Object value){
147                if(null != name){
148                        Class<?> type = optionalParamTypes.get(name);
149                        if(null != type && null != value){
150                                if(!type.isInstance(value)){
151                                        throw new IllegalArgumentException(String.format("INVALID  param type for %s , %srequired",name,type));
152                                }
153                                if(additionalParams.get() == null){
154                                        additionalParams.set(new HashMap<String,Object>());
155                                }
156                                additionalParams.get().put(name, value);
157                        }
158                }
159                return this;
160        }
161        @Override
162        public final URLStreamHandler createURLStreamHandler(String protocol) {
163        if(getProtocol().equals(protocol)){
164            return doGetURLStreamHandler();
165        }
166        return null;
167        }
168        /**
169         * 将当前{@link URLStreamHandlerFactory}实例安装到JVM
170         * @return 当前对象
171         * @throws Exception
172         */
173        public final BaseURLStore intall() throws Exception{
174                // double check
175                if(!installed){
176                        synchronized(this){
177                                if(!installed){
178                                        Installer.setURLStreamHandlerFactory(this);
179                                        installed = true;
180                                }
181                        }
182                }
183                return this;
184        }
185
186        @Override
187        public int hashCode() {
188                final int prime = 31;
189                int result = 1;
190                result = prime * result + ((getProtocol() == null) ? 0 : getProtocol().hashCode());
191                return result;
192        }
193
194        @Override
195        public boolean equals(Object obj) {
196                if (this == obj)
197                        return true;
198                if (obj == null)
199                        return false;
200                if (!(obj instanceof BaseURLStore))
201                        return false;
202                BaseURLStore other = (BaseURLStore) obj;
203                if (getProtocol() == null) {
204                        if (other.getProtocol() != null)
205                                return false;
206                } else if (!getProtocol().equals(other.getProtocol()))
207                        return false;
208                return true;
209        }
210
211        @Override
212        public String toString() {
213                StringBuilder builder = new StringBuilder();
214                builder.append("BaseURLStore [protocol=");
215                builder.append(getProtocol());
216                builder.append("]");
217                return builder.toString();
218        }
219        
220}
221
222