001/*
002 * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl-2.1.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu, jcarsique
016 */
017package org.nuxeo.connect.update.standalone;
018
019import java.io.File;
020import java.io.FileInputStream;
021import java.io.IOException;
022import java.io.InputStream;
023import java.nio.file.attribute.FileTime;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.zip.ZipEntry;
029import java.util.zip.ZipFile;
030
031import org.nuxeo.common.Environment;
032import org.nuxeo.common.xmap.Context;
033import org.nuxeo.common.xmap.XMap;
034import org.nuxeo.common.xmap.XValueFactory;
035import org.nuxeo.connect.update.LocalPackage;
036import org.nuxeo.connect.update.PackageDependency;
037import org.nuxeo.connect.update.PackageException;
038import org.nuxeo.connect.update.PackageState;
039import org.nuxeo.connect.update.PackageType;
040import org.nuxeo.connect.update.PackageUpdateService;
041import org.nuxeo.connect.update.Version;
042import org.nuxeo.connect.update.model.PackageDefinition;
043import org.nuxeo.connect.update.task.Command;
044import org.nuxeo.connect.update.task.standalone.InstallTask;
045import org.nuxeo.connect.update.task.standalone.UninstallTask;
046import org.nuxeo.connect.update.task.standalone.commands.Append;
047import org.nuxeo.connect.update.task.standalone.commands.Config;
048import org.nuxeo.connect.update.task.standalone.commands.Copy;
049import org.nuxeo.connect.update.task.standalone.commands.Delete;
050import org.nuxeo.connect.update.task.standalone.commands.DeployConfigPlaceholder;
051import org.nuxeo.connect.update.task.standalone.commands.DeployPlaceholder;
052import org.nuxeo.connect.update.task.standalone.commands.FlushCoreCachePlaceholder;
053import org.nuxeo.connect.update.task.standalone.commands.FlushJaasCachePlaceholder;
054import org.nuxeo.connect.update.task.standalone.commands.FlushPlaceholder;
055import org.nuxeo.connect.update.task.standalone.commands.InstallPlaceholder;
056import org.nuxeo.connect.update.task.standalone.commands.LoadJarPlaceholder;
057import org.nuxeo.connect.update.task.standalone.commands.ParameterizedCopy;
058import org.nuxeo.connect.update.task.standalone.commands.ReloadPropertiesPlaceholder;
059import org.nuxeo.connect.update.task.standalone.commands.UnAppend;
060import org.nuxeo.connect.update.task.standalone.commands.UndeployConfigPlaceholder;
061import org.nuxeo.connect.update.task.standalone.commands.UndeployPlaceholder;
062import org.nuxeo.connect.update.task.standalone.commands.UninstallPlaceholder;
063import org.nuxeo.connect.update.task.standalone.commands.UnloadJarPlaceholder;
064import org.nuxeo.connect.update.task.update.Rollback;
065import org.nuxeo.connect.update.task.update.Update;
066import org.nuxeo.connect.update.xml.FormsDefinition;
067import org.nuxeo.connect.update.xml.PackageDefinitionImpl;
068
069/**
070 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
071 */
072public class StandaloneUpdateService implements PackageUpdateService {
073
074    protected static XMap xmap;
075
076    protected PackagePersistence persistence;
077
078    protected Map<String, Class<? extends Command>> commands;
079
080    public static XMap getXmap() {
081        return xmap;
082    }
083
084    public StandaloneUpdateService(Environment env) throws IOException {
085        // TODO NXP-9086: Add some checks on the environment
086        Environment.setDefault(env);
087        persistence = new PackagePersistence(this);
088        commands = new HashMap<>();
089    }
090
091    @Override
092    public File getDataDir() {
093        return persistence.getRoot();
094    }
095
096    public PackagePersistence getPersistence() {
097        return persistence;
098    }
099
100    @Override
101    public LocalPackage addPackage(File file) throws PackageException {
102        return persistence.addPackage(file);
103    }
104
105    @Override
106    public void removePackage(String id) throws PackageException {
107        persistence.removePackage(id);
108    }
109
110    @Override
111    public LocalPackage getPackage(String id) throws PackageException {
112        return persistence.getPackage(id);
113    }
114
115    @Override
116    public List<LocalPackage> getPackages() throws PackageException {
117        return persistence.getPackages();
118    }
119
120    public static XMap createXmap() {
121        @SuppressWarnings("hiding")
122        XMap xmap = new XMap();
123        xmap.setValueFactory(PackageType.class, new XValueFactory() {
124            @Override
125            public String serialize(Context arg0, Object arg1) {
126                return ((PackageType) arg1).getValue();
127            }
128
129            @Override
130            public Object deserialize(Context arg0, String arg1) {
131                return PackageType.getByValue(arg1);
132            }
133        });
134        xmap.setValueFactory(Version.class, new XValueFactory() {
135            @Override
136            public String serialize(Context arg0, Object arg1) {
137                return arg1.toString();
138            }
139
140            @Override
141            public Object deserialize(Context arg0, String arg1) {
142                return new Version(arg1);
143            }
144        });
145        xmap.setValueFactory(PackageDependency.class, new XValueFactory() {
146
147            @Override
148            public String serialize(Context arg0, Object arg1) {
149                return arg1.toString();
150            }
151
152            @Override
153            public Object deserialize(Context arg0, String arg1) {
154                return new PackageDependency(arg1);
155            }
156        });
157        xmap.register(PackageDefinitionImpl.class);
158        xmap.register(FormsDefinition.class);
159        return xmap;
160    }
161
162    @Override
163    public void initialize() throws PackageException {
164        xmap = createXmap();
165        addCommands();
166        startInstalledPackages();
167    }
168
169    protected void addCommands() {
170        addCommand(Copy.ID, Copy.class);
171        addCommand(Append.ID, Append.class);
172        addCommand(UnAppend.ID, UnAppend.class);
173        addCommand(ParameterizedCopy.ID, ParameterizedCopy.class);
174        addCommand(Delete.ID, Delete.class);
175        addCommand(InstallPlaceholder.ID, InstallPlaceholder.class);
176        addCommand(UninstallPlaceholder.ID, UninstallPlaceholder.class);
177        addCommand(FlushCoreCachePlaceholder.ID, FlushCoreCachePlaceholder.class);
178        addCommand(FlushJaasCachePlaceholder.ID, FlushJaasCachePlaceholder.class);
179        addCommand(FlushPlaceholder.ID, FlushPlaceholder.class);
180        addCommand(ReloadPropertiesPlaceholder.ID, ReloadPropertiesPlaceholder.class);
181        addCommand(DeployPlaceholder.ID, DeployPlaceholder.class);
182        addCommand(UndeployPlaceholder.ID, UndeployPlaceholder.class);
183        addCommand(DeployConfigPlaceholder.ID, DeployConfigPlaceholder.class);
184        addCommand(UndeployConfigPlaceholder.ID, UndeployConfigPlaceholder.class);
185        addCommand(LoadJarPlaceholder.ID, LoadJarPlaceholder.class);
186        addCommand(UnloadJarPlaceholder.ID, UnloadJarPlaceholder.class);
187        addCommand(Config.ID, Config.class);
188        addCommand(Update.ID, Update.class);
189        addCommand(Rollback.ID, Rollback.class);
190    }
191
192    @Deprecated
193    @Override
194    public void setPackageState(LocalPackage pkg, int state) throws PackageException {
195        persistence.updateState(pkg.getId(), state);
196        pkg.setState(state);
197    }
198
199    @Override
200    public void setPackageState(LocalPackage pkg, PackageState state) throws PackageException {
201        persistence.updateState(pkg.getId(), state);
202        pkg.setState(state);
203    }
204
205    @Override
206    public void shutdown() throws PackageException {
207        xmap = null;
208    }
209
210    @Override
211    public Command getCommand(String id) throws PackageException {
212        Class<? extends Command> type = commands.get(id);
213        if (type != null) {
214            try {
215                return type.getConstructor().newInstance();
216            } catch (ReflectiveOperationException e) {
217                throw new PackageException("Failed to load command " + id, e);
218            }
219        }
220        return null;
221    }
222
223    @Override
224    public String getDefaultInstallTaskType() {
225        return InstallTask.class.getName();
226    }
227
228    @Override
229    public String getDefaultUninstallTaskType() {
230        return UninstallTask.class.getName();
231    }
232
233    public void addCommand(String id, Class<? extends Command> cmd) {
234        commands.put(id, cmd);
235    }
236
237    public void removeCommand(String id) {
238        commands.remove(id);
239    }
240
241    @Override
242    public LocalPackage getActivePackage(String name) throws PackageException {
243        return persistence.getActivePackage(name);
244    }
245
246    @Override
247    public PackageDefinition loadPackageFromZip(File file) throws PackageException {
248        ZipFile zip = null;
249        try {
250            zip = new ZipFile(file);
251            ZipEntry mfEntry = zip.getEntry(LocalPackage.MANIFEST);
252            InputStream mfStream = zip.getInputStream(mfEntry);
253            return loadPackage(mfStream);
254        } catch (PackageException e) {
255            throw e;
256        } catch (IOException e) {
257            throw new PackageException("Failed to load package definition from zip file: " + file, e);
258        } finally {
259            if (zip != null) {
260                try {
261                    zip.close();
262                } catch (IOException e) {
263                    throw new PackageException("Failed to close package zip: " + file, e);
264                }
265            }
266        }
267    }
268
269    @Override
270    public PackageDefinition loadPackage(File file) throws PackageException {
271        FileInputStream in = null;
272        try {
273            in = new FileInputStream(file);
274            return loadPackage(in);
275        } catch (PackageException e) {
276            throw e;
277        } catch (IOException e) {
278            throw new PackageException("Failed to load XML package definition from file: " + file, e);
279        } finally {
280            try {
281                if (in != null) {
282                    in.close();
283                }
284            } catch (IOException e) {
285                throw new PackageException("Failed to close input stream for " + file, e);
286            }
287        }
288    }
289
290    @Override
291    public PackageDefinition loadPackage(InputStream in) throws PackageException {
292        try {
293            return (PackageDefinition) xmap.load(in);
294        } catch (IOException e) {
295            throw new PackageException("Failed to parse XML package definition", e);
296        }
297    }
298
299    protected void startInstalledPackages() throws PackageException {
300        for (Entry<String, PackageState> entry : persistence.getStates().entrySet()) {
301            if (entry.getValue() == PackageState.INSTALLED) {
302                persistence.updateState(entry.getKey(), PackageState.STARTED);
303            }
304        }
305    }
306
307    @Override
308    public void reset() throws PackageException {
309        persistence.reset();
310    }
311
312    @Override
313    public void restart() throws PackageException {
314        throw new UnsupportedOperationException();
315    }
316
317    @Override
318    public boolean isStarted(String pkgId) {
319        return persistence.getState(pkgId) == PackageState.STARTED;
320    }
321
322    @Override
323    public File getRegistry() {
324        return new File(getDataDir(), "registry.xml");
325    }
326
327    @Override
328    public File getBackupDir() {
329        return new File(getDataDir(), "backup");
330    }
331
332    @Override
333    public FileTime getInstallDate(String id) {
334        return getPersistence().getInstallDate(id);
335    }
336
337}