001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.q2;
020
021import org.jdom2.Element;
022import org.jpos.core.Configurable;
023import org.jpos.core.Configuration;
024import org.jpos.core.ConfigurationException;
025import org.jpos.util.*;
026
027import java.beans.BeanInfo;
028import java.beans.Introspector;
029import java.beans.PropertyDescriptor;
030import java.io.ByteArrayOutputStream;
031import java.io.Closeable;
032import java.io.PrintStream;
033import java.lang.reflect.Method;
034import java.net.URL;
035import java.util.Iterator;
036import java.util.concurrent.ScheduledThreadPoolExecutor;
037
038/**
039 * @author <a href="mailto:taherkordy@dpi2.dpi.net.ir">Alireza Taherkordi</a>
040 * @author <a href="mailto:apr@cs.com.uy">Alejandro P. Revilla</a>
041 */
042public class QBeanSupport
043    implements QBean, QPersist, QBeanSupportMBean, Configurable
044{
045    Element persist;
046    int state;
047    Q2 server;
048    final Object modifyLock = new Object();
049    boolean modified;
050    String name;
051    protected Log log;
052    protected Configuration cfg;
053    protected ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
054
055    public QBeanSupport () {
056        super();
057        setLogger (Q2.LOGGER_NAME);
058        state = -1;
059    }
060
061    @Override
062    public void setServer (Q2 server) {
063        this.server = server;
064    }
065
066    @Override
067    public Q2 getServer () {
068        return server;
069    }
070    public QFactory getFactory () {
071        return getServer().getFactory ();
072    }
073
074    @Override
075    public void setName (String name) {
076        if (this.name == null)
077            this.name = name;
078        if (log != null)
079            log.setRealm (name);
080        setModified (true);
081    }
082
083    @Override
084    public void setLogger (String loggerName) {
085        log = Log.getLog (loggerName, getClass().getName());
086        setModified (true);
087    }
088
089    @Override
090    public void setRealm (String realm) {
091        if (log != null)
092            log.setRealm (realm);
093    }
094
095    @Override
096    public String getRealm() {
097        return log != null ? log.getRealm() : null;
098    }
099
100    @Override
101    public String getLogger () {
102        return log != null ? log.getLogger().getName() : null;
103    }
104
105    public Log getLog () {
106        return log;
107    }
108
109    @Override
110    public String getName () {
111        return name;
112    }
113
114    @Override
115    public void init () {
116        if (state == -1) {
117            setModified (false);
118            try {
119                initService();
120                state = QBean.STOPPED;
121            } catch (Throwable t) {
122                log.warn ("init", t);
123            }
124        }
125    }
126
127    @Override
128    public synchronized void start() {
129        if (state != QBean.DESTROYED &&
130            state != QBean.STOPPED   &&
131            state != QBean.FAILED)
132           return;
133
134        this.state = QBean.STARTING;
135
136        try {
137           startService();
138        } catch (Throwable t) {
139           state = QBean.FAILED;
140           log.warn ("start", t);
141           return;
142        }
143        state = QBean.STARTED;
144    }
145
146    @Override
147    public synchronized void stop () {
148        if (state != QBean.STARTED)
149           return;
150        state = QBean.STOPPING;
151        try {
152           stopService();
153        } catch (Throwable t) {
154           state = QBean.FAILED;
155           log.warn ("stop", t);
156           return;
157        }
158        state = QBean.STOPPED;
159    }
160
161    @Override
162    public void destroy () {
163        if (state == QBean.DESTROYED)
164           return;
165        if (state != QBean.STOPPED)
166           stop();
167
168        if (scheduledThreadPoolExecutor != null) {
169            scheduledThreadPoolExecutor.shutdown();
170            scheduledThreadPoolExecutor = null;
171        }
172        try {
173           destroyService();
174        }
175        catch (Throwable t) {
176           log.warn ("destroy", t);
177        }
178        state = QBean.DESTROYED;
179    }
180
181    @Override
182    public int getState () {
183        return state;
184    }
185
186    @Override
187    public URL[] getLoaderURLS() {
188        return server.getLoader().getURLs();
189    }
190
191    @Override
192    public QClassLoader getLoader() {
193        return server.getLoader();
194    }
195
196    @Override
197    public String getStateAsString () {
198        return state >= 0 ? stateString[state] : "Unknown";
199    }
200
201    public void setState (int state) {
202        this.state = state;
203    }
204
205    @Override
206    public void setPersist (Element persist) {
207        this.persist = persist ;
208    }
209
210    @Override
211    public Element getPersist () {
212        setModified (false);
213        return persist;
214    }
215
216    public void setModified (boolean modified) {
217        synchronized (this.modifyLock) {
218            this.modified = modified;
219        }
220    }
221
222    @Override
223    public boolean isModified () {
224        synchronized (this.modifyLock) {
225            return modified;
226        }
227    }
228
229    public boolean running () {
230        return state == QBean.STARTING || state == QBean.STARTED;
231    }
232
233    @Override
234    public void setConfiguration (Configuration cfg)
235      throws ConfigurationException
236    {
237        this.cfg = cfg;
238    }
239    public Configuration getConfiguration () {
240        return cfg;
241    }
242
243    public String getDump () {
244        if (this instanceof Loggeable) {
245            ByteArrayOutputStream baos = new ByteArrayOutputStream();
246            PrintStream p = new PrintStream(baos);
247            ((Loggeable)this).dump(p, "");
248            return baos.toString();
249        }
250        return toString();
251    }
252    protected void initService()    throws Exception {}
253    protected void startService()   throws Exception {}
254    protected void stopService()    throws Exception {}
255    protected void destroyService() throws Exception {}
256
257    protected synchronized ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor() {
258        if (scheduledThreadPoolExecutor == null)
259            scheduledThreadPoolExecutor = ConcurrentUtil.newScheduledThreadPoolExecutor();
260        return scheduledThreadPoolExecutor;
261    }
262
263    protected Element createElement (String name, Class mbeanClass) {
264        Element e = new Element (name);
265        Element classPath = persist != null ?
266            persist.getChild ("classpath") : null;
267        if (classPath != null)
268            e.addContent (classPath);
269        e.setAttribute ("class", getClass().getName());
270        if (!e.getName().equals (getName ()))
271            e.setAttribute ("name", getName());
272        String loggerName = getLogger();
273        if (loggerName != null)
274            e.setAttribute ("logger", loggerName);
275
276        try {
277            BeanInfo info = Introspector.getBeanInfo (mbeanClass);
278            PropertyDescriptor[] desc = info.getPropertyDescriptors();
279            for (PropertyDescriptor aDesc : desc) {
280                if (aDesc.getWriteMethod() != null) {
281                    Method read = aDesc.getReadMethod();
282                    Object obj = read.invoke(this);
283                    String type = read.getReturnType().getName();
284                    if ("java.lang.String".equals(type))
285                        type = null;
286
287                    addAttr(e, aDesc.getName(), obj, type);
288                }
289            }
290        } catch (Exception ex) {
291            log.warn ("get-persist", ex);
292        }
293        return e;
294    }
295    protected void addAttr (Element e, String name, Object obj, String type) {
296        String value = obj == null ? "null" : obj.toString();
297        Element attr = new Element ("attr");
298        attr.setAttribute ("name", name);
299        if (type != null)
300            attr.setAttribute ("type", type);
301        attr.setText (value);
302        e.addContent (attr);
303    }
304    protected Iterator getAttrs () {
305        return getPersist().getChildren ("attr").iterator();
306    }
307    protected Iterator getAttrs (String parent) {
308        return getPersist().getChild(parent).
309            getChildren("attr").iterator();
310    }
311    protected void setAttr (Iterator attrs, String name, Object obj) {
312        String value = obj == null ? "null" : obj.toString ();
313        while (attrs.hasNext ()) {
314            Element e = (Element) attrs.next ();
315            if (name.equals (e.getAttributeValue ("name")))  {
316                e.setText(value);
317                break;
318            }
319        }
320    }
321    protected Iterator getProperties (String parent) {
322        return getPersist().getChild (parent).
323               getChildren ("property").iterator();
324    }
325    protected void setProperty (Iterator props, String name, String value) {
326        while (props.hasNext()) {
327            Element e = (Element) props.next();
328            if (name.equals (e.getAttributeValue ("name"))) {
329                e.setAttribute ("value", value);
330                break;
331            }
332        }
333    }
334    protected String getProperty (Iterator props, String name) {
335        while (props.hasNext()) {
336            Element e = (Element) props.next();
337            if (name.equals (e.getAttributeValue ("name"))) {
338                return e.getAttributeValue ("value");
339            }
340        }
341        return null;
342    }
343    protected void close (Closeable... closeables) {
344        LogEvent evt = null;
345        for (Closeable c : closeables) {
346            try {
347                c.close();
348            } catch (Exception e) {
349                if (evt == null)
350                    evt = getLog().createWarn();
351                evt.addMessage(e);
352            }
353        }
354        if (evt != null)
355            Logger.log(evt);
356    }
357}