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