001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.transaction.file;
018    
019    import java.io.ByteArrayInputStream;
020    import java.io.InputStream;
021    import java.io.OutputStream;
022    
023    import org.apache.commons.transaction.util.LoggerFacade;
024    
025    /**
026     * A resource manager for streamable objects stored in a file system that
027     * features additional administration commands.
028     * 
029     * @version $Id: FileResourceManager.java 519647 2007-03-18 17:50:02Z
030     *          ozeigermann $
031     */
032    public class VirtualAdminCommandsFileResourceManager extends
033            FileResourceManager implements ResourceManager,
034            ResourceManagerErrorCodes {
035    
036        protected String virtualAdminPath = null;
037    
038        public String getVirtualAdminPath() {
039            return virtualAdminPath;
040        }
041    
042        public void setVirtualAdminPath(String virutalAdminPath) {
043            this.virtualAdminPath = virutalAdminPath;
044        }
045    
046        /**
047         * Creates a new resource manager operation on the specified directories.
048         * 
049         * @param storeDir
050         *            directory where main data should go after commit
051         * @param workDir
052         *            directory where transactions store temporary data
053         * @param urlEncodePath
054         *            if set to <code>true</code> encodes all paths to allow for
055         *            any kind of characters
056         * @param logger
057         *            the logger to be used by this store
058         */
059        public VirtualAdminCommandsFileResourceManager(String storeDir,
060                String workDir, boolean urlEncodePath, LoggerFacade logger) {
061            this(storeDir, workDir, urlEncodePath, logger, false);
062        }
063    
064        /**
065         * Creates a new resource manager operation on the specified directories.
066         * 
067         * @param storeDir
068         *            directory where main data should go after commit
069         * @param workDir
070         *            directory where transactions store temporary data
071         * @param urlEncodePath
072         *            if set to <code>true</code> encodes all paths to allow for
073         *            any kind of characters
074         * @param logger
075         *            the logger to be used by this store
076         * @param debug
077         *            if set to <code>true</code> logs all locking information to
078         *            "transaction.log" for debugging inspection
079         */
080        public VirtualAdminCommandsFileResourceManager(String storeDir,
081                String workDir, boolean urlEncodePath, LoggerFacade logger,
082                boolean debug) {
083            this(storeDir, workDir, urlEncodePath ? new URLEncodeIdMapper() : null,
084                    new NoOpTransactionIdToPathMapper(), logger, debug);
085        }
086    
087        /**
088         * Creates a new resource manager operation on the specified directories.
089         * This constructor is reintroduced for backwards API compatibility and is
090         * used by jakarta-slide.
091         * 
092         * @param storeDir
093         *            directory where main data should go after commit
094         * @param workDir
095         *            directory where transactions store temporary data
096         * @param idMapper
097         *            mapper for resourceId to path
098         * @param logger
099         *            the logger to be used by this store
100         * @param debug
101         *            if set to <code>true</code> logs all locking information to
102         *            "transaction.log" for debugging inspection
103         */
104        public VirtualAdminCommandsFileResourceManager(String storeDir,
105                String workDir, ResourceIdToPathMapper idMapper,
106                LoggerFacade logger, boolean debug) {
107            this(storeDir, workDir, idMapper, new NoOpTransactionIdToPathMapper(),
108                    logger, debug);
109        }
110    
111        /**
112         * Creates a new resource manager operation on the specified directories.
113         * 
114         * @param storeDir
115         *            directory where main data should go after commit
116         * @param workDir
117         *            directory where transactions store temporary data
118         * @param idMapper
119         *            mapper for resourceId to path
120         * @param txIdMapper
121         *            mapper for transaction id to path
122         * @param logger
123         *            the logger to be used by this store
124         * @param debug
125         *            if set to <code>true</code> logs all locking information to
126         *            "transaction.log" for debugging inspection
127         */
128        public VirtualAdminCommandsFileResourceManager(String storeDir,
129                String workDir, ResourceIdToPathMapper idMapper,
130                TransactionIdToPathMapper txIdMapper, LoggerFacade logger,
131                boolean debug) {
132            super(workDir, storeDir, idMapper, txIdMapper, logger, debug);
133        }
134    
135        public boolean resourceExists(Object resourceId)
136                throws ResourceManagerException {
137            if (isVirtualAdminId(resourceId)) {
138                logger
139                        .logFine("Faking existence of virtual administration command");
140                return true;
141            }
142    
143            return super.resourceExists(resourceId);
144        }
145    
146        public boolean resourceExists(Object txId, Object resourceId)
147                throws ResourceManagerException {
148            if (isVirtualAdminId(resourceId)) {
149                logger
150                        .logFine("Faking existence of virtual administration command");
151                return true;
152            }
153    
154            return super.resourceExists(txId, resourceId);
155        }
156    
157        public void deleteResource(Object txId, Object resourceId)
158                throws ResourceManagerException {
159    
160            checkForVirtualAdminCommand(resourceId);
161    
162            super.deleteResource(txId, resourceId);
163        }
164    
165        public void deleteResource(Object txId, Object resourceId,
166                boolean assureOnly) throws ResourceManagerException {
167    
168            checkForVirtualAdminCommand(resourceId);
169    
170            super.deleteResource(txId, resourceId, assureOnly);
171        }
172    
173        public void createResource(Object txId, Object resourceId)
174                throws ResourceManagerException {
175    
176            checkForVirtualAdminCommand(resourceId);
177    
178            super.createResource(txId, resourceId);
179        }
180    
181        public void createResource(Object txId, Object resourceId,
182                boolean assureOnly) throws ResourceManagerException {
183    
184            checkForVirtualAdminCommand(resourceId);
185    
186            super.createResource(txId, resourceId, assureOnly);
187        }
188    
189        public void copyResource(Object txId, Object fromResourceId,
190                Object toResourceId, boolean overwrite)
191                throws ResourceManagerException {
192    
193            checkForVirtualAdminCommand(fromResourceId);
194            checkForVirtualAdminCommand(toResourceId);
195    
196            super.copyResource(txId, fromResourceId, toResourceId, overwrite);
197        }
198    
199        public void moveResource(Object txId, Object fromResourceId,
200                Object toResourceId, boolean overwrite)
201                throws ResourceManagerException {
202    
203            checkForVirtualAdminCommand(fromResourceId);
204            checkForVirtualAdminCommand(toResourceId);
205    
206            super.moveResource(txId, fromResourceId, toResourceId, overwrite);
207        }
208    
209        public InputStream readResource(Object resourceId)
210                throws ResourceManagerException {
211    
212            if (isVirtualAdminId(resourceId)) {
213                logger.logWarning("Issuing virtual admin command" + resourceId);
214                return executeAdminCommand(resourceId);
215            }
216    
217            return super.readResource(resourceId);
218        }
219    
220        public InputStream readResource(Object txId, Object resourceId)
221                throws ResourceManagerException {
222    
223            if (isVirtualAdminId(resourceId)) {
224                String message = "You must not call virtual admin commands ("
225                        + resourceId + ") from within transactions!";
226                logger.logSevere(message);
227                throw new ResourceManagerException(message);
228            }
229    
230            return super.readResource(txId, resourceId);
231        }
232    
233        protected void checkForVirtualAdminCommand(Object resourceId)
234                throws ResourceManagerException {
235            if (isVirtualAdminId(resourceId)) {
236                String message = "You must not make modification calls to virtual admin commands ("
237                        + resourceId + ")!";
238                logger.logSevere(message);
239                throw new ResourceManagerException(message);
240            }
241        }
242    
243        protected boolean isVirtualAdminId(Object resourceId) {
244            return (getVirtualAdminPath() != null && resourceId.toString()
245                    .startsWith(getVirtualAdminPath()));
246        }
247    
248        protected InputStream executeAdminCommand(Object resourceId) {
249            StringBuffer sb = new StringBuffer();
250    
251            if (!isVirtualAdminId(resourceId)) {
252                String message = "Internal error: " + resourceId.toString()
253                        + " is no administration command, but is supposed to!";
254                sb.append(message);
255                logger.logSevere(message);
256            } else {
257                String command = resourceId.toString().substring(
258                        getVirtualAdminPath().length());
259                logger.logInfo("Processing admin command " + command);
260    
261                // XXX this really should be more flexible
262                try {
263                    if (isAKnowCommand(command)) {
264                        if (command.equals("recover")) {
265                            recover();
266                        }
267    
268                        String message = "Command " + command
269                                + " terminated successfully";
270                        sb.append(message);
271                        logger.logInfo(message);
272                    } else {
273                        String message = "Command " + command + " unknown";
274                        sb.append(message);
275                        logger.logWarning(message);
276    
277                    }
278                } catch (ResourceManagerSystemException e) {
279                    String message = "Command " + command
280                            + " failed with the following message: "
281                            + e.getMessage();
282                    sb.append(message);
283                    logger.logSevere(message, e);
284                }
285    
286            }
287            ByteArrayInputStream baIs = new ByteArrayInputStream(sb.toString()
288                    .getBytes());
289            return baIs;
290    
291        }
292    
293        protected boolean isAKnowCommand(String command) {
294            return command.equals("recover");
295        }
296    
297        public OutputStream writeResource(Object txId, Object resourceId,
298                boolean append) throws ResourceManagerException {
299    
300            checkForVirtualAdminCommand(resourceId);
301    
302            return super.writeResource(txId, resourceId, append);
303        }
304    }