001package gu.sql2java.store;
002
003import static gu.sql2java.store.BinaryUtils.saveBytes;
004
005import java.io.File;
006import java.io.FileInputStream;
007import java.io.FileNotFoundException;
008import java.io.FilenameFilter;
009import java.io.IOException;
010import java.io.InputStream;
011import java.net.MalformedURLException;
012import java.net.URL;
013import java.net.URLConnection;
014import java.net.URLStreamHandler;
015import java.nio.file.Files;
016import java.nio.file.Path;
017import java.nio.file.Paths;
018
019import com.google.common.base.ConditionChecks;
020
021/**
022 * 二进制数据本地存储实现
023 * @author guyadong
024 *
025 */
026public class LocalBinaryStore extends BaseURLStore {
027        private static final String ORIGIN_ROOT_NAME = "origin";
028        private static final String PROTOCOL = "lbs";
029        private final URLStreamHandler handler = new Handler(); 
030        private File storeRoot;
031        public static final LocalBinaryStore SINGLETON = new LocalBinaryStore();
032        private LocalBinaryStore() {
033        }
034
035        /**
036         * 创建iadb存储地址对象(将path中storeRoot路径剥离)
037         * @param file
038         * @return
039         */
040        protected URL createStoreURL(File file){
041                try {
042                        return new URL(PROTOCOL, null,file.toURI().toURL().getPath().substring(getStoreRoot().toURI().toURL().getPath().length()));
043                } catch (MalformedURLException e) {
044                        throw new RuntimeException(e);
045                }
046        }
047        @Override
048        protected boolean doExists(URL storedURL){
049                return Files.isReadable(pathOf(storedURL));
050        }
051        @Override
052        protected URL doFind(final String md5) {
053                File folder = relativeFolderPath(md5).toFile();
054                if(folder.isDirectory()){
055                        File[] files = folder.listFiles(new FilenameFilter() {
056                                @Override
057                                public boolean accept(File dir, String name) {
058                                        return name.startsWith(md5);
059                                }
060                        });
061                        try {
062                                return files.length >0 ? files[0].toURI().toURL() : null;
063                        } catch (MalformedURLException e) {
064                                throw new RuntimeException(e);
065                        }
066                }
067                return null;
068        }
069
070        protected Path pathOf(URL storedURL) {
071                // 在path中添加root路径
072                return Paths.get(getStoreRoot().getPath(),storedURL.getPath());
073        }
074        @Override
075        protected URL doStore(byte[] binary, String md5, String extension, boolean makeURLOnly) throws IOException {
076                File dst = createDestFile(md5, extension);
077                if(!makeURLOnly){
078                        if(dst.length() != binary.length){
079                                saveBytes(binary, dst, true);
080                        }                       
081                }
082                return createStoreURL(dst);
083        }
084        @Override
085        protected boolean doDelete(URL storedURL) throws IOException{           
086                return Files.deleteIfExists(pathOf(storedURL));
087        }
088
089        @Override
090        public final String getProtocol() {
091                return PROTOCOL;
092        }
093
094        public LocalBinaryStore setStoreRoot(File storeRoot) {
095                this.storeRoot = storeRoot;
096                return this;
097        }
098
099        private File getStoreRoot() {
100                return ConditionChecks.checkNotNull(storeRoot, 
101                                IllegalStateException.class, 
102                                "storeRoot is uninitialized,please call setStoreRoot(File) firstly");
103        }
104
105        protected String relativeFilePath(String md5, String suffix){
106                // md5前两位做第一级子目录,接下来的两位做第二级子目录
107                String s = (suffix == null || suffix.length() == 0) ? "" : "." + suffix;
108                Path p = Paths.get(ORIGIN_ROOT_NAME, md5.substring(0, 2),md5.substring(2, 4),md5 + s);
109                return  p.toString();
110        }
111        protected Path relativeFolderPath(String md5){
112                // md5前两位做第一级子目录,接下来的两位做第二级子目录
113                return Paths.get(ORIGIN_ROOT_NAME, md5.substring(0, 2),md5.substring(2, 4));
114        }
115        private File createDestFile(String md5, String suffix){
116                return new File(getStoreRoot(),relativeFilePath(md5, suffix));
117        }
118
119        @Override
120        protected URLStreamHandler doGetURLStreamHandler() {
121                return handler;
122        }
123        
124        @Override
125        public int hashCode() {
126                final int prime = 31;
127                int result = super.hashCode();
128                result = prime * result + ((getProtocol() == null) ? 0 : getProtocol().hashCode());
129                result = prime * result + ((storeRoot == null) ? 0 : storeRoot.hashCode());
130                return result;
131        }
132
133        @Override
134        public boolean equals(Object obj) {
135                if (this == obj)
136                        return true;
137                if (!super.equals(obj))
138                        return false;
139                if (!(obj instanceof LocalBinaryStore))
140                        return false;
141                LocalBinaryStore other = (LocalBinaryStore) obj;
142                if (getProtocol() == null) {
143                        if (other.getProtocol() != null)
144                                return false;
145                } else if (!getProtocol().equals(other.getProtocol()))
146                        return false;
147                if (storeRoot == null) {
148                        if (other.storeRoot != null)
149                                return false;
150                } else if (!storeRoot.equals(other.storeRoot))
151                        return false;
152                return true;
153        }
154
155        @Override
156        public String toString() {
157                StringBuilder builder = new StringBuilder();
158                builder.append("LocalBinaryStore [protocol=");
159                builder.append(getProtocol());
160                builder.append(",storeRoot=");
161                builder.append(storeRoot);
162                builder.append("]");
163                return builder.toString();
164        }
165
166        private class Handler extends URLStreamHandler {
167                @Override
168                protected URLConnection openConnection(URL u) throws IOException {
169                        return new FileConnection(u);
170                }
171        }
172        
173        private class FileConnection extends URLConnection{
174
175                protected FileConnection(URL url) {
176                        super(url);
177                }
178
179                @Override
180                public void connect() throws IOException {
181                        connected = true;
182                }
183
184                @Override
185                public InputStream getInputStream() throws IOException {
186                        connect();
187                        try {
188                                return new FileInputStream(pathOf(url).toFile());
189                        } catch (FileNotFoundException e) {
190                                throw new DataNotFoundException(url,e);
191                        }
192                }
193                
194        }
195}