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