/*
* Copyright 2009 NCHOVY
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.cron.impl;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Invalidate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Requires;
import org.apache.felix.ipojo.annotations.Validate;
import org.krakenapps.confdb.ConfigService;
import org.krakenapps.cron.CronService;
import org.krakenapps.cron.DuplicatedScheduleException;
import org.krakenapps.cron.Schedule;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
/**
* This class provides implementation for the {@link CronService} interface.
*
* @author periphery
* @since 1.0.0
*/
@Component(name = "cron-service")
@Provides
public class CronServiceImpl implements CronService {
private static BundleContext bundleContext;
/**
* schedule id to Schedule mapping.
*/
private ConcurrentMap<Integer, Schedule> map;
@Requires
private ConfigService conf;
private final CronConfig config;
private final Scheduler scheduler = new Scheduler();
private JobServiceTracker tracker;
public CronServiceImpl(BundleContext context) throws ParseException {
tracker = new JobServiceTracker(context, this);
bundleContext = context;
this.config = new CronConfig(conf);
refreshMap();
}
public CronServiceImpl(BundleContext context, ConfigService conf) throws ParseException {
tracker = new JobServiceTracker(context, this);
bundleContext = context;
this.config = new CronConfig(conf);
refreshMap();
validate();
}
@Validate
public void validate() {
scheduler.start(getMap());
tracker.open();
}
@Invalidate
public void invalidate() {
tracker.close();
scheduler.stop();
}
/**
* register schedule. schedule is saved in db, and added to scheduler.
*/
@Override
public int registerSchedule(Schedule schedule) {
// check duplicated schedule
for (Schedule e : map.values())
if (e.equals(schedule))
throw new DuplicatedScheduleException(schedule);
int id = config.addEntry(schedule);
this.map.put(id, schedule);
scheduler.put(id, schedule);
return id;
}
/**
* unregister schedule. schedule is removed from db, and from scheduler.
*/
@Override
public void unregisterSchedule(int i) {
if (null == this.map.remove(i))
throw new NoSuchElementException();
config.removeEntry(i);
scheduler.remove(i);
}
/**
* load schedules from db.
*
* @throws ParseException
* when data in db is corrupted and unable to parse as schedule.
*/
private void refreshMap() throws ParseException {
this.map = new ConcurrentHashMap<Integer, Schedule>(config.getEntries());
}
private Map<Integer, Schedule> getMap() {
return new HashMap<Integer, Schedule>(this.map);
}
@Override
public Map<Integer, Schedule> getSchedules() {
// it is safe to return because schedule is immutable object
Map<Integer, Schedule> schedules = new TreeMap<Integer, Schedule>();
for (Integer key : map.keySet()) {
Schedule schedule = map.get(key);
schedules.put(key, schedule);
}
return schedules;
}
@Override
public List<String> getJobList() {
return scheduler.getJobList();
}
public static Runnable getRef(String Name) throws InvalidSyntaxException {
return getRef(bundleContext, Name);
}
/**
* get reference of current active Runnable given its instance name.
*
* @param context
* @param instanceName
* instance name of the Runnable
* @return
* @throws InvalidSyntaxException
* when given instance name is not a valid string.
*/
private static Runnable getRef(BundleContext context, String instanceName) throws InvalidSyntaxException {
ServiceReference[] refs = context.getServiceReferences(Runnable.class.getName(), "(instance.name=" + instanceName + ")");
if (refs == null || refs.length == 0) {
throw new NullPointerException();
}
Runnable task = ((Runnable) context.getService(refs[0]));
return task;
}
}