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 */
017package org.apache.commons.transaction.file;
018
019import java.io.ByteArrayInputStream;
020import java.io.InputStream;
021import java.io.OutputStream;
022
023import 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 */
032public 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}