View | Details | Raw Unified | Return to bug 48674
Collapse All | Expand All

(-)java/org/apache/catalina/manager/host/HostXMLUpdater.java (+574 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 * 
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 * 
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
package org.apache.catalina.manager.host;
19
20
import java.io.File;
21
import java.io.FileInputStream;
22
import java.io.FileOutputStream;
23
import java.io.IOException;
24
import java.nio.channels.FileChannel;
25
import java.util.Date;
26
27
import javax.xml.parsers.DocumentBuilder;
28
import javax.xml.parsers.DocumentBuilderFactory;
29
import javax.xml.parsers.ParserConfigurationException;
30
import javax.xml.transform.OutputKeys;
31
import javax.xml.transform.Result;
32
import javax.xml.transform.Source;
33
import javax.xml.transform.Transformer;
34
import javax.xml.transform.TransformerConfigurationException;
35
import javax.xml.transform.TransformerException;
36
import javax.xml.transform.TransformerFactory;
37
import javax.xml.transform.TransformerFactoryConfigurationError;
38
import javax.xml.transform.dom.DOMSource;
39
import javax.xml.transform.stream.StreamResult;
40
41
import org.apache.catalina.Engine;
42
import org.apache.catalina.Host;
43
import org.apache.catalina.Service;
44
import org.apache.catalina.core.StandardHost;
45
import org.apache.catalina.startup.Catalina;
46
import org.w3c.dom.Comment;
47
import org.w3c.dom.Document;
48
import org.w3c.dom.Element;
49
import org.w3c.dom.NodeList;
50
import org.xml.sax.SAXException;
51
52
/**
53
 * Responsible for adding and removing hosts from server.xml.
54
 * 
55
 * @author Wesley
56
 * 
57
 */
58
public class HostXMLUpdater {
59
60
    /**
61
     * The directory where tomcat resides.
62
     */
63
    private String baseFileName;
64
    /**
65
     * The name of the configuration file that has the hosts defined in it. This
66
     * is usually conf/server.xml.
67
     */
68
    private String confFileName;
69
    /**
70
     * The name of the file where the contents will be initially written.
71
     */
72
    private String tempFileName;
73
    /**
74
     * The name of the file which will contain a backup of the old settings.
75
     */
76
    private String backupFileName;
77
78
    public HostXMLUpdater() {
79
        init();
80
    }
81
82
    synchronized private void init() {
83
        confFileName = System.getProperty(Catalina.CONFIG_FILE_PROPERTY);
84
        baseFileName = System.getProperty("catalina.base");
85
    }
86
87
    /**
88
     * Saves a host to the xml configuration file.
89
     * 
90
     * @param host
91
     *            The host to save as xml.
92
     */
93
    public void addHost(StandardHost host) {
94
95
        try {
96
            FileReferences ref = createBackupAndTempFiles();
97
98
            Document document = createDocumentFromFile(ref.getTempFile());
99
100
            HostParents hostParents = findParents(host);
101
            Element engineNode = findHostParentNode(hostParents, document);
102
            Element hostNode = createHostNode(document, host);
103
            engineNode.appendChild(hostNode);
104
            // Following is debug code
105
            Transformer aTransformer = createTransformer();
106
            writeToTempFile(ref, document, aTransformer);
107
            replaceCurrentConfFileWithTempFile(ref);
108
        } catch (Exception e) {
109
            // FIXME Need to decide what to do on a failure.
110
            e.printStackTrace();
111
        } finally {
112
            // FIXME cleanup files
113
        }
114
115
    }
116
117
    /**
118
     * Deletes the current file and replaces it with the temp file.
119
     * 
120
     * @param ref
121
     *            References to the temp and current files.
122
     */
123
    private void replaceCurrentConfFileWithTempFile(FileReferences ref) {
124
        ref.currentFile.delete();
125
126
        ref.tempFile.renameTo(ref.currentFile);
127
    }
128
129
    /**
130
     * Writes output to tempory file.
131
     * 
132
     * @param ref
133
     *            A reference to the current and temp and backup files.
134
     * @param document
135
     *            The document to be written to the temp file.
136
     * @param transformer
137
     *            The transformer which writes to the temp file.
138
     * @throws TransformerException
139
     *             If an unrecoverable error occurs during the course of the
140
     *             transformation.
141
     */
142
    private void writeToTempFile(FileReferences ref, Document document,
143
            Transformer transformer) throws TransformerException {
144
        Source src = new DOMSource(document);
145
        Result dest = new StreamResult(ref.tempFile);
146
        transformer.transform(src, dest);
147
    }
148
149
    /**
150
     * @return A Transformer to transform the document to file.
151
     * @throws TransformerFactoryConfigurationError
152
     *             Thrown if the TransformerFactory implementation is not
153
     *             available or cannot be instantiated.
154
     * @throws TransformerConfigurationException
155
     *             When it is not possible to create a transformer.
156
     */
157
    private Transformer createTransformer()
158
            throws TransformerFactoryConfigurationError,
159
            TransformerConfigurationException {
160
        TransformerFactory tranFactory = TransformerFactory.newInstance();
161
        Transformer transformer = tranFactory.newTransformer();
162
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
163
        return transformer;
164
    }
165
166
    /**
167
     * Removes a host from the configuration file.
168
     * 
169
     * @param host
170
     *            The host to remove.
171
     */
172
    public void removeHost(Host host) {
173
174
        try {
175
            FileReferences ref = createBackupAndTempFiles();
176
177
            Document document = createDocumentFromFile(ref.getTempFile());
178
179
            HostParents hostParents = findParents(host);
180
            Element engineNode = findHostParentNode(hostParents, document);
181
            deleteHostNode(document, engineNode, host);
182
            Transformer aTransformer = createTransformer();
183
            writeToTempFile(ref, document, aTransformer);
184
            replaceCurrentConfFileWithTempFile(ref);
185
        } catch (Exception e) {
186
            e.printStackTrace();
187
        } finally {
188
            // FIXME cleanup files
189
        }
190
191
    }
192
193
    /**
194
     * Finds a host by name and removes it from a document.
195
     * 
196
     * @param document
197
     *            The document to remove the host from.
198
     * @param engineNode
199
     *            The node to remove the host from.
200
     * @param hostThe
201
     *            host to remove.
202
     */
203
    private void deleteHostNode(Document document, Element engineNode, Host host) {
204
        NodeList hostNodes = engineNode.getElementsByTagName("Host");
205
        for (int i = 0; i < hostNodes.getLength(); i++) {
206
            Element currentElement = (Element) hostNodes.item(i);
207
            String name = currentElement.getAttribute("name");
208
            if (name.equals(host.getName())) {
209
                engineNode.removeChild(currentElement);
210
            }
211
        }
212
213
    }
214
215
    /**
216
     * Creates a host node.
217
     * 
218
     * @param document
219
     *            The document to create the node on.
220
     * @param host
221
     *            The host to represent as XML.
222
     * @return A newly created XML element representing the host.
223
     */
224
    private Element createHostNode(Document document, StandardHost host) {
225
        Element hostNode = document.createElement("Host");
226
227
        hostNode.setAttribute("name", host.getName());
228
229
        hostNode.setAttribute("appBase", host.getAppBase());
230
        hostNode.setAttribute("autoDeploy", Boolean.toString(host
231
                .getAutoDeploy()));
232
        hostNode.setAttribute("deployOnStartup", Boolean.toString(host
233
                .getDeployOnStartup()));
234
        hostNode
235
                .setAttribute("deployXML", Boolean.toString(host.isDeployXML()));
236
        String[] aliases = host.getAliases();
237
238
        Comment creationComment = document
239
                .createComment("Automatically created host on "
240
                        + (new Date().toString()));
241
        hostNode.appendChild(creationComment);
242
        for (int i = 0; i < aliases.length; i++) {
243
            Element alias = document.createElement("Alias");
244
            alias.setTextContent(aliases[i]);
245
            hostNode.appendChild(alias);
246
        }
247
        return hostNode;
248
    }
249
250
    /**
251
     * Finds the parent nodes of a host node.
252
     * 
253
     * First finds the service by name then the engine.
254
     * 
255
     * @param hostParents
256
     *            The parents of the node to find.
257
     * @param document
258
     *            The document to find the XML representation of the parents on.
259
     * @return An element representing the engine the host is attached to.
260
     */
261
    private Element findHostParentNode(HostParents hostParents,
262
            Document document) {
263
        try {
264
            NodeList serviceNodeList = document.getElementsByTagName("Service");
265
            Element serviceNode = (Element) serviceNodeList.item(0);
266
            for (int i = 0; i < serviceNodeList.getLength(); i++) {
267
                serviceNode = (Element) serviceNodeList.item(i);
268
                String name = serviceNode.getAttribute("name");
269
                if (name.equals(hostParents.getService().getName())) {
270
                    break;
271
                }
272
            }
273
            NodeList engineNodeList = serviceNode
274
                    .getElementsByTagName("Engine");
275
            if (engineNodeList.getLength() != 1) {
276
                throw new ServiceParseException(
277
                        "Unable to parse server.xml. There should only be one engine associated with a given service.");
278
            }
279
            return ((Element) engineNodeList.item(0));
280
        } catch (RuntimeException e) {
281
            throw new ServiceParseException(e);
282
        }
283
    }
284
285
    /**
286
     * Finds the service and engine associated with a host.
287
     * 
288
     * @param host
289
     *            The host which we need too find the parents of.
290
     * @return An object representing the service and the engine that parent the
291
     *         host.
292
     */
293
    private HostParents findParents(Host host) {
294
        Engine engine = (Engine) host.getParent();
295
296
        Service service = engine.getService();
297
        HostParents parents = new HostParents();
298
        parents.setEngine(engine);
299
        parents.setService(service);
300
        return parents;
301
    }
302
303
    private FileReferences createBackupAndTempFiles() throws IOException {
304
        FileReferences ref = createFileRefrences();
305
306
        copyFile(ref.getCurrentFile(), ref.getBackupFile());
307
        copyFile(ref.getCurrentFile(), ref.getTempFile());
308
        return ref;
309
    }
310
311
    /**
312
     * Creates a document from a file.
313
     * 
314
     * @param file
315
     *            The file to parse to create a document.
316
     * @return The document instance that is within the file.
317
     * @throws ParserConfigurationException
318
     *             if a DocumentBuilder cannot be created which satisfies the
319
     *             configuration requested.
320
     * @throws SAXException
321
     *             If there is an error parsing.
322
     * @throws IOException
323
     *             If there is any IO errors.
324
     */
325
    private Document createDocumentFromFile(File file)
326
            throws ParserConfigurationException, SAXException, IOException {
327
        DocumentBuilderFactory builderFactory = DocumentBuilderFactory
328
                .newInstance();
329
        DocumentBuilder parser = builderFactory.newDocumentBuilder();
330
        Document document = parser.parse(file);
331
        return document;
332
    }
333
334
    /**
335
     * Creates references to the current configuration file, the backup file and
336
     * a temporary file.
337
     * 
338
     * @return A reference to the temporary configuration file, the backup file
339
     *         and the current configuration file.
340
     */
341
    private FileReferences createFileRefrences() {
342
        File currentFile = createFileReference(confFileName);
343
        // Make two copies of the current file.
344
        String tempFileName = confFileName + ".tmp";
345
        File tempFile = createFileReference(tempFileName);
346
347
        String backupFileName = confFileName + ".bak";
348
        File backupFile = createFileReference(backupFileName);
349
        FileReferences ref = new FileReferences();
350
        ref.setBackupFile(backupFile);
351
        ref.setCurrentFile(currentFile);
352
        ref.setTempFile(tempFile);
353
        return ref;
354
    }
355
356
    /**
357
     * Creates a file reference representing the filename passed in. Creates all
358
     * directories that are needed to create the file.
359
     * 
360
     * @param fileName
361
     * @return
362
     */
363
    private File createFileReference(String fileName) {
364
        File currentFile = new File(fileName);
365
        if (!currentFile.isAbsolute()) {
366
            currentFile = new File(baseFileName, fileName);
367
        }
368
        currentFile.getParentFile().mkdirs();
369
        return currentFile;
370
    }
371
372
    /**
373
     * @param confFileName
374
     *            The name of the current configuration file. Usually
375
     *            "conf/server.xml".
376
     */
377
    public void setConfFileName(String confFileName) {
378
        this.confFileName = confFileName;
379
    }
380
381
    /**
382
     * @return The name of the current configuration file. Usually
383
     *         "conf/server.xml".
384
     */
385
    public String getConfFileName() {
386
        return confFileName;
387
    }
388
389
    /**
390
     * @param tempFileName
391
     *            The file where the temporary contents will be written before
392
     *            Overwriting the main configuration file.
393
     */
394
    public void setTempFileName(String tempFileName) {
395
        this.tempFileName = tempFileName;
396
    }
397
398
    /**
399
     * 
400
     * @return The file where the temporary contents will be written before
401
     *         Overwriting the main configuration file.
402
     */
403
    public String getTempFileName() {
404
        return tempFileName;
405
    }
406
407
    /**
408
     * @param backupFileName
409
     *            The name of the backup file.
410
     */
411
    public void setBackupFileName(String backupFileName) {
412
        this.backupFileName = backupFileName;
413
    }
414
415
    /**
416
     * @return The name of the backup file.
417
     */
418
    public String getBackupFileName() {
419
        return backupFileName;
420
    }
421
422
    /**
423
     * @param baseFileName
424
     *            The location of catalina.base.
425
     */
426
    public void setBaseFileName(String baseFileName) {
427
        this.baseFileName = baseFileName;
428
    }
429
430
    /**
431
     * @return The location of catalina.base.
432
     */
433
    public String getBaseFileName() {
434
        return baseFileName;
435
    }
436
437
    /**
438
     * Copies a file to another file.
439
     * 
440
     * @param sourceFile
441
     *            The file to copy.
442
     * @param destFile
443
     *            The file to write.
444
     * @throws IOException
445
     *             If sourceFile cannot be read or destFile cannot be written.
446
     * 
447
     * @see http://www.javalobby.org/java/forums/t17036.html
448
     */
449
    void copyFile(File sourceFile, File destFile) throws IOException {
450
        if (!destFile.exists()) {
451
            destFile.createNewFile();
452
        }
453
454
        FileChannel source = null;
455
        FileChannel destination = null;
456
        try {
457
            source = new FileInputStream(sourceFile).getChannel();
458
            destination = new FileOutputStream(destFile).getChannel();
459
            destination.transferFrom(source, 0, source.size());
460
        } finally {
461
            if (source != null) {
462
                source.close();
463
            }
464
        }
465
        if (destination != null) {
466
            destination.close();
467
        }
468
    }
469
470
    /**
471
     * Simply used as a private return value.
472
     * 
473
     * Represents the three files the current file, the backup file and a
474
     * Temporary file.
475
     * 
476
     * @author Wesley
477
     * 
478
     */
479
    private class FileReferences {
480
        private File currentFile;
481
        private File tempFile;
482
        private File backupFile;
483
484
        /**
485
         * Constructor
486
         */
487
        public FileReferences() {
488
        }
489
490
        /**
491
         * @return The current file.
492
         */
493
        public File getCurrentFile() {
494
            return currentFile;
495
        }
496
497
        /**
498
         * @param The current file.
499
         */
500
        public void setCurrentFile(File currentFile) {
501
            this.currentFile = currentFile;
502
        }
503
504
        /**
505
         * @return A temporary file that will be used to update the current file.
506
         */
507
        public File getTempFile() {
508
            return tempFile;
509
        }
510
511
        /**
512
         * @param tempFile A temporary file that will be used to update the current file.
513
         */
514
        public void setTempFile(File tempFile) {
515
            this.tempFile = tempFile;
516
        }
517
518
        /**
519
         * @return A backup file of the current file.
520
         */
521
        public File getBackupFile() {
522
            return backupFile;
523
        }
524
525
        /**
526
         * @param backupFile A backup file of the current file.
527
         */
528
        public void setBackupFile(File backupFile) {
529
            this.backupFile = backupFile;
530
        }
531
    }
532
533
    /**
534
     * Represents the service and engine associated with an individual host.
535
     * @author Wesley
536
     *
537
     */
538
    private class HostParents {
539
540
        private Engine engine;
541
        private Service service;
542
543
        /**
544
         * @return The engine associated with the host.
545
         */
546
        public Engine getEngine() {
547
            return engine;
548
        }
549
550
        /**
551
         * @param engine The engine associated with the host.
552
         */
553
        public void setEngine(Engine engine) {
554
            this.engine = engine;
555
556
        }
557
558
        /**
559
         * @return The service associated with the host.
560
         */
561
        public Service getService() {
562
            return service;
563
        }
564
565
        /**
566
         * @param service The service associated with the host.
567
         */
568
        public void setService(Service service) {
569
            this.service = service;
570
571
        }
572
573
    }
574
}
(-)java/org/apache/catalina/manager/host/HostManagerServlet.java (+8 lines)
Lines 104-109 Link Here
104
    // ----------------------------------------------------- Instance Variables
104
    // ----------------------------------------------------- Instance Variables
105
105
106
106
107
    private static final long serialVersionUID = -3386410077320670918L;
108
109
107
    /**
110
    /**
108
     * Path where context descriptors should be deployed.
111
     * Path where context descriptors should be deployed.
109
     */
112
     */
Lines 151-156 Link Here
151
     * The Wrapper container associated with this servlet.
154
     * The Wrapper container associated with this servlet.
152
     */
155
     */
153
    protected Wrapper wrapper = null;
156
    protected Wrapper wrapper = null;
157
    
158
    protected static HostXMLUpdater xmlUpdater = new HostXMLUpdater();
154
159
155
160
156
    // ----------------------------------------------- ContainerServlet Methods
161
    // ----------------------------------------------- ContainerServlet Methods
Lines 470-475 Link Here
470
        host = (StandardHost) engine.findChild(name);
475
        host = (StandardHost) engine.findChild(name);
471
        if (host != null) {
476
        if (host != null) {
472
            writer.println(sm.getString("hostManagerServlet.add", name));
477
            writer.println(sm.getString("hostManagerServlet.add", name));
478
            xmlUpdater.addHost(host);
473
        } else {
479
        } else {
474
            // Something failed
480
            // Something failed
475
            writer.println(sm.getString("hostManagerServlet.addFailed", name));
481
            writer.println(sm.getString("hostManagerServlet.addFailed", name));
Lines 514-519 Link Here
514
        // Note that the host will not get physically removed
520
        // Note that the host will not get physically removed
515
        try {
521
        try {
516
            Container child = engine.findChild(name);
522
            Container child = engine.findChild(name);
523
            xmlUpdater.removeHost((StandardHost) child);
517
            engine.removeChild(child);
524
            engine.removeChild(child);
518
            if ( child instanceof ContainerBase ) ((ContainerBase)child).destroy();
525
            if ( child instanceof ContainerBase ) ((ContainerBase)child).destroy();
519
        } catch (Exception e) {
526
        } catch (Exception e) {
Lines 525-530 Link Here
525
        Host host = (StandardHost) engine.findChild(name);
532
        Host host = (StandardHost) engine.findChild(name);
526
        if (host == null) {
533
        if (host == null) {
527
            writer.println(sm.getString("hostManagerServlet.remove", name));
534
            writer.println(sm.getString("hostManagerServlet.remove", name));
535
            
528
        } else {
536
        } else {
529
            // Something failed
537
            // Something failed
530
            writer.println(sm.getString("hostManagerServlet.removeFailed", name));
538
            writer.println(sm.getString("hostManagerServlet.removeFailed", name));
(-)java/org/apache/catalina/startup/Catalina.java (-2 / +7 lines)
Lines 65-74 Link Here
65
65
66
public class Catalina extends Embedded {
66
public class Catalina extends Embedded {
67
67
68
    public static final String CONFIG_FILE_PROPERTY = "catalina.configFileProperty";
68
69
69
    // ----------------------------------------------------- Instance Variables
70
    // ----------------------------------------------------- Instance Variables
70
71
71
72
    /**
72
    /**
73
     * Pathname to the server configuration file.
73
     * Pathname to the server configuration file.
74
     */
74
     */
Lines 116-121 Link Here
116
116
117
    public void setConfigFile(String file) {
117
    public void setConfigFile(String file) {
118
        configFile = file;
118
        configFile = file;
119
        
120
        System.setProperty(CONFIG_FILE_PROPERTY, configFile);
119
    }
121
    }
120
122
121
123
Lines 144-151 Link Here
144
        this.parentClassLoader = parentClassLoader;
146
        this.parentClassLoader = parentClassLoader;
145
147
146
    }
148
    }
149
    // ----------------------------------------------------------- Constructor
150
    public Catalina() {
151
        setConfigFile(configFile);
152
    }
147
153
148
149
    // ----------------------------------------------------------- Main Program
154
    // ----------------------------------------------------------- Main Program
150
155
151
    /**
156
    /**
(-)java/org/apache/catalina/manager/host/ServiceUpdateException.java (+46 lines)
Line 0 Link Here
1
/**
2
 * 
3
 */
4
package org.apache.catalina.manager.host;
5
6
/**
7
 * @author Wesley
8
 *
9
 */
10
public class ServiceUpdateException extends RuntimeException {
11
12
    private static final long serialVersionUID = 7282583481793836635L;
13
14
    /**
15
     * @see RuntimeException#RuntimeException()
16
     */
17
    public ServiceUpdateException() {
18
        super();
19
        // TODO Auto-generated constructor stub
20
    }
21
22
    /**
23
     * @see RuntimeException#RuntimeException(String, Throwable)
24
     */
25
    public ServiceUpdateException(String message, Throwable cause) {
26
        super(message, cause);
27
        // TODO Auto-generated constructor stub
28
    }
29
30
    /**
31
     * @see RuntimeException#RuntimeException(String)
32
     */
33
    public ServiceUpdateException(String message) {
34
        super(message);
35
        // TODO Auto-generated constructor stub
36
    }
37
38
    /**
39
     * @see RuntimeException#RuntimeException(Throwable)
40
     */
41
    public ServiceUpdateException(Throwable cause) {
42
        super(cause);
43
        // TODO Auto-generated constructor stub
44
    }
45
46
}
(-)java/org/apache/catalina/manager/host/ServiceParseException.java (+41 lines)
Line 0 Link Here
1
/**
2
 * 
3
 */
4
package org.apache.catalina.manager.host;
5
6
/**
7
 * @author Wesley
8
 *
9
 */
10
public class ServiceParseException extends ServiceUpdateException {
11
12
    private static final long serialVersionUID = -3951000538081277024L;
13
14
    /**
15
     * @see RuntimeException#RuntimeException()
16
     */
17
    public ServiceParseException() {
18
    }
19
20
    /**
21
     * @see RuntimeException#RuntimeException(String)
22
     */
23
    public ServiceParseException(String message) {
24
        super(message);
25
    }
26
27
    /**
28
     * @see RuntimeException#RuntimeException(Throwable)
29
     */
30
    public ServiceParseException(Throwable cause) {
31
        super(cause);
32
    }
33
34
    /**
35
     * @see RuntimeException#RuntimeException(String, Throwable)
36
     */
37
    public ServiceParseException(String message, Throwable cause) {
38
        super(message, cause);
39
    }
40
41
}

Return to bug 48674